Skip to content

Commit 34a4f79

Browse files
authored
Merge branch 'main' into fix/bound-diagnostic-config-read
2 parents 437f443 + 7e4a76a commit 34a4f79

2,042 files changed

Lines changed: 175931 additions & 63182 deletions

File tree

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

.agents/skills/openclaw-ci-limits/SKILL.md

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -128,7 +128,10 @@ These are intentionally guarded by `test/scripts/ci-workflow-guards.test.ts`:
128128
- `CI` concurrency key version, PR cancellation, and non-canceling canonical
129129
`main` single-flight with one coalesced pending tip.
130130
- `preflight` and hosted `security-fast` start immediately without a debounce
131-
or standalone admission job.
131+
or standalone admission job. On Node-relevant canonical main pushes,
132+
preflight also owns the sole dependency sticky-disk write and 8 GiB prune
133+
before fanout; replacement visibility is proved only by a later exact-marker
134+
restore because Blacksmith snapshot promotion can lag job completion.
132135
- CI matrix caps: fast/check lanes at 12, Node test shards at 28, Windows and
133136
Android at 2.
134137
- Canonical PR Node tests use one precise changed-target job when possible;
@@ -139,6 +142,9 @@ These are intentionally guarded by `test/scripts/ci-workflow-guards.test.ts`:
139142
- lower-weight Node/check shards on `blacksmith-4vcpu-ubuntu-2404`.
140143
- heavy retained Linux/Android shards on `blacksmith-8vcpu-ubuntu-2404`.
141144
- CodeQL Critical Quality on `ubuntu-24.04` with no `blacksmith-` labels.
145+
- Vitest/test compile caches are restore-only in CI and use immutable Actions
146+
caches; the daily/dispatch warmer is their sole writer. Build compile cache
147+
writes rotate at most once per UTC day. PRs create no runtime-cache archives.
142148

143149
When changing one knob, update `docs/ci.md` and the guard test in the same PR.
144150

.agents/skills/openclaw-live-updater/scripts/update-main.mjs

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -38,6 +38,7 @@ const DEFAULT_EXPECTED_ORIGIN = "openclaw/openclaw";
3838
const FULL_SHA_RE = /^[0-9a-f]{40}$/u;
3939
const GATEWAY_READINESS_ATTEMPTS = 3;
4040
const GATEWAY_READINESS_RETRY_DELAY_MS = 5_000;
41+
const GATEWAY_CLI_TIMEOUT_MS = 30_000;
4142
const GATEWAY_STOP_PROOF_ATTEMPTS = 100;
4243
const GATEWAY_STOP_PROOF_RETRY_DELAY_MS = 100;
4344
const GATEWAY_SUSPEND_TIMEOUT_MS = 10_000;
@@ -1013,7 +1014,7 @@ export function parseLaunchctlArguments(output) {
10131014
: [];
10141015
}
10151016

1016-
function runBuiltGatewayCli(checkout, args, deployment, options = {}) {
1017+
export function runBuiltGatewayCli(checkout, args, deployment, options = {}) {
10171018
const observedDeployment = deployment ?? readManagedGatewayLaunchAgent(checkout);
10181019
const sourceEntrypoint = path.join(checkout, "dist/index.js");
10191020
let managedDeployment = observedDeployment;
@@ -1105,7 +1106,9 @@ function runBuiltGatewayCli(checkout, args, deployment, options = {}) {
11051106
cwd: workingDirectory ?? path.dirname(path.dirname(entrypoint)),
11061107
encoding: "utf8",
11071108
env,
1109+
killSignal: "SIGKILL",
11081110
stdio: ["ignore", "pipe", options.stderr ?? "inherit"],
1111+
timeout: options.timeoutMs ?? GATEWAY_CLI_TIMEOUT_MS,
11091112
});
11101113
} finally {
11111114
rmSync(overlayPath, { force: true });

.github/actions/create-generated-pr-tokens/action.yml

Lines changed: 7 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -8,12 +8,15 @@ inputs:
88
contents-private-key:
99
description: GitHub App private key with contents write access.
1010
required: true
11-
pull-request-app-id:
12-
description: GitHub App id with pull-request write access.
11+
pull-request-client-id:
12+
description: GitHub App client id with pull-request write access.
1313
required: true
1414
pull-request-private-key:
1515
description: GitHub App private key with pull-request write access.
1616
required: true
17+
pull-request-contents-permission:
18+
description: Optional contents permission for pull-request operations that require repository write access.
19+
required: false
1720

1821
outputs:
1922
contents-token:
@@ -40,8 +43,9 @@ runs:
4043
id: pull-request-token
4144
uses: actions/create-github-app-token@bcd2ba49218906704ab6c1aa796996da409d3eb1 # v3
4245
with:
43-
app-id: ${{ inputs.pull-request-app-id }}
46+
client-id: ${{ inputs.pull-request-client-id }}
4447
private-key: ${{ inputs.pull-request-private-key }}
4548
owner: ${{ github.repository_owner }}
4649
repositories: ${{ github.event.repository.name }}
50+
permission-contents: ${{ inputs.pull-request-contents-permission }}
4751
permission-pull-requests: write

.github/actions/publish-generated-pr/action.yml

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -8,8 +8,8 @@ inputs:
88
contents-private-key:
99
description: GitHub App private key with contents write access.
1010
required: true
11-
pull-request-app-id:
12-
description: GitHub App id with pull-request write access.
11+
pull-request-client-id:
12+
description: GitHub App client id with pull-request write access.
1313
required: true
1414
pull-request-private-key:
1515
description: GitHub App private key with pull-request write access.
@@ -57,8 +57,9 @@ runs:
5757
with:
5858
contents-client-id: ${{ inputs.contents-client-id }}
5959
contents-private-key: ${{ inputs.contents-private-key }}
60-
pull-request-app-id: ${{ inputs.pull-request-app-id }}
60+
pull-request-client-id: ${{ inputs.pull-request-client-id }}
6161
pull-request-private-key: ${{ inputs.pull-request-private-key }}
62+
pull-request-contents-permission: ${{ inputs.auto-merge == 'true' && 'write' || '' }}
6263

6364
- name: Publish generated pull request
6465
shell: bash

.github/actions/setup-node-env/action.yml

Lines changed: 42 additions & 68 deletions
Original file line numberDiff line numberDiff line change
@@ -43,10 +43,6 @@ inputs:
4343
snapshot.
4444
required: false
4545
default: "false"
46-
runtime-cache-sticky-disk:
47-
description: Whether runtime caches should use Blacksmith sticky disks instead of actions/cache.
48-
required: false
49-
default: "false"
5046
vitest-fs-cache:
5147
description: Whether to persist Vitest's experimental filesystem module cache.
5248
required: false
@@ -64,10 +60,7 @@ inputs:
6460
required: false
6561
default: "false"
6662
save-vitest-fs-cache:
67-
description: >
68-
Whether this job may save the shared Vitest filesystem module cache.
69-
Only honored outside pull_request events for sticky disks; PR jobs
70-
always mount the protected snapshot read-only.
63+
description: Whether this job may save the shared Vitest filesystem module cache.
7164
required: false
7265
default: "false"
7366
build-all-cache-scope:
@@ -122,15 +115,15 @@ runs:
122115
123116
- name: Mount dependency sticky disk
124117
if: inputs.sticky-disk == 'true'
125-
uses: useblacksmith/stickydisk@5b350170ae4ef55b536b548ef5f5896e76a6b54f # v1.4.0
118+
uses: useblacksmith/stickydisk@6d373c96a74cbde0c99fedc5ea5d3a7ba66ba494 # main (post-v1.4.0 hot-attach fix)
126119
with:
127-
# One stable disk per Node line. v4 resets the v3 lineage after
128-
# Blacksmith repeatedly acknowledged commits but kept restoring its
129-
# original snapshot. The v2 per-PR/per-manifest-hash keys
120+
# One stable disk per Node line. v5 starts a fresh lineage for the
121+
# preflight-serialized writer after Blacksmith acknowledged repeated v4
122+
# commits but kept restoring its original snapshot. The v2 per-PR/per-manifest-hash keys
130123
# saturated Blacksmith's installation-wide sticky-disk budget. Install
131124
# inputs, runner platform, and the exact Node patch live in the runtime
132125
# marker below, so changes refresh this disk in place.
133-
key: ${{ github.repository }}-node-deps-bind-v4-${{ inputs.node-version }}
126+
key: ${{ github.repository }}-node-deps-bind-v5-${{ inputs.node-version }}
134127
path: /var/tmp/openclaw-node-deps
135128
# Single semantic writer: only the designated trusted-push job may
136129
# commit, so pull_request clones stay read-only. Like every sticky
@@ -139,58 +132,37 @@ runs:
139132
# and same-repo PR authors already hold repository write access.
140133
# Explicit true (not on-change) because the allocated-byte heuristic
141134
# can miss a fingerprint refresh whose reinstall keeps disk usage
142-
# stable, permanently stranding consumers on a stale marker. v1.4.0
143-
# skips commit after failed/cancelled steps, so a broken install
144-
# cannot seed this key.
135+
# stable, permanently stranding consumers on a stale marker. The action
136+
# skips commit after failed/cancelled steps, so a broken install cannot
137+
# seed this key.
145138
commit: ${{ inputs.save-sticky-disk == 'true' && github.event_name != 'pull_request' && 'true' || 'false' }}
146139

147-
- name: Mount Vitest transform cache sticky disk
148-
if: inputs.vitest-fs-cache == 'true' && (inputs.sticky-disk == 'true' || inputs.runtime-cache-sticky-disk == 'true') && runner.os != 'Windows'
149-
uses: useblacksmith/stickydisk@5b350170ae4ef55b536b548ef5f5896e76a6b54f # v1.4.0
150-
with:
151-
# One stable disk per runner shape. The old per-PR overlay disks
152-
# minted a new backing disk for every PR and helped saturate
153-
# Blacksmith's installation-wide sticky-disk budget, 429-failing every
154-
# mount. Cache entries are content-hash keyed (id+content+env), so PRs
155-
# can safely read the protected snapshot directly; PR-local misses are
156-
# rebuilt in the discarded mount. The generation guard below clears
157-
# incompatible transform inputs without leaking one disk per lockfile.
158-
key: ${{ github.repository }}-vitest-fs-v2-protected-${{ runner.os }}-${{ runner.arch }}-node-${{ inputs.node-version }}
159-
path: /var/tmp/openclaw-vitest-fs-cache
160-
# Single semantic writer: only the designated non-PR writer commits;
161-
# pull_request mounts stay read-only clones. Explicit true avoids
162-
# stickydisk's allocated-byte heuristic missing equal-size
163-
# replacements or prune-balanced writes.
164-
commit: ${{ inputs.save-vitest-fs-cache == 'true' && github.event_name != 'pull_request' && 'true' || 'false' }}
165-
166140
- name: Restore and save Vitest transform cache
167-
if: inputs.vitest-fs-cache == 'true' && inputs.sticky-disk != 'true' && inputs.runtime-cache-sticky-disk != 'true' && inputs.save-vitest-fs-cache == 'true' && runner.os != 'Windows'
141+
if: inputs.vitest-fs-cache == 'true' && inputs.save-vitest-fs-cache == 'true' && runner.os != 'Windows'
168142
uses: actions/cache@27d5ce7f107fe9357f9df03efb73ab90386fccae # v5
169143
with:
170144
path: /var/tmp/openclaw-vitest-fs-cache
171-
# actions/cache entries are immutable. One matrix job writes a new
172-
# run key; every job restores the newest cache through coarse prefixes.
173-
key: ${{ github.repository }}-vitest-fs-v2-${{ github.event_name == 'pull_request' && format('pr-{0}', github.event.pull_request.number) || 'protected' }}-${{ runner.os }}-${{ runner.arch }}-node-${{ inputs.node-version }}-${{ hashFiles('pnpm-lock.yaml', 'pnpm-workspace.yaml', '**/package.json', '**/tsconfig*.json', 'vitest.config.*', 'test/vitest/**') }}-${{ github.run_id }}-${{ github.run_attempt }}
145+
# Blacksmith transparently accelerates the upstream Actions cache API.
146+
# The scheduled/dispatch warmer writes one immutable protected archive;
147+
# all CI shards restore it into isolated runner-local directories.
148+
key: ${{ github.repository }}-vitest-fs-v3-protected-${{ runner.os }}-${{ runner.arch }}-node-${{ inputs.node-version }}-${{ hashFiles('pnpm-lock.yaml', 'pnpm-workspace.yaml', '**/package.json', '**/tsconfig*.json', 'vitest.config.*', 'test/vitest/**', '!**/node_modules/**') }}-${{ github.run_id }}-${{ github.run_attempt }}
174149
restore-keys: |
175-
${{ github.repository }}-vitest-fs-v2-${{ github.event_name == 'pull_request' && format('pr-{0}', github.event.pull_request.number) || 'protected' }}-${{ runner.os }}-${{ runner.arch }}-node-${{ inputs.node-version }}-${{ hashFiles('pnpm-lock.yaml', 'pnpm-workspace.yaml', '**/package.json', '**/tsconfig*.json', 'vitest.config.*', 'test/vitest/**') }}-
150+
${{ github.repository }}-vitest-fs-v3-protected-${{ runner.os }}-${{ runner.arch }}-node-${{ inputs.node-version }}-${{ hashFiles('pnpm-lock.yaml', 'pnpm-workspace.yaml', '**/package.json', '**/tsconfig*.json', 'vitest.config.*', 'test/vitest/**', '!**/node_modules/**') }}-
176151
177152
- name: Restore Vitest transform cache
178-
if: inputs.vitest-fs-cache == 'true' && inputs.sticky-disk != 'true' && inputs.runtime-cache-sticky-disk != 'true' && inputs.save-vitest-fs-cache != 'true' && runner.os != 'Windows'
153+
if: inputs.vitest-fs-cache == 'true' && inputs.save-vitest-fs-cache != 'true' && runner.os != 'Windows'
179154
uses: actions/cache/restore@27d5ce7f107fe9357f9df03efb73ab90386fccae # v5
180155
with:
181156
path: /var/tmp/openclaw-vitest-fs-cache
182-
key: ${{ github.repository }}-vitest-fs-v2-${{ github.event_name == 'pull_request' && format('pr-{0}', github.event.pull_request.number) || 'protected' }}-${{ runner.os }}-${{ runner.arch }}-node-${{ inputs.node-version }}-${{ hashFiles('pnpm-lock.yaml', 'pnpm-workspace.yaml', '**/package.json', '**/tsconfig*.json', 'vitest.config.*', 'test/vitest/**') }}-${{ github.run_id }}-${{ github.run_attempt }}
157+
key: ${{ github.repository }}-vitest-fs-v3-protected-${{ runner.os }}-${{ runner.arch }}-node-${{ inputs.node-version }}-${{ hashFiles('pnpm-lock.yaml', 'pnpm-workspace.yaml', '**/package.json', '**/tsconfig*.json', 'vitest.config.*', 'test/vitest/**', '!**/node_modules/**') }}-${{ github.run_id }}-${{ github.run_attempt }}
183158
restore-keys: |
184-
${{ github.repository }}-vitest-fs-v2-${{ github.event_name == 'pull_request' && format('pr-{0}', github.event.pull_request.number) || 'protected' }}-${{ runner.os }}-${{ runner.arch }}-node-${{ inputs.node-version }}-${{ hashFiles('pnpm-lock.yaml', 'pnpm-workspace.yaml', '**/package.json', '**/tsconfig*.json', 'vitest.config.*', 'test/vitest/**') }}-
159+
${{ github.repository }}-vitest-fs-v3-protected-${{ runner.os }}-${{ runner.arch }}-node-${{ inputs.node-version }}-${{ hashFiles('pnpm-lock.yaml', 'pnpm-workspace.yaml', '**/package.json', '**/tsconfig*.json', 'vitest.config.*', 'test/vitest/**', '!**/node_modules/**') }}-
185160
186161
- name: Configure Vitest transform cache
187162
if: inputs.vitest-fs-cache == 'true' && runner.os != 'Windows'
188163
env:
189-
CACHE_GENERATION: ${{ hashFiles('pnpm-lock.yaml', 'pnpm-workspace.yaml', '**/package.json', '**/tsconfig*.json', 'vitest.config.*', 'test/vitest/**') }}
190-
# Sticky PR mounts never commit, so pruning there would only burn
191-
# shard wall clock on a discarded clone; non-sticky (actions/cache)
192-
# writers still prune their PR-scoped archives.
193-
CACHE_WRITER: ${{ inputs.save-vitest-fs-cache == 'true' && ((inputs.sticky-disk != 'true' && inputs.runtime-cache-sticky-disk != 'true') || github.event_name != 'pull_request') && '1' || '0' }}
164+
CACHE_GENERATION: ${{ hashFiles('pnpm-lock.yaml', 'pnpm-workspace.yaml', '**/package.json', '**/tsconfig*.json', 'vitest.config.*', 'test/vitest/**', '!**/node_modules/**') }}
165+
CACHE_WRITER: ${{ inputs.save-vitest-fs-cache == 'true' && '1' || '0' }}
194166
shell: bash
195167
run: |
196168
set -euo pipefail
@@ -202,9 +174,8 @@ runs:
202174
cache_generation="$(<"$generation_file")"
203175
fi
204176
cache_entry="$(find "$cache_root" -mindepth 1 -maxdepth 1 -print -quit)"
205-
# Read-only PR mounts clear a mismatched generation too: the wipe only
206-
# affects the discarded local clone, and mixing generations could
207-
# false-hit transforms produced under incompatible tsconfig/lockfile.
177+
# Every restore is runner-local. Never mix incompatible transform
178+
# inputs even when an older archive was selected through a prefix.
208179
if [[ -n "$cache_entry" ]] && [[ "$cache_generation" != "$CACHE_GENERATION" ]]; then
209180
echo "Vitest transform inputs changed; clearing incompatible cache generation"
210181
find "$cache_root" -mindepth 1 -maxdepth 1 -exec rm -rf -- {} +
@@ -215,39 +186,42 @@ runs:
215186
echo "OPENCLAW_VITEST_FS_MODULE_CACHE_PATH=$cache_root" >> "$GITHUB_ENV"
216187
echo "OPENCLAW_VITEST_FS_MODULE_CACHE_WRITER=$CACHE_WRITER" >> "$GITHUB_ENV"
217188
218-
- name: Mount Node compile cache sticky disk
219-
if: inputs.node-compile-cache == 'true' && (inputs.sticky-disk == 'true' || inputs.runtime-cache-sticky-disk == 'true') && runner.os != 'Windows'
220-
uses: useblacksmith/stickydisk@5b350170ae4ef55b536b548ef5f5896e76a6b54f # v1.4.0
221-
with:
222-
# Trusted PRs read the protected dependency bytecode seed but cannot
223-
# publish feature-branch code into it. Node isolates exact versions and
224-
# invalidates bytecode when module contents change.
225-
key: ${{ github.repository }}-node-compile-v2-${{ inputs.node-compile-cache-scope }}-protected-${{ runner.os }}-${{ runner.arch }}-node-${{ inputs.node-version }}
226-
path: /var/tmp/openclaw-node-compile-cache
227-
commit: ${{ inputs.save-node-compile-cache == 'true' && github.event_name != 'pull_request' && 'true' || 'false' }}
189+
- name: Select Node compile cache epoch
190+
id: node-compile-cache-epoch
191+
if: inputs.node-compile-cache == 'true' && runner.os != 'Windows'
192+
shell: bash
193+
env:
194+
CACHE_SCOPE: ${{ inputs.node-compile-cache-scope }}
195+
run: |
196+
set -euo pipefail
197+
if [ "$CACHE_SCOPE" = "build" ]; then
198+
echo "value=$(date -u +%Y%m%d)" >> "$GITHUB_OUTPUT"
199+
else
200+
echo "value=${GITHUB_RUN_ID:?}-${GITHUB_RUN_ATTEMPT:?}" >> "$GITHUB_OUTPUT"
201+
fi
228202
229203
- name: Restore and save Node compile cache
230-
if: inputs.node-compile-cache == 'true' && inputs.sticky-disk != 'true' && inputs.runtime-cache-sticky-disk != 'true' && inputs.save-node-compile-cache == 'true' && runner.os != 'Windows'
204+
if: inputs.node-compile-cache == 'true' && inputs.save-node-compile-cache == 'true' && runner.os != 'Windows'
231205
uses: actions/cache@27d5ce7f107fe9357f9df03efb73ab90386fccae # v5
232206
with:
233207
path: /var/tmp/openclaw-node-compile-cache
234-
key: ${{ github.repository }}-node-compile-v2-${{ inputs.node-compile-cache-scope }}-${{ github.event_name == 'pull_request' && format('pr-{0}', github.event.pull_request.number) || 'protected' }}-${{ runner.os }}-${{ runner.arch }}-node-${{ inputs.node-version }}-${{ github.run_id }}-${{ github.run_attempt }}
208+
key: ${{ github.repository }}-node-compile-v3-${{ inputs.node-compile-cache-scope }}-protected-${{ runner.os }}-${{ runner.arch }}-node-${{ inputs.node-version }}-${{ steps.node-compile-cache-epoch.outputs.value }}
235209
restore-keys: |
236-
${{ github.repository }}-node-compile-v2-${{ inputs.node-compile-cache-scope }}-${{ github.event_name == 'pull_request' && format('pr-{0}', github.event.pull_request.number) || 'protected' }}-${{ runner.os }}-${{ runner.arch }}-node-${{ inputs.node-version }}-
210+
${{ github.repository }}-node-compile-v3-${{ inputs.node-compile-cache-scope }}-protected-${{ runner.os }}-${{ runner.arch }}-node-${{ inputs.node-version }}-
237211
238212
- name: Restore Node compile cache
239-
if: inputs.node-compile-cache == 'true' && inputs.sticky-disk != 'true' && inputs.runtime-cache-sticky-disk != 'true' && inputs.save-node-compile-cache != 'true' && runner.os != 'Windows'
213+
if: inputs.node-compile-cache == 'true' && inputs.save-node-compile-cache != 'true' && runner.os != 'Windows'
240214
uses: actions/cache/restore@27d5ce7f107fe9357f9df03efb73ab90386fccae # v5
241215
with:
242216
path: /var/tmp/openclaw-node-compile-cache
243-
key: ${{ github.repository }}-node-compile-v2-${{ inputs.node-compile-cache-scope }}-${{ github.event_name == 'pull_request' && format('pr-{0}', github.event.pull_request.number) || 'protected' }}-${{ runner.os }}-${{ runner.arch }}-node-${{ inputs.node-version }}-${{ github.run_id }}-${{ github.run_attempt }}
217+
key: ${{ github.repository }}-node-compile-v3-${{ inputs.node-compile-cache-scope }}-protected-${{ runner.os }}-${{ runner.arch }}-node-${{ inputs.node-version }}-${{ steps.node-compile-cache-epoch.outputs.value }}
244218
restore-keys: |
245-
${{ github.repository }}-node-compile-v2-${{ inputs.node-compile-cache-scope }}-${{ github.event_name == 'pull_request' && format('pr-{0}', github.event.pull_request.number) || 'protected' }}-${{ runner.os }}-${{ runner.arch }}-node-${{ inputs.node-version }}-
219+
${{ github.repository }}-node-compile-v3-${{ inputs.node-compile-cache-scope }}-protected-${{ runner.os }}-${{ runner.arch }}-node-${{ inputs.node-version }}-
246220
247221
- name: Configure Node compile cache
248222
if: inputs.node-compile-cache == 'true' && runner.os != 'Windows'
249223
env:
250-
CACHE_WRITER: ${{ inputs.save-node-compile-cache == 'true' && ((inputs.sticky-disk != 'true' && inputs.runtime-cache-sticky-disk != 'true') || github.event_name != 'pull_request') && '1' || '0' }}
224+
CACHE_WRITER: ${{ inputs.save-node-compile-cache == 'true' && '1' || '0' }}
251225
shell: bash
252226
run: |
253227
set -euo pipefail

0 commit comments

Comments
 (0)