diff --git a/.github/actions/setup-node-env/action.yml b/.github/actions/setup-node-env/action.yml index e9a00edbe8d9..16ef00595ed5 100644 --- a/.github/actions/setup-node-env/action.yml +++ b/.github/actions/setup-node-env/action.yml @@ -23,24 +23,16 @@ inputs: description: Whether to use --frozen-lockfile for install. required: false default: "true" - use-actions-cache: - description: Whether to restore the pnpm store with actions/cache. + cache-mode: + description: Cache authority for this setup action (off, restore, or read-write). required: false - default: "true" - save-actions-cache: - description: Whether to save the pnpm store with actions/cache after install when no exact cache restored. - required: false - default: "false" + default: "off" dependency-cache: description: Whether to restore workspace node_modules and its local pnpm store from the exact semantic dependency cache. required: false default: "false" - save-dependency-cache: - description: Whether to save workspace node_modules and its local pnpm store after a successful install on an exact cache miss. - required: false - default: "false" vitest-fs-cache: - description: Whether to persist Vitest's experimental filesystem module cache. + description: Whether to restore Vitest's experimental filesystem module cache. required: false default: "false" restore-test-caches: @@ -48,30 +40,75 @@ inputs: required: false default: "false" node-compile-cache: - description: Whether to persist Node's on-disk V8 compile cache. + description: Whether to restore Node's on-disk V8 compile cache. required: false default: "false" node-compile-cache-scope: description: Cache namespace for isolating workloads with different writer ownership. required: false default: "test" - save-node-compile-cache: - description: Whether this job may save the Node compile cache. - required: false - default: "false" - save-vitest-fs-cache: - description: Whether this job may save the shared Vitest filesystem module cache. - required: false - default: "false" build-all-cache-scope: description: > - Namespace for restoring and saving build-all's content-addressed step cache. + Namespace for restoring build-all's content-addressed step cache. Leave empty to disable; use only for declaration builds with public inputs. required: false default: "" +outputs: + cache-mode: + description: Validated cache authority selected by the caller. + value: ${{ inputs.cache-mode }} + node-toolchain-cache-key: + description: Exact key for saving a newly downloaded Node toolchain. + value: openclaw-node-toolchain-v1-${{ runner.os }}-${{ runner.arch }}-${{ inputs.node-version }}-${{ steps.setup-node.outputs.resolved-version }} + node-toolchain-cache-matched-key: + description: Node toolchain cache key restored by this run. + value: ${{ steps.node-toolchain-restore.outputs.cache-matched-key }} + node-toolchain-cache-path: + description: Runner-local Node toolchain cache path. + value: ${{ runner.temp }}/openclaw-node-toolchain/node + node-toolchain-populated: + description: Whether setup downloaded a Node toolchain into the cache path. + value: ${{ steps.setup-node.outputs.toolchain-populated }} + dependency-cache-hit: + description: Whether the exact semantic dependency cache restored. + value: ${{ steps.dependency-cache.outputs.cache-hit }} + dependency-cache-key: + description: Exact semantic dependency cache key. + value: ${{ steps.dependency-cache-key.outputs.key }} + pnpm-store-cache-hit: + description: Whether the pnpm store restored an exact key. + value: ${{ steps.setup-pnpm.outputs.store-cache-hit }} + pnpm-store-cache-key: + description: Exact pnpm store key used for restore or save. + value: ${{ steps.setup-pnpm.outputs.store-cache-primary-key }} + pnpm-store-cache-path: + description: Resolved pnpm store path. + value: ${{ steps.setup-pnpm.outputs.store-path }} + vitest-cache-key: + description: Exact Vitest transform cache key. + value: ${{ steps.vitest-cache.outputs.cache-primary-key }} + node-compile-cache-key: + description: Exact Node compile cache key. + value: ${{ steps.node-compile-cache.outputs.cache-primary-key }} + build-all-cache-key: + description: Exact build-all cache key. + value: ${{ steps.build-all-cache.outputs.cache-primary-key }} runs: using: composite steps: + - name: Validate cache mode + shell: bash + env: + CACHE_MODE: ${{ inputs.cache-mode }} + run: | + case "$CACHE_MODE" in + off|restore|read-write) ;; + *) + echo "::error::Invalid cache-mode input: '$CACHE_MODE' (expected off, restore, or read-write)" + exit 2 + ;; + esac + - name: Normalize container toolcache shell: bash run: | @@ -91,16 +128,15 @@ runs: # entry is self-healing: ensure-node probes each candidate's version and # falls back to the download when none satisfies the floor. # Restore by prefix, never by exact key: cache entries are immutable and an - # exact hit suppresses the post-job save, so a floating `24.x` key would pin - # the first Node it ever saw. Once the floor advances past it every job would - # restore the rejected payload and re-download forever. The save below is - # keyed on the version actually installed, so a newer resolve publishes a new - # entry and later prefix restores pick it up. + # exact key would pin the first Node it ever saw. Once the floor advances + # past it every job would restore the rejected payload and re-download + # forever. The trusted cache warmer publishes the resolved version as a new + # exact key, so later prefix restores pick it up. # GitHub-hosted images carry a Node that already clears the floor, so they - # resolve from /opt/hostedtoolcache and would only ever miss here, then warn - # on a save whose path was never created. Scope both steps to self-hosted. + # resolve from /opt/hostedtoolcache and would only ever miss here. Scope the + # restore to self-hosted runners. - name: Restore Node toolchain cache - if: runner.os != 'Windows' && runner.environment != 'github-hosted' + if: inputs.cache-mode != 'off' && runner.os != 'Windows' && runner.environment != 'github-hosted' id: node-toolchain-restore continue-on-error: true uses: actions/cache/restore@27d5ce7f107fe9357f9df03efb73ab90386fccae # v5 @@ -115,7 +151,7 @@ runs: shell: bash env: REQUESTED_NODE_VERSION: ${{ inputs.node-version }} - OPENCLAW_NODE_TOOLCHAIN_ROOT: ${{ runner.os != 'Windows' && format('{0}/openclaw-node-toolchain/node', runner.temp) || '' }} + OPENCLAW_NODE_TOOLCHAIN_ROOT: ${{ inputs.cache-mode != 'off' && runner.os != 'Windows' && format('{0}/openclaw-node-toolchain/node', runner.temp) || '' }} run: | set -euo pipefail source "$GITHUB_ACTION_PATH/../setup-pnpm-store-cache/ensure-node.sh" @@ -128,19 +164,8 @@ runs: echo "toolchain-populated=true" >> "$GITHUB_OUTPUT" fi - # Skipped when the restore already matched this exact resolved version, so a - # warm run uploads nothing. On a version change the 46-way fanout races here; - # the losers log a benign "cache already exists" and continue. - - name: Save Node toolchain cache - if: ${{ runner.os != 'Windows' && runner.environment != 'github-hosted' && steps.setup-node.outputs.toolchain-populated == 'true' && steps.node-toolchain-restore.outputs.cache-matched-key != format('openclaw-node-toolchain-v1-{0}-{1}-{2}-{3}', runner.os, runner.arch, inputs.node-version, steps.setup-node.outputs.resolved-version) }} - continue-on-error: true - uses: actions/cache/save@27d5ce7f107fe9357f9df03efb73ab90386fccae # v5 - with: - path: ${{ runner.temp }}/openclaw-node-toolchain/node - key: openclaw-node-toolchain-v1-${{ runner.os }}-${{ runner.arch }}-${{ inputs.node-version }}-${{ steps.setup-node.outputs.resolved-version }} - - name: Configure dependency cache store - if: inputs.dependency-cache == 'true' + if: inputs.cache-mode != 'off' && inputs.dependency-cache == 'true' shell: bash run: | set -euo pipefail @@ -150,7 +175,7 @@ runs: - name: Resolve dependency cache key id: dependency-cache-key - if: inputs.dependency-cache == 'true' + if: inputs.cache-mode != 'off' && inputs.dependency-cache == 'true' shell: bash env: FROZEN_LOCKFILE: ${{ inputs.frozen-lockfile }} @@ -162,7 +187,7 @@ runs: echo "key=$cache_key" >> "$GITHUB_OUTPUT" - name: Prepare dependency cache restore - if: inputs.dependency-cache == 'true' + if: inputs.cache-mode != 'off' && inputs.dependency-cache == 'true' shell: bash run: | rm -rf "$GITHUB_WORKSPACE/node_modules" "$GITHUB_WORKSPACE/.cache/openclaw-pnpm-store" @@ -176,7 +201,7 @@ runs: - name: Restore exact dependency cache id: dependency-cache - if: inputs.dependency-cache == 'true' + if: inputs.cache-mode != 'off' && inputs.dependency-cache == 'true' continue-on-error: true uses: actions/cache/restore@27d5ce7f107fe9357f9df03efb73ab90386fccae # v5 with: @@ -189,7 +214,7 @@ runs: key: ${{ steps.dependency-cache-key.outputs.key }} - name: Prepare dependency cache miss fallback - if: inputs.dependency-cache == 'true' && steps.dependency-cache.outputs.cache-hit != 'true' + if: inputs.cache-mode != 'off' && inputs.dependency-cache == 'true' && steps.dependency-cache.outputs.cache-hit != 'true' shell: bash run: | # actions/cache treats service, download, and extraction failures as @@ -211,27 +236,16 @@ runs: # On an exact dependency-cache hit, the same archive already restored # the complete store. Every miss can seed it from the coarser cache, # including legacy Blacksmith callers that disabled that old fallback. - use-actions-cache: ${{ ((inputs.dependency-cache == 'true' && steps.dependency-cache.outputs.cache-hit != 'true') || (inputs.dependency-cache != 'true' && inputs.use-actions-cache == 'true')) && 'true' || 'false' }} + cache-mode: ${{ inputs.cache-mode != 'off' && (inputs.dependency-cache != 'true' || steps.dependency-cache.outputs.cache-hit != 'true') && 'restore' || 'off' }} - name: Setup TruffleHog if: inputs.install-trufflehog == 'true' shell: bash run: bash scripts/install-trufflehog.sh - - name: Restore and save Vitest transform cache - if: inputs.vitest-fs-cache == 'true' && inputs.save-vitest-fs-cache == 'true' && runner.os != 'Windows' - uses: actions/cache@27d5ce7f107fe9357f9df03efb73ab90386fccae # v5 - with: - path: /var/tmp/openclaw-vitest-fs-cache - # Blacksmith transparently accelerates the upstream Actions cache API. - # The scheduled/dispatch warmer writes one immutable protected archive; - # all CI shards restore it into isolated runner-local directories. - 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/**', 'src/state/*.sql', '!**/node_modules/**') }}-${{ github.run_id }}-${{ github.run_attempt }} - restore-keys: | - ${{ 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/**', 'src/state/*.sql', '!**/node_modules/**') }}- - - name: Restore Vitest transform cache - if: (inputs.vitest-fs-cache == 'true' || inputs.restore-test-caches == 'true') && inputs.save-vitest-fs-cache != 'true' && runner.os != 'Windows' + id: vitest-cache + if: inputs.cache-mode != 'off' && (inputs.vitest-fs-cache == 'true' || inputs.restore-test-caches == 'true') && runner.os != 'Windows' uses: actions/cache/restore@27d5ce7f107fe9357f9df03efb73ab90386fccae # v5 with: path: /var/tmp/openclaw-vitest-fs-cache @@ -240,10 +254,10 @@ runs: ${{ 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/**', 'src/state/*.sql', '!**/node_modules/**') }}- - name: Configure Vitest transform cache - if: (inputs.vitest-fs-cache == 'true' || inputs.restore-test-caches == 'true') && runner.os != 'Windows' + if: inputs.cache-mode != 'off' && (inputs.vitest-fs-cache == 'true' || inputs.restore-test-caches == 'true') && runner.os != 'Windows' env: CACHE_GENERATION: ${{ hashFiles('pnpm-lock.yaml', 'pnpm-workspace.yaml', '**/package.json', '**/tsconfig*.json', 'vitest.config.*', 'test/vitest/**', 'src/state/*.sql', '!**/node_modules/**') }} - CACHE_WRITER: ${{ inputs.save-vitest-fs-cache == 'true' && '1' || '0' }} + CACHE_WRITER: "0" shell: bash run: | set -euo pipefail @@ -269,7 +283,7 @@ runs: - name: Select Node compile cache epoch id: node-compile-cache-epoch - if: (inputs.node-compile-cache == 'true' || inputs.restore-test-caches == 'true') && runner.os != 'Windows' + if: inputs.cache-mode != 'off' && (inputs.node-compile-cache == 'true' || inputs.restore-test-caches == 'true') && runner.os != 'Windows' shell: bash env: CACHE_SCOPE: ${{ inputs.node-compile-cache-scope }} @@ -281,17 +295,9 @@ runs: echo "value=${GITHUB_RUN_ID:?}-${GITHUB_RUN_ATTEMPT:?}" >> "$GITHUB_OUTPUT" fi - - name: Restore and save Node compile cache - if: inputs.node-compile-cache == 'true' && inputs.save-node-compile-cache == 'true' && runner.os != 'Windows' - uses: actions/cache@27d5ce7f107fe9357f9df03efb73ab90386fccae # v5 - with: - path: /var/tmp/openclaw-node-compile-cache - 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 }} - restore-keys: | - ${{ github.repository }}-node-compile-v3-${{ inputs.node-compile-cache-scope }}-protected-${{ runner.os }}-${{ runner.arch }}-node-${{ inputs.node-version }}- - - name: Restore Node compile cache - if: (inputs.node-compile-cache == 'true' || inputs.restore-test-caches == 'true') && inputs.save-node-compile-cache != 'true' && runner.os != 'Windows' + id: node-compile-cache + if: inputs.cache-mode != 'off' && (inputs.node-compile-cache == 'true' || inputs.restore-test-caches == 'true') && runner.os != 'Windows' uses: actions/cache/restore@27d5ce7f107fe9357f9df03efb73ab90386fccae # v5 with: path: /var/tmp/openclaw-node-compile-cache @@ -300,9 +306,7 @@ runs: ${{ github.repository }}-node-compile-v3-${{ inputs.node-compile-cache-scope }}-protected-${{ runner.os }}-${{ runner.arch }}-node-${{ inputs.node-version }}- - name: Configure Node compile cache - if: (inputs.node-compile-cache == 'true' || inputs.restore-test-caches == 'true') && runner.os != 'Windows' - env: - CACHE_WRITER: ${{ inputs.save-node-compile-cache == 'true' && '1' || '0' }} + if: inputs.cache-mode != 'off' && (inputs.node-compile-cache == 'true' || inputs.restore-test-caches == 'true') && runner.os != 'Windows' shell: bash run: | set -euo pipefail @@ -310,7 +314,7 @@ runs: mkdir -p "$cache_root" echo "NODE_COMPILE_CACHE=$cache_root" >> "$GITHUB_ENV" echo "NODE_COMPILE_CACHE_PORTABLE=1" >> "$GITHUB_ENV" - echo "OPENCLAW_NODE_COMPILE_CACHE_WRITER=$CACHE_WRITER" >> "$GITHUB_ENV" + echo "OPENCLAW_NODE_COMPILE_CACHE_WRITER=0" >> "$GITHUB_ENV" - name: Setup Bun if: inputs.install-bun == 'true' @@ -343,7 +347,7 @@ runs: shell: bash env: CI: "true" - DEPENDENCY_CACHE: ${{ inputs.dependency-cache }} + DEPENDENCY_CACHE: ${{ inputs.cache-mode != 'off' && inputs.dependency-cache == 'true' && 'true' || 'false' }} DEPENDENCY_CACHE_HIT: ${{ steps.dependency-cache.outputs.cache-hit }} FROZEN_LOCKFILE: ${{ inputs.frozen-lockfile }} run: | @@ -450,22 +454,10 @@ runs: echo "pnpm_config_verify_deps_before_run=false" >> "$GITHUB_ENV" fi - - name: Save exact dependency cache - if: inputs.install-deps == 'true' && inputs.dependency-cache == 'true' && inputs.save-dependency-cache == 'true' && steps.dependency-cache.outputs.cache-hit != 'true' && steps.dependency-cache.outcome != 'failure' - continue-on-error: true - uses: actions/cache/save@27d5ce7f107fe9357f9df03efb73ab90386fccae # v5 - with: - path: | - node_modules - ui/node_modules - packages/*/node_modules - examples/*/node_modules - .cache/openclaw-pnpm-store - key: ${{ steps.dependency-cache-key.outputs.key }} - - - name: Restore and save build-all cache - if: inputs.build-all-cache-scope != '' - uses: actions/cache@27d5ce7f107fe9357f9df03efb73ab90386fccae # v5 + - name: Restore build-all cache + id: build-all-cache + if: inputs.cache-mode != 'off' && inputs.build-all-cache-scope != '' + uses: actions/cache/restore@27d5ce7f107fe9357f9df03efb73ab90386fccae # v5 with: path: .artifacts/build-all-cache # Exact keys deduplicate concurrent jobs. Coarse restore supplies the @@ -473,23 +465,3 @@ runs: key: ${{ github.repository }}-build-all-v1-${{ inputs.build-all-cache-scope }}-${{ runner.os }}-${{ runner.arch }}-node-${{ inputs.node-version }}-${{ hashFiles('package.json', 'pnpm-lock.yaml', 'tsconfig*.json', 'tsdown*.config.ts', 'scripts/build-all.mts', 'scripts/tsdown-build.mts', 'scripts/lib/tsdown-*.mts', 'scripts/lib/plugin-sdk-*', 'scripts/lib/bundled-plugin-*', 'scripts/lib/optional-bundled-clusters.mjs', 'src/**', 'packages/**', 'extensions/**') }} restore-keys: | ${{ github.repository }}-build-all-v1-${{ inputs.build-all-cache-scope }}-${{ runner.os }}-${{ runner.arch }}-node-${{ inputs.node-version }}- - - # Prune before saving: prefix-key restores accrete every prior lockfile - # generation into the archive (measured 2.05 GiB, ~36s restore per job). - # Pruning collapses it to the current lockfile's closure; a dropped entry - # costs one registry refetch in a later job at worst. - - name: Prune pnpm store before save - if: ${{ inputs.install-deps == 'true' && inputs.use-actions-cache == 'true' && (inputs.dependency-cache != 'true' || steps.dependency-cache.outputs.cache-hit != 'true') && inputs.save-actions-cache == 'true' && runner.os != 'Windows' && steps.setup-pnpm.outputs.store-cache-hit != 'true' }} - shell: bash - working-directory: ${{ steps.package-manager.outputs.project-dir }} - run: | - du -sh "${{ steps.setup-pnpm.outputs.store-path }}" || true - pnpm store prune - du -sh "${{ steps.setup-pnpm.outputs.store-path }}" || true - - - name: Save pnpm store cache - if: ${{ inputs.install-deps == 'true' && inputs.use-actions-cache == 'true' && (inputs.dependency-cache != 'true' || steps.dependency-cache.outputs.cache-hit != 'true') && inputs.save-actions-cache == 'true' && runner.os != 'Windows' && steps.setup-pnpm.outputs.store-cache-hit != 'true' }} - uses: actions/cache/save@27d5ce7f107fe9357f9df03efb73ab90386fccae # v5 - with: - path: ${{ steps.setup-pnpm.outputs.store-path }} - key: ${{ steps.setup-pnpm.outputs.store-cache-primary-key }} diff --git a/.github/actions/setup-pnpm-store-cache/action.yml b/.github/actions/setup-pnpm-store-cache/action.yml index b39a891ce8f4..1afce7632b41 100644 --- a/.github/actions/setup-pnpm-store-cache/action.yml +++ b/.github/actions/setup-pnpm-store-cache/action.yml @@ -13,10 +13,10 @@ inputs: description: Expected Node.js version already installed by actions/setup-node. required: false default: "" - use-actions-cache: - description: Whether actions/cache should restore the pnpm store. + cache-mode: + description: Cache authority for this setup action (off, restore, or read-write). required: false - default: "true" + default: "off" outputs: pnpm-version: description: Resolved pnpm version activated by the setup action. @@ -40,10 +40,18 @@ runs: id: setup-pnpm shell: bash env: + CACHE_MODE: ${{ inputs.cache-mode }} PACKAGE_MANAGER_FILE: ${{ inputs.package-manager-file }} REQUESTED_NODE_VERSION: ${{ inputs.node-version }} run: | set -euo pipefail + case "$CACHE_MODE" in + off|restore|read-write) ;; + *) + echo "::error::Invalid cache-mode input: '$CACHE_MODE' (expected off, restore, or read-write)" + exit 2 + ;; + esac project_dir="$(dirname "$PACKAGE_MANAGER_FILE")" if [[ ! -f "$PACKAGE_MANAGER_FILE" ]]; then echo "::error::package manager file not found: $PACKAGE_MANAGER_FILE" @@ -88,7 +96,7 @@ runs: - name: Resolve pnpm store path id: pnpm-store - if: ${{ inputs.use-actions-cache == 'true' && runner.os != 'Windows' }} + if: ${{ inputs.cache-mode != 'off' && runner.os != 'Windows' }} shell: bash run: | set -euo pipefail @@ -98,7 +106,7 @@ runs: - name: Restore pnpm store cache id: pnpm-store-cache - if: ${{ inputs.use-actions-cache == 'true' && runner.os != 'Windows' }} + if: ${{ inputs.cache-mode != 'off' && runner.os != 'Windows' }} uses: actions/cache/restore@27d5ce7f107fe9357f9df03efb73ab90386fccae # v5 with: path: ${{ steps.pnpm-store.outputs.path }} diff --git a/.github/actions/setup-release-harness/action.yml b/.github/actions/setup-release-harness/action.yml index 874b7d60d9c0..db5ccbf16781 100644 --- a/.github/actions/setup-release-harness/action.yml +++ b/.github/actions/setup-release-harness/action.yml @@ -10,9 +10,9 @@ runs: - name: Setup trusted release harness package manager uses: ./.release-harness/.github/actions/setup-pnpm-store-cache with: + cache-mode: off node-version: ${{ inputs.node-version }} package-manager-file: .release-harness/package.json - use-actions-cache: "false" - name: Install trusted release harness dependencies shell: bash diff --git a/.github/workflows/android-release.yml b/.github/workflows/android-release.yml index 1feb76bc7ab1..43712cdfa19f 100644 --- a/.github/workflows/android-release.yml +++ b/.github/workflows/android-release.yml @@ -165,9 +165,9 @@ jobs: - name: Setup Node environment uses: ./.github/actions/setup-node-env with: + cache-mode: off install-bun: "true" install-deps: "false" - use-actions-cache: "false" - name: Setup Android toolchain uses: ./.github/actions/setup-android-toolchain diff --git a/.github/workflows/ci-build-artifacts-testbox.yml b/.github/workflows/ci-build-artifacts-testbox.yml index afb50b7ead0d..0310b4abbc42 100644 --- a/.github/workflows/ci-build-artifacts-testbox.yml +++ b/.github/workflows/ci-build-artifacts-testbox.yml @@ -48,8 +48,10 @@ jobs: persist-credentials: false - name: Setup Node environment + id: setup-node-env uses: ./.github/actions/setup-node-env with: + cache-mode: ${{ github.event_name == 'workflow_dispatch' && 'read-write' || 'restore' }} install-bun: "false" install-trufflehog: "true" @@ -143,7 +145,7 @@ jobs: test -f dist/control-ui/index.html - name: Save dist build cache - if: steps.dist-cache.outputs.cache-hit != 'true' + if: steps.setup-node-env.outputs.cache-mode == 'read-write' && steps.dist-cache.outputs.cache-hit != 'true' uses: actions/cache/save@27d5ce7f107fe9357f9df03efb73ab90386fccae # v5.0.5 with: path: | diff --git a/.github/workflows/ci-check-arm-testbox.yml b/.github/workflows/ci-check-arm-testbox.yml index b1d993e68c3b..775f57f2763a 100644 --- a/.github/workflows/ci-check-arm-testbox.yml +++ b/.github/workflows/ci-check-arm-testbox.yml @@ -59,6 +59,7 @@ jobs: - name: Setup Node environment uses: ./.github/actions/setup-node-env with: + cache-mode: restore install-bun: "false" install-trufflehog: "true" - name: Ensure Testbox base commit diff --git a/.github/workflows/ci-check-testbox.yml b/.github/workflows/ci-check-testbox.yml index baf97d1cf03f..3f58bb0074d0 100644 --- a/.github/workflows/ci-check-testbox.yml +++ b/.github/workflows/ci-check-testbox.yml @@ -52,11 +52,11 @@ jobs: - name: Setup Node environment uses: ./.github/actions/setup-node-env with: + cache-mode: restore install-bun: "false" install-trufflehog: "true" # Testbox hydration uses the ordinary pnpm store cache. Canonical CI # owns the exact dependency archive and never delegates here. - use-actions-cache: "true" - name: Ensure Testbox base commit if: github.event_name == 'pull_request' uses: ./.github/actions/ensure-base-commit diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 2314f5863172..4f56e51439b2 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -435,6 +435,7 @@ jobs: if: github.event_name == 'workflow_dispatch' uses: ./.github/actions/setup-pnpm-store-cache with: + cache-mode: restore node-version: ${{ env.NODE_VERSION }} - name: Install manifest dependencies @@ -772,15 +773,7 @@ jobs: ...changedExtensionFallbackShards, ] : []; - const assignVitestFsCacheWriter = - typeof nodeTestPlan.assignVitestFsCacheWriter === "function" - ? nodeTestPlan.assignVitestFsCacheWriter - : (shards) => - shards.map((shard, index) => ({ - ...shard, - saveVitestFsCache: index === 0, - })); - const nodeTestShards = assignVitestFsCacheWriter(rawNodeTestShards).map((shard) => ({ + const nodeTestShards = rawNodeTestShards.map((shard) => ({ check_name: shard.checkName, runtime: "node", task: "test-shard", @@ -794,7 +787,6 @@ jobs: timeout_minutes: shard.timeoutMinutes, plan_concurrency: shard.planConcurrency, predicted_seconds: shard.predictedSeconds, - save_vitest_fs_cache: shard.saveVitestFsCache, targets: shard.targets, requires_go: shard.shardName.startsWith("core-tooling") || @@ -947,19 +939,15 @@ jobs: node scripts/check-protocol-event-coverage.mts fi - # Publish one immutable semantic dependency archive before same-repo - # Blacksmith jobs fan out. The GitHub backend uses the pnpm-store cache. - # Pull-request archives remain merge-ref scoped; - # main archives seed later pull requests through the default-branch scope. - - name: Publish exact dependency cache + # Validate and consume the immutable dependency archive before same-repo + # Blacksmith jobs fan out. Cache publication belongs to the trusted warmer. + - name: Restore exact dependency cache if: vars.OPENCLAW_CI_RUNNER_BACKEND != 'github' && vars.OPENCLAW_CI_RUNNER_BACKEND != 'hybrid' && github.repository == 'openclaw/openclaw' && steps.manifest.outputs.run_node == 'true' && ((github.event_name == 'push' && github.ref == 'refs/heads/main') || (github.event_name == 'pull_request' && github.event.pull_request.head.repo.full_name == github.repository)) uses: ./.github/actions/setup-node-env with: + cache-mode: restore dependency-cache: "true" install-bun: "false" - save-actions-cache: "true" - save-dependency-cache: "true" - use-actions-cache: "true" # Run dependency-free security checks on a hosted runner in parallel with # scope detection. No downstream job waits for Python/pre-commit setup. @@ -1165,9 +1153,9 @@ jobs: - name: Audit production dependencies run: node scripts/pre-commit/pnpm-audit-prod.mjs --audit-level=high - # Warm the lockfile- and pnpm-pinned Actions cache for fork PRs, manual runs, - # the GitHub backend, and docs-only same-repo PRs. Node-relevant Blacksmith - # runs already publish it through the exact dependency-cache writer in preflight. + # Prime the lockfile- and pnpm-pinned store for fork PRs, manual runs, the + # GitHub backend, and docs-only same-repo PRs. This job restores only; the + # trusted cache warmer owns publication. pnpm-store-warmup: permissions: contents: read @@ -1229,8 +1217,8 @@ jobs: - name: Setup Node environment uses: ./.github/actions/setup-node-env with: + cache-mode: restore install-bun: "false" - save-actions-cache: "true" # Build dist once for Node-relevant changes and share it with downstream jobs. # Keep this overlapping with the fast correctness lanes so green PRs get heavy @@ -1257,17 +1245,16 @@ jobs: - name: Setup Node environment uses: ./.github/actions/setup-node-env with: + cache-mode: restore install-bun: "false" node-compile-cache: "true" node-compile-cache-scope: "build" # Same-repo Blacksmith runs restore the dependency cache published by # preflight; hosted paths use the pnpm store cache instead. dependency-cache: ${{ vars.OPENCLAW_CI_RUNNER_BACKEND != 'github' && vars.OPENCLAW_CI_RUNNER_BACKEND != 'hybrid' && github.event_name != 'workflow_dispatch' && github.repository == 'openclaw/openclaw' && (github.event_name != 'pull_request' || github.event.pull_request.head.repo.full_name == 'openclaw/openclaw') && 'true' || 'false' }} - use-actions-cache: ${{ vars.OPENCLAW_CI_RUNNER_BACKEND != 'github' && vars.OPENCLAW_CI_RUNNER_BACKEND != 'hybrid' && github.event_name != 'workflow_dispatch' && github.repository == 'openclaw/openclaw' && (github.event_name != 'pull_request' || github.event.pull_request.head.repo.full_name == 'openclaw/openclaw') && 'false' || 'true' }} - save-node-compile-cache: ${{ github.event_name == 'push' && github.ref == 'refs/heads/main' && 'true' || 'false' }} - name: Restore build-all step cache - uses: actions/cache@27d5ce7f107fe9357f9df03efb73ab90386fccae # v5.0.5 + uses: actions/cache/restore@27d5ce7f107fe9357f9df03efb73ab90386fccae # v5.0.5 with: path: .artifacts/build-all-cache key: ${{ runner.os }}-build-all-v4-${{ hashFiles('package.json', 'pnpm-lock.yaml', 'scripts/build-all.mts', 'scripts/runtime-postbuild.mjs', 'scripts/runtime-postbuild.mts', 'scripts/lib/tsx-cli-shim.mjs', 'scripts/write-plugin-sdk-entry-dts.ts', 'scripts/lib/plugin-sdk-entries.mts', 'scripts/lib/plugin-sdk-entrypoints.json', 'scripts/lib/plugin-sdk-private-local-only-subpaths.json', 'scripts/lib/plugin-sdk-deprecated-public-subpaths.json', 'scripts/lib/plugin-sdk-deprecated-barrel-subpaths.json', 'tsconfig.json', 'tsconfig.plugin-sdk.dts.json', 'src/**', 'packages/**', '!src/**/dist/**', '!src/**/node_modules/**', '!packages/**/dist/**', '!packages/**/node_modules/**') }} @@ -1510,19 +1497,6 @@ jobs: if-no-files-found: ignore retention-days: 7 - - name: Save dist build cache - if: steps.dist_build_cache.outputs.cache-hit != 'true' - uses: actions/cache/save@27d5ce7f107fe9357f9df03efb73ab90386fccae # v5.0.5 - continue-on-error: true - with: - path: | - dist/ - dist-runtime/ - packages/*/dist/ - extensions/*/src/host/**/.bundle.hash - extensions/*/src/host/**/*.bundle.js - key: ${{ steps.dist_build_cache.outputs.cache-primary-key }} - - name: Upload gateway watch regression artifacts if: always() && needs.preflight.outputs.run_check_additional == 'true' uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7 @@ -1544,9 +1518,9 @@ jobs: - name: Setup Node environment uses: ./.github/actions/setup-node-env with: + cache-mode: restore install-bun: "false" dependency-cache: ${{ vars.OPENCLAW_CI_RUNNER_BACKEND != 'github' && vars.OPENCLAW_CI_RUNNER_BACKEND != 'hybrid' && github.event_name != 'workflow_dispatch' && github.repository == 'openclaw/openclaw' && (github.event_name != 'pull_request' || github.event.pull_request.head.repo.full_name == 'openclaw/openclaw') && 'true' || 'false' }} - use-actions-cache: ${{ vars.OPENCLAW_CI_RUNNER_BACKEND != 'github' && vars.OPENCLAW_CI_RUNNER_BACKEND != 'hybrid' && github.event_name != 'workflow_dispatch' && github.repository == 'openclaw/openclaw' && (github.event_name != 'pull_request' || github.event.pull_request.head.repo.full_name == 'openclaw/openclaw') && 'false' || 'true' }} restore-test-caches: ${{ (vars.OPENCLAW_CI_RUNNER_BACKEND == 'github' || vars.OPENCLAW_CI_RUNNER_BACKEND == 'hybrid') && 'true' || 'false' }} - name: Download exact-run built runtime @@ -1581,11 +1555,11 @@ jobs: - name: Setup Node environment uses: ./.github/actions/setup-node-env with: + cache-mode: restore install-bun: "false" # Same-repo Blacksmith runs restore the dependency cache published by # preflight; hosted paths use the pnpm store cache instead. dependency-cache: ${{ vars.OPENCLAW_CI_RUNNER_BACKEND != 'github' && vars.OPENCLAW_CI_RUNNER_BACKEND != 'hybrid' && github.event_name != 'workflow_dispatch' && github.repository == 'openclaw/openclaw' && (github.event_name != 'pull_request' || github.event.pull_request.head.repo.full_name == 'openclaw/openclaw') && 'true' || 'false' }} - use-actions-cache: ${{ vars.OPENCLAW_CI_RUNNER_BACKEND != 'github' && vars.OPENCLAW_CI_RUNNER_BACKEND != 'hybrid' && github.event_name != 'workflow_dispatch' && github.repository == 'openclaw/openclaw' && (github.event_name != 'pull_request' || github.event.pull_request.head.repo.full_name == 'openclaw/openclaw') && 'false' || 'true' }} - name: Verify native app i18n source run: | @@ -1624,18 +1598,18 @@ jobs: - name: Setup Node environment uses: ./.github/actions/setup-node-env with: + cache-mode: restore node-version: "24.x" install-bun: "false" # Same-repo Blacksmith runs restore the dependency cache published by # preflight; hosted paths use the pnpm store cache instead. dependency-cache: ${{ vars.OPENCLAW_CI_RUNNER_BACKEND != 'github' && vars.OPENCLAW_CI_RUNNER_BACKEND != 'hybrid' && github.event_name != 'workflow_dispatch' && github.repository == 'openclaw/openclaw' && (github.event_name != 'pull_request' || github.event.pull_request.head.repo.full_name == 'openclaw/openclaw') && 'true' || 'false' }} - use-actions-cache: ${{ vars.OPENCLAW_CI_RUNNER_BACKEND != 'github' && vars.OPENCLAW_CI_RUNNER_BACKEND != 'hybrid' && github.event_name != 'workflow_dispatch' && github.repository == 'openclaw/openclaw' && (github.event_name != 'pull_request' || github.event.pull_request.head.repo.full_name == 'openclaw/openclaw') && 'false' || 'true' }} restore-test-caches: ${{ (vars.OPENCLAW_CI_RUNNER_BACKEND == 'github' || vars.OPENCLAW_CI_RUNNER_BACKEND == 'hybrid') && 'true' || 'false' }} - &cache_playwright_chromium name: Cache Playwright Chromium if: needs.preflight.outputs.compatibility_target != 'true' - uses: actions/cache@27d5ce7f107fe9357f9df03efb73ab90386fccae # v5.0.5 + uses: actions/cache/restore@27d5ce7f107fe9357f9df03efb73ab90386fccae # v5.0.5 with: path: ~/.cache/ms-playwright key: ${{ runner.os }}-playwright-chromium-1.62.1 @@ -1699,12 +1673,12 @@ jobs: - name: Setup Node environment uses: ./.github/actions/setup-node-env with: + cache-mode: restore node-version: "24.x" install-bun: "false" # The github/hybrid planner profile uses the Actions pnpm-store cache # on either runner backend; all-Blacksmith mode restores preflight's tree. dependency-cache: ${{ (vars.OPENCLAW_CI_RUNNER_BACKEND == 'github' || vars.OPENCLAW_CI_RUNNER_BACKEND == 'hybrid' || github.event_name == 'workflow_dispatch' || (github.event_name == 'pull_request' && github.run_attempt > 1)) && 'false' || (github.repository == 'openclaw/openclaw' && (github.event_name != 'pull_request' || github.event.pull_request.head.repo.full_name == 'openclaw/openclaw') && 'true' || 'false') }} - use-actions-cache: ${{ (vars.OPENCLAW_CI_RUNNER_BACKEND == 'github' || vars.OPENCLAW_CI_RUNNER_BACKEND == 'hybrid' || github.event_name == 'workflow_dispatch' || (github.event_name == 'pull_request' && github.run_attempt > 1)) && 'true' || (github.repository == 'openclaw/openclaw' && (github.event_name != 'pull_request' || github.event.pull_request.head.repo.full_name == 'openclaw/openclaw') && 'false' || 'true') }} restore-test-caches: ${{ (vars.OPENCLAW_CI_RUNNER_BACKEND == 'github' || vars.OPENCLAW_CI_RUNNER_BACKEND == 'hybrid') && 'true' || 'false' }} - *cache_playwright_chromium @@ -1754,12 +1728,12 @@ jobs: - name: Setup Node environment uses: ./.github/actions/setup-node-env with: + cache-mode: restore node-version: "24.x" install-bun: "false" # The github/hybrid planner profile uses the Actions pnpm-store cache # on either runner backend; all-Blacksmith mode restores preflight's tree. dependency-cache: ${{ (vars.OPENCLAW_CI_RUNNER_BACKEND == 'github' || vars.OPENCLAW_CI_RUNNER_BACKEND == 'hybrid' || github.event_name == 'workflow_dispatch' || (github.event_name == 'pull_request' && github.run_attempt > 1)) && 'false' || (github.repository == 'openclaw/openclaw' && (github.event_name != 'pull_request' || github.event.pull_request.head.repo.full_name == 'openclaw/openclaw') && 'true' || 'false') }} - use-actions-cache: ${{ (vars.OPENCLAW_CI_RUNNER_BACKEND == 'github' || vars.OPENCLAW_CI_RUNNER_BACKEND == 'hybrid' || github.event_name == 'workflow_dispatch' || (github.event_name == 'pull_request' && github.run_attempt > 1)) && 'true' || (github.repository == 'openclaw/openclaw' && (github.event_name != 'pull_request' || github.event.pull_request.head.repo.full_name == 'openclaw/openclaw') && 'false' || 'true') }} - *cache_playwright_chromium - *install_playwright_chromium @@ -1815,12 +1789,12 @@ jobs: - name: Setup Node environment uses: ./.github/actions/setup-node-env with: + cache-mode: restore node-version: "24.x" install-bun: "false" # Same-repo Blacksmith runs restore the dependency cache published by # preflight; hosted paths use the pnpm store cache instead. dependency-cache: ${{ vars.OPENCLAW_CI_RUNNER_BACKEND != 'github' && vars.OPENCLAW_CI_RUNNER_BACKEND != 'hybrid' && github.event_name != 'workflow_dispatch' && github.repository == 'openclaw/openclaw' && (github.event_name != 'pull_request' || github.event.pull_request.head.repo.full_name == 'openclaw/openclaw') && 'true' || 'false' }} - use-actions-cache: ${{ vars.OPENCLAW_CI_RUNNER_BACKEND != 'github' && vars.OPENCLAW_CI_RUNNER_BACKEND != 'hybrid' && github.event_name != 'workflow_dispatch' && github.repository == 'openclaw/openclaw' && (github.event_name != 'pull_request' || github.event.pull_request.head.repo.full_name == 'openclaw/openclaw') && 'false' || 'true' }} - name: Verify Control UI i18n source run: | @@ -1904,11 +1878,11 @@ jobs: - name: Setup Node environment uses: ./.github/actions/setup-node-env with: + cache-mode: restore install-bun: ${{ matrix.task == 'bun-launcher' && 'true' || 'false' }} # Same-repo Blacksmith runs restore the dependency cache published by # preflight; hosted paths use the pnpm store cache instead. dependency-cache: ${{ vars.OPENCLAW_CI_RUNNER_BACKEND != 'github' && vars.OPENCLAW_CI_RUNNER_BACKEND != 'hybrid' && github.event_name != 'workflow_dispatch' && github.repository == 'openclaw/openclaw' && (github.event_name != 'pull_request' || github.event.pull_request.head.repo.full_name == 'openclaw/openclaw') && 'true' || 'false' }} - use-actions-cache: ${{ vars.OPENCLAW_CI_RUNNER_BACKEND != 'github' && vars.OPENCLAW_CI_RUNNER_BACKEND != 'hybrid' && github.event_name != 'workflow_dispatch' && github.repository == 'openclaw/openclaw' && (github.event_name != 'pull_request' || github.event.pull_request.head.repo.full_name == 'openclaw/openclaw') && 'false' || 'true' }} restore-test-caches: ${{ (vars.OPENCLAW_CI_RUNNER_BACKEND == 'github' || vars.OPENCLAW_CI_RUNNER_BACKEND == 'hybrid') && (matrix.task == 'bundled-protocol' || matrix.task == 'contracts-plugins-ci-routing' || matrix.task == 'ci-routing' || matrix.task == 'bun-launcher') && 'true' || 'false' }} - name: Run ${{ matrix.task }} (${{ matrix.runtime }}) @@ -1999,11 +1973,11 @@ jobs: - name: Setup Node environment uses: ./.github/actions/setup-node-env with: + cache-mode: restore install-bun: "false" # The github/hybrid planner profile uses the Actions pnpm-store cache # on either runner backend; all-Blacksmith mode restores preflight's tree. dependency-cache: ${{ vars.OPENCLAW_CI_RUNNER_BACKEND != 'github' && vars.OPENCLAW_CI_RUNNER_BACKEND != 'hybrid' && github.event_name != 'workflow_dispatch' && github.repository == 'openclaw/openclaw' && (github.event_name != 'pull_request' || github.event.pull_request.head.repo.full_name == 'openclaw/openclaw') && 'true' || 'false' }} - use-actions-cache: ${{ vars.OPENCLAW_CI_RUNNER_BACKEND != 'github' && vars.OPENCLAW_CI_RUNNER_BACKEND != 'hybrid' && github.event_name != 'workflow_dispatch' && github.repository == 'openclaw/openclaw' && (github.event_name != 'pull_request' || github.event.pull_request.head.repo.full_name == 'openclaw/openclaw') && 'false' || 'true' }} # Reader of build-artifacts' protected daily archive: warms tsdown # tooling plus the repeated openclaw.mjs boots in the scenario loop. node-compile-cache: "true" @@ -2183,11 +2157,11 @@ jobs: - name: Setup Node environment uses: ./.github/actions/setup-node-env with: + cache-mode: restore install-bun: "false" # Same-repo Blacksmith runs restore the dependency cache published by # preflight; hosted paths use the pnpm store cache instead. dependency-cache: ${{ vars.OPENCLAW_CI_RUNNER_BACKEND != 'github' && vars.OPENCLAW_CI_RUNNER_BACKEND != 'hybrid' && github.event_name != 'workflow_dispatch' && github.repository == 'openclaw/openclaw' && (github.event_name != 'pull_request' || github.event.pull_request.head.repo.full_name == 'openclaw/openclaw') && 'true' || 'false' }} - use-actions-cache: ${{ vars.OPENCLAW_CI_RUNNER_BACKEND != 'github' && vars.OPENCLAW_CI_RUNNER_BACKEND != 'hybrid' && github.event_name != 'workflow_dispatch' && github.repository == 'openclaw/openclaw' && (github.event_name != 'pull_request' || github.event.pull_request.head.repo.full_name == 'openclaw/openclaw') && 'false' || 'true' }} restore-test-caches: ${{ (vars.OPENCLAW_CI_RUNNER_BACKEND == 'github' || vars.OPENCLAW_CI_RUNNER_BACKEND == 'hybrid') && 'true' || 'false' }} - name: Run plugin contract shard @@ -2226,11 +2200,11 @@ jobs: - name: Setup Node environment uses: ./.github/actions/setup-node-env with: + cache-mode: restore install-bun: "false" # Same-repo Blacksmith runs restore the dependency cache published by # preflight; hosted paths use the pnpm store cache instead. dependency-cache: ${{ vars.OPENCLAW_CI_RUNNER_BACKEND != 'github' && vars.OPENCLAW_CI_RUNNER_BACKEND != 'hybrid' && github.event_name != 'workflow_dispatch' && github.repository == 'openclaw/openclaw' && (github.event_name != 'pull_request' || github.event.pull_request.head.repo.full_name == 'openclaw/openclaw') && 'true' || 'false' }} - use-actions-cache: ${{ vars.OPENCLAW_CI_RUNNER_BACKEND != 'github' && vars.OPENCLAW_CI_RUNNER_BACKEND != 'hybrid' && github.event_name != 'workflow_dispatch' && github.repository == 'openclaw/openclaw' && (github.event_name != 'pull_request' || github.event.pull_request.head.repo.full_name == 'openclaw/openclaw') && 'false' || 'true' }} restore-test-caches: ${{ (vars.OPENCLAW_CI_RUNNER_BACKEND == 'github' || vars.OPENCLAW_CI_RUNNER_BACKEND == 'hybrid') && 'true' || 'false' }} - name: Run channel contract shard @@ -2266,6 +2240,7 @@ jobs: - name: Setup Node environment uses: ./.github/actions/setup-node-env with: + cache-mode: restore node-version: "22.22.3" install-bun: "false" build-all-cache-scope: full @@ -2302,18 +2277,15 @@ jobs: - name: Setup Node environment uses: ./.github/actions/setup-node-env with: + cache-mode: restore node-version: "${{ matrix.node_version || '24.x' }}" install-bun: "false" - # Only the preflight writer saves an exact dependency archive. - # All-Blacksmith shards restore it; github/hybrid use the pnpm store. + # Blacksmith shards restore the exact dependency archive; + # github/hybrid use the pnpm store. dependency-cache: ${{ (matrix.node_version == null || matrix.node_version == '24.x') && vars.OPENCLAW_CI_RUNNER_BACKEND != 'github' && vars.OPENCLAW_CI_RUNNER_BACKEND != 'hybrid' && github.event_name != 'workflow_dispatch' && github.repository == 'openclaw/openclaw' && (github.event_name != 'pull_request' || github.event.pull_request.head.repo.full_name == 'openclaw/openclaw') && 'true' || 'false' }} - use-actions-cache: ${{ (matrix.node_version == null || matrix.node_version == '24.x') && vars.OPENCLAW_CI_RUNNER_BACKEND != 'github' && vars.OPENCLAW_CI_RUNNER_BACKEND != 'hybrid' && github.event_name != 'workflow_dispatch' && github.repository == 'openclaw/openclaw' && (github.event_name != 'pull_request' || github.event.pull_request.head.repo.full_name == 'openclaw/openclaw') && 'false' || 'true' }} vitest-fs-cache: "true" node-compile-cache: "true" node-compile-cache-scope: "test" - # The github/hybrid planner profile elects one in-run writer so the - # transform cache can recover without waiting for the cache warmer. - save-vitest-fs-cache: ${{ (vars.OPENCLAW_CI_RUNNER_BACKEND == 'github' || vars.OPENCLAW_CI_RUNNER_BACKEND == 'hybrid') && matrix.save_vitest_fs_cache && 'true' || 'false' }} - name: Setup Go for docs i18n if: matrix.requires_go == true @@ -2441,11 +2413,11 @@ jobs: - name: Setup Node environment uses: ./.github/actions/setup-node-env with: + cache-mode: restore install-bun: "false" # The github/hybrid planner profile uses the Actions pnpm-store cache # on either runner backend; all-Blacksmith mode restores preflight's tree. dependency-cache: ${{ vars.OPENCLAW_CI_RUNNER_BACKEND != 'github' && vars.OPENCLAW_CI_RUNNER_BACKEND != 'hybrid' && github.event_name != 'workflow_dispatch' && github.repository == 'openclaw/openclaw' && (github.event_name != 'pull_request' || github.event.pull_request.head.repo.full_name == 'openclaw/openclaw') && 'true' || 'false' }} - use-actions-cache: ${{ vars.OPENCLAW_CI_RUNNER_BACKEND != 'github' && vars.OPENCLAW_CI_RUNNER_BACKEND != 'hybrid' && github.event_name != 'workflow_dispatch' && github.repository == 'openclaw/openclaw' && (github.event_name != 'pull_request' || github.event.pull_request.head.repo.full_name == 'openclaw/openclaw') && 'false' || 'true' }} - name: Compute extension boundary input fingerprint id: extension-boundary-inputs @@ -2458,7 +2430,7 @@ jobs: - name: Cache extension package boundary artifacts for hosted lint if: matrix.task == 'lint' && (vars.OPENCLAW_CI_RUNNER_BACKEND == 'github' || vars.OPENCLAW_CI_RUNNER_BACKEND == 'hybrid' || (github.event_name == 'pull_request' && github.event.pull_request.head.repo.full_name != github.repository)) - uses: actions/cache@27d5ce7f107fe9357f9df03efb73ab90386fccae # v5.0.5 + uses: actions/cache/restore@27d5ce7f107fe9357f9df03efb73ab90386fccae # v5.0.5 with: path: | dist/plugin-sdk @@ -2758,9 +2730,9 @@ jobs: - name: Setup Node environment uses: ./.github/actions/setup-node-env with: + cache-mode: restore install-bun: "false" dependency-cache: ${{ vars.OPENCLAW_CI_RUNNER_BACKEND != 'github' && vars.OPENCLAW_CI_RUNNER_BACKEND != 'hybrid' && github.event_name != 'workflow_dispatch' && github.repository == 'openclaw/openclaw' && (github.event_name != 'pull_request' || github.event.pull_request.head.repo.full_name == 'openclaw/openclaw') && 'true' || 'false' }} - use-actions-cache: ${{ vars.OPENCLAW_CI_RUNNER_BACKEND != 'github' && vars.OPENCLAW_CI_RUNNER_BACKEND != 'hybrid' && github.event_name != 'workflow_dispatch' && github.repository == 'openclaw/openclaw' && (github.event_name != 'pull_request' || github.event.pull_request.head.repo.full_name == 'openclaw/openclaw') && 'false' || 'true' }} - name: Run hosted core lint stripe env: @@ -2795,9 +2767,9 @@ jobs: - name: Setup Node environment uses: ./.github/actions/setup-node-env with: + cache-mode: restore install-bun: "false" dependency-cache: ${{ vars.OPENCLAW_CI_RUNNER_BACKEND != 'github' && vars.OPENCLAW_CI_RUNNER_BACKEND != 'hybrid' && github.event_name != 'workflow_dispatch' && github.repository == 'openclaw/openclaw' && (github.event_name != 'pull_request' || github.event.pull_request.head.repo.full_name == 'openclaw/openclaw') && 'true' || 'false' }} - use-actions-cache: ${{ vars.OPENCLAW_CI_RUNNER_BACKEND != 'github' && vars.OPENCLAW_CI_RUNNER_BACKEND != 'hybrid' && github.event_name != 'workflow_dispatch' && github.repository == 'openclaw/openclaw' && (github.event_name != 'pull_request' || github.event.pull_request.head.repo.full_name == 'openclaw/openclaw') && 'false' || 'true' }} - name: Run hosted core test-types stripe shell: bash @@ -2884,11 +2856,11 @@ jobs: - name: Setup Node environment uses: ./.github/actions/setup-node-env with: + cache-mode: restore install-bun: "false" # The github/hybrid planner profile uses the Actions pnpm-store cache # on either runner backend; all-Blacksmith mode restores preflight's tree. dependency-cache: ${{ vars.OPENCLAW_CI_RUNNER_BACKEND != 'github' && vars.OPENCLAW_CI_RUNNER_BACKEND != 'hybrid' && github.event_name != 'workflow_dispatch' && github.repository == 'openclaw/openclaw' && (github.event_name != 'pull_request' || github.event.pull_request.head.repo.full_name == 'openclaw/openclaw') && 'true' || 'false' }} - use-actions-cache: ${{ vars.OPENCLAW_CI_RUNNER_BACKEND != 'github' && vars.OPENCLAW_CI_RUNNER_BACKEND != 'hybrid' && github.event_name != 'workflow_dispatch' && github.repository == 'openclaw/openclaw' && (github.event_name != 'pull_request' || github.event.pull_request.head.repo.full_name == 'openclaw/openclaw') && 'false' || 'true' }} - name: Compute extension boundary input fingerprint id: extension-boundary-inputs @@ -2956,7 +2928,7 @@ jobs: - name: Cache extension package boundary artifacts id: extension-package-boundary-cache if: matrix.group == 'extension-package-boundary' - uses: actions/cache@27d5ce7f107fe9357f9df03efb73ab90386fccae # v5.0.5 + uses: actions/cache/restore@27d5ce7f107fe9357f9df03efb73ab90386fccae # v5.0.5 with: path: | dist/plugin-sdk @@ -3160,11 +3132,11 @@ jobs: - name: Setup Node environment uses: ./.github/actions/setup-node-env with: + cache-mode: restore install-bun: "false" # Same-repo Blacksmith runs restore the dependency cache published by # preflight; hosted paths use the pnpm store cache instead. dependency-cache: ${{ vars.OPENCLAW_CI_RUNNER_BACKEND != 'github' && vars.OPENCLAW_CI_RUNNER_BACKEND != 'hybrid' && github.event_name != 'workflow_dispatch' && github.repository == 'openclaw/openclaw' && (github.event_name != 'pull_request' || github.event.pull_request.head.repo.full_name == 'openclaw/openclaw') && 'true' || 'false' }} - use-actions-cache: ${{ vars.OPENCLAW_CI_RUNNER_BACKEND != 'github' && vars.OPENCLAW_CI_RUNNER_BACKEND != 'hybrid' && github.event_name != 'workflow_dispatch' && github.repository == 'openclaw/openclaw' && (github.event_name != 'pull_request' || github.event.pull_request.head.repo.full_name == 'openclaw/openclaw') && 'false' || 'true' }} - name: Check formatting if: needs.preflight.outputs.run_format_check == 'true' @@ -3373,6 +3345,7 @@ jobs: - name: Setup pnpm uses: ./.github/actions/setup-pnpm-store-cache with: + cache-mode: restore node-version: 22.x - name: Runtime versions @@ -3439,8 +3412,8 @@ jobs: - name: Setup Node environment uses: ./.github/actions/setup-node-env with: + cache-mode: restore install-bun: "false" - save-actions-cache: "true" - name: TS tests (macOS) env: @@ -3715,6 +3688,7 @@ jobs: - name: Setup Node environment uses: ./.github/actions/setup-node-env with: + cache-mode: restore install-bun: "false" - name: Install iOS Swift tooling @@ -3937,6 +3911,7 @@ jobs: if: needs.preflight.outputs.use_compatible_android_ci != 'true' uses: ./.github/actions/setup-node-env with: + cache-mode: restore install-bun: "false" # Same-repo runs carry the Gradle user home (dependency, wrapper, and @@ -4086,10 +4061,10 @@ jobs: - name: Setup Node environment uses: ./.github/actions/setup-node-env with: + cache-mode: restore node-version: "24.x" install-bun: "false" dependency-cache: ${{ (vars.OPENCLAW_CI_RUNNER_BACKEND == 'github' || vars.OPENCLAW_CI_RUNNER_BACKEND == 'hybrid' || github.event_name == 'workflow_dispatch' || (github.event_name == 'pull_request' && github.run_attempt > 1)) && 'false' || (github.repository == 'openclaw/openclaw' && (github.event_name != 'pull_request' || github.event.pull_request.head.repo.full_name == 'openclaw/openclaw') && 'true' || 'false') }} - use-actions-cache: ${{ (vars.OPENCLAW_CI_RUNNER_BACKEND == 'github' || vars.OPENCLAW_CI_RUNNER_BACKEND == 'hybrid' || github.event_name == 'workflow_dispatch' || (github.event_name == 'pull_request' && github.run_attempt > 1)) && 'true' || (github.repository == 'openclaw/openclaw' && (github.event_name != 'pull_request' || github.event.pull_request.head.repo.full_name == 'openclaw/openclaw') && 'false' || 'true') }} - name: Run changed Docker seed owner lanes env: diff --git a/.github/workflows/codeql-android-critical-security.yml b/.github/workflows/codeql-android-critical-security.yml index 36954faacdbe..d4845ea53add 100644 --- a/.github/workflows/codeql-android-critical-security.yml +++ b/.github/workflows/codeql-android-critical-security.yml @@ -37,6 +37,7 @@ jobs: - name: Setup Node environment uses: ./.github/actions/setup-node-env with: + cache-mode: restore install-bun: "false" - name: Initialize CodeQL diff --git a/.github/workflows/control-ui-locale-refresh.yml b/.github/workflows/control-ui-locale-refresh.yml index ff76be30a5fd..044ed0f36b8f 100644 --- a/.github/workflows/control-ui-locale-refresh.yml +++ b/.github/workflows/control-ui-locale-refresh.yml @@ -166,6 +166,7 @@ jobs: - name: Setup Node environment uses: ./.github/actions/setup-node-env with: + cache-mode: restore install-bun: "false" - name: Ensure translation provider secrets exist @@ -303,6 +304,7 @@ jobs: - name: Setup Node environment uses: ./.github/actions/setup-node-env with: + cache-mode: restore install-bun: "false" - name: Finalize control UI generated artifacts diff --git a/.github/workflows/crabbox-hydrate.yml b/.github/workflows/crabbox-hydrate.yml index 862ed16e048f..14c7f8030a2b 100644 --- a/.github/workflows/crabbox-hydrate.yml +++ b/.github/workflows/crabbox-hydrate.yml @@ -686,8 +686,8 @@ jobs: env: PNPM_HOME: ${{ runner.temp }}/pnpm-home with: + cache-mode: off install-bun: "false" - use-actions-cache: "false" - name: Prepare Crabbox shell shell: bash diff --git a/.github/workflows/dated-todo-sweep.yml b/.github/workflows/dated-todo-sweep.yml index 511ad64f5685..f4608f4a948d 100644 --- a/.github/workflows/dated-todo-sweep.yml +++ b/.github/workflows/dated-todo-sweep.yml @@ -40,6 +40,7 @@ jobs: - name: Setup Node environment uses: ./.github/actions/setup-node-env with: + cache-mode: restore node-version: "24.x" install-bun: "false" diff --git a/.github/workflows/docs-agent.yml b/.github/workflows/docs-agent.yml index dc4453a0f658..a8084532364f 100644 --- a/.github/workflows/docs-agent.yml +++ b/.github/workflows/docs-agent.yml @@ -134,6 +134,7 @@ jobs: if: steps.gate.outputs.run_agent == 'true' uses: ./.github/actions/setup-node-env with: + cache-mode: restore install-bun: "false" - name: Ensure docs agent key exists diff --git a/.github/workflows/docs-external-links.yml b/.github/workflows/docs-external-links.yml index 7bcc6b37958a..b6775d5a1335 100644 --- a/.github/workflows/docs-external-links.yml +++ b/.github/workflows/docs-external-links.yml @@ -25,6 +25,7 @@ jobs: - name: Setup Node.js and pnpm uses: ./.github/actions/setup-node-env with: + cache-mode: restore install-bun: "false" install-deps: "false" diff --git a/.github/workflows/docs.yml b/.github/workflows/docs.yml index 0e2e2234ab14..3cc63801bf5a 100644 --- a/.github/workflows/docs.yml +++ b/.github/workflows/docs.yml @@ -34,6 +34,7 @@ jobs: - name: Setup Node environment uses: ./.github/actions/setup-node-env with: + cache-mode: restore install-bun: "false" - name: Checkout ClawHub docs source diff --git a/.github/workflows/install-smoke-reusable.yml b/.github/workflows/install-smoke-reusable.yml index 891295f20fdc..0a0fae38f228 100644 --- a/.github/workflows/install-smoke-reusable.yml +++ b/.github/workflows/install-smoke-reusable.yml @@ -662,6 +662,7 @@ jobs: - name: Setup Node environment for installer smoke uses: ./.github/actions/setup-node-env with: + cache-mode: restore install-bun: "false" install-deps: "true" @@ -796,6 +797,7 @@ jobs: - name: Setup Node environment for Bun smoke uses: ./.github/actions/setup-node-env with: + cache-mode: restore install-bun: "true" install-deps: "true" @@ -829,5 +831,6 @@ jobs: - name: Setup Node environment for package smoke uses: ./.github/actions/setup-node-env with: + cache-mode: restore install-bun: "false" install-deps: "true" diff --git a/.github/workflows/ios-periphery.yml b/.github/workflows/ios-periphery.yml index 96f2774f2158..ac82ce8f916c 100644 --- a/.github/workflows/ios-periphery.yml +++ b/.github/workflows/ios-periphery.yml @@ -107,6 +107,7 @@ jobs: - name: Setup Node environment uses: ./.github/actions/setup-node-env with: + cache-mode: restore install-bun: "false" - name: Install iOS Swift tooling diff --git a/.github/workflows/linux-app-release.yml b/.github/workflows/linux-app-release.yml index 3a3bced9fb9c..47cb1f5f2472 100644 --- a/.github/workflows/linux-app-release.yml +++ b/.github/workflows/linux-app-release.yml @@ -109,6 +109,7 @@ jobs: - name: Setup Node environment uses: ./.github/actions/setup-node-env with: + cache-mode: restore install-bun: "false" - name: Build Linux companion bundles @@ -183,6 +184,7 @@ jobs: - name: Setup Node environment uses: ./.github/actions/setup-node-env with: + cache-mode: restore install-bun: "false" - name: Build macOS test bundles @@ -250,6 +252,7 @@ jobs: - name: Setup Node environment uses: ./.github/actions/setup-node-env with: + cache-mode: restore install-bun: "false" - name: Build Windows test bundle diff --git a/.github/workflows/linux-app.yml b/.github/workflows/linux-app.yml index 2b41b8d838f4..d0a984547d44 100644 --- a/.github/workflows/linux-app.yml +++ b/.github/workflows/linux-app.yml @@ -66,6 +66,7 @@ jobs: - name: Setup Node environment uses: ./.github/actions/setup-node-env with: + cache-mode: restore install-bun: "false" - name: Check Rust formatting @@ -124,6 +125,7 @@ jobs: - name: Setup Node environment uses: ./.github/actions/setup-node-env with: + cache-mode: restore install-bun: "false" - name: Cache Cargo diff --git a/.github/workflows/macos-periphery.yml b/.github/workflows/macos-periphery.yml index 8c6a5fc92d67..025cc9caf514 100644 --- a/.github/workflows/macos-periphery.yml +++ b/.github/workflows/macos-periphery.yml @@ -98,6 +98,7 @@ jobs: - name: Setup Node environment uses: ./.github/actions/setup-node-env with: + cache-mode: restore install-bun: "false" - name: Install Periphery diff --git a/.github/workflows/macos-release.yml b/.github/workflows/macos-release.yml index 4f59472b8072..51e7ddf4f40e 100644 --- a/.github/workflows/macos-release.yml +++ b/.github/workflows/macos-release.yml @@ -51,6 +51,7 @@ jobs: - name: Setup Node environment uses: ./.github/actions/setup-node-env with: + cache-mode: restore node-version: ${{ env.NODE_VERSION }} install-bun: "false" build-all-cache-scope: full diff --git a/.github/workflows/mantis-discord-smoke.yml b/.github/workflows/mantis-discord-smoke.yml index e36a7e688c24..25fca70079f4 100644 --- a/.github/workflows/mantis-discord-smoke.yml +++ b/.github/workflows/mantis-discord-smoke.yml @@ -140,6 +140,7 @@ jobs: - name: Setup Node environment uses: ./.github/actions/setup-node-env with: + cache-mode: restore node-version: ${{ env.NODE_VERSION }} install-bun: "true" diff --git a/.github/workflows/mantis-discord-status-reactions.yml b/.github/workflows/mantis-discord-status-reactions.yml index bc3321f8c179..829a0332f872 100644 --- a/.github/workflows/mantis-discord-status-reactions.yml +++ b/.github/workflows/mantis-discord-status-reactions.yml @@ -129,6 +129,7 @@ jobs: - name: Setup Node environment uses: ./.github/actions/setup-node-env with: + cache-mode: restore node-version: ${{ env.NODE_VERSION }} install-bun: "true" diff --git a/.github/workflows/mantis-discord-thread-attachment.yml b/.github/workflows/mantis-discord-thread-attachment.yml index 0c95f3e49855..ed009a2821eb 100644 --- a/.github/workflows/mantis-discord-thread-attachment.yml +++ b/.github/workflows/mantis-discord-thread-attachment.yml @@ -131,6 +131,7 @@ jobs: - name: Setup Node environment uses: ./.github/actions/setup-node-env with: + cache-mode: restore node-version: ${{ env.NODE_VERSION }} install-bun: "true" diff --git a/.github/workflows/mantis-slack-desktop-smoke.yml b/.github/workflows/mantis-slack-desktop-smoke.yml index 6093b3e27592..76520473caea 100644 --- a/.github/workflows/mantis-slack-desktop-smoke.yml +++ b/.github/workflows/mantis-slack-desktop-smoke.yml @@ -140,6 +140,7 @@ jobs: - name: Setup Node environment uses: ./.github/actions/setup-node-env with: + cache-mode: restore node-version: ${{ env.NODE_VERSION }} install-bun: "true" @@ -147,7 +148,7 @@ jobs: run: pnpm build - name: Cache Mantis candidate pnpm store - uses: actions/cache@27d5ce7f107fe9357f9df03efb73ab90386fccae # v5.0.5 + uses: actions/cache/restore@27d5ce7f107fe9357f9df03efb73ab90386fccae # v5.0.5 with: path: | ~/.local/share/pnpm/store diff --git a/.github/workflows/mantis-telegram-desktop-proof.yml b/.github/workflows/mantis-telegram-desktop-proof.yml index a72d34d96509..40daa6b800df 100644 --- a/.github/workflows/mantis-telegram-desktop-proof.yml +++ b/.github/workflows/mantis-telegram-desktop-proof.yml @@ -290,8 +290,10 @@ jobs: fetch-depth: 1 - name: Setup Node environment + id: setup-node-env uses: ./.github/actions/setup-node-env with: + cache-mode: read-write node-version: ${{ env.NODE_VERSION }} install-bun: "true" @@ -588,7 +590,7 @@ jobs: test -z "$(find "$build_cache_root" -type f -links +1 -print -quit)" - name: Save exact baseline build - if: steps.baseline_build_cache.outputs.cache-hit != 'true' + if: steps.setup-node-env.outputs.cache-mode == 'read-write' && steps.baseline_build_cache.outputs.cache-hit != 'true' continue-on-error: true uses: actions/cache/save@27d5ce7f107fe9357f9df03efb73ab90386fccae # v5.0.5 with: @@ -1279,6 +1281,7 @@ jobs: - name: Setup Node environment uses: ./.github/actions/setup-node-env with: + cache-mode: restore node-version: ${{ env.NODE_VERSION }} install-bun: "true" diff --git a/.github/workflows/mantis-telegram-live.yml b/.github/workflows/mantis-telegram-live.yml index 66e201746f02..72bb9e0d2f87 100644 --- a/.github/workflows/mantis-telegram-live.yml +++ b/.github/workflows/mantis-telegram-live.yml @@ -229,6 +229,7 @@ jobs: - name: Setup Node environment uses: ./.github/actions/setup-node-env with: + cache-mode: restore node-version: ${{ env.NODE_VERSION }} install-bun: "true" @@ -236,7 +237,7 @@ jobs: run: pnpm build - name: Cache Mantis candidate pnpm store - uses: actions/cache@27d5ce7f107fe9357f9df03efb73ab90386fccae # v5.0.5 + uses: actions/cache/restore@27d5ce7f107fe9357f9df03efb73ab90386fccae # v5.0.5 with: path: | ~/.local/share/pnpm/store diff --git a/.github/workflows/mantis-web-ui-chat-proof.yml b/.github/workflows/mantis-web-ui-chat-proof.yml index 467ea7d6f243..d84cea87498b 100644 --- a/.github/workflows/mantis-web-ui-chat-proof.yml +++ b/.github/workflows/mantis-web-ui-chat-proof.yml @@ -126,6 +126,7 @@ jobs: - name: Setup Node environment uses: ./.github/actions/setup-node-env with: + cache-mode: restore node-version: ${{ env.NODE_VERSION }} install-bun: "false" install-deps: "false" diff --git a/.github/workflows/maturity-scorecard.yml b/.github/workflows/maturity-scorecard.yml index 04c2ea32c661..532ee2136b37 100644 --- a/.github/workflows/maturity-scorecard.yml +++ b/.github/workflows/maturity-scorecard.yml @@ -406,6 +406,7 @@ jobs: - name: Setup Node environment uses: ./.github/actions/setup-node-env with: + cache-mode: restore node-version: ${{ env.NODE_VERSION }} install-bun: "false" diff --git a/.github/workflows/native-app-locale-refresh.yml b/.github/workflows/native-app-locale-refresh.yml index 4d688b4f8071..7e39ac1f2ad5 100644 --- a/.github/workflows/native-app-locale-refresh.yml +++ b/.github/workflows/native-app-locale-refresh.yml @@ -149,6 +149,7 @@ jobs: - name: Setup Node environment uses: ./.github/actions/setup-node-env with: + cache-mode: restore install-bun: "false" - name: Ensure translation provider secrets exist @@ -283,6 +284,7 @@ jobs: - name: Setup Node environment uses: ./.github/actions/setup-node-env with: + cache-mode: restore install-bun: "false" # Source PRs own the stable-ID inventory. Locale workers only contribute diff --git a/.github/workflows/node22-compat.yml b/.github/workflows/node22-compat.yml index 6c91fa71d068..438920a4fc78 100644 --- a/.github/workflows/node22-compat.yml +++ b/.github/workflows/node22-compat.yml @@ -21,6 +21,7 @@ jobs: - name: Setup Node environment uses: ./.github/actions/setup-node-env with: + cache-mode: restore node-version: "22.22.3" install-bun: "false" diff --git a/.github/workflows/npm-telegram-beta-e2e.yml b/.github/workflows/npm-telegram-beta-e2e.yml index 1cf9889f93fa..eb3432e388c2 100644 --- a/.github/workflows/npm-telegram-beta-e2e.yml +++ b/.github/workflows/npm-telegram-beta-e2e.yml @@ -228,6 +228,7 @@ jobs: - name: Setup Node environment uses: ./.github/actions/setup-node-env with: + cache-mode: restore node-version: ${{ env.NODE_VERSION }} install-bun: "true" diff --git a/.github/workflows/openclaw-cross-os-release-checks-reusable.yml b/.github/workflows/openclaw-cross-os-release-checks-reusable.yml index 0fc9431164f7..140e9f3e97b3 100644 --- a/.github/workflows/openclaw-cross-os-release-checks-reusable.yml +++ b/.github/workflows/openclaw-cross-os-release-checks-reusable.yml @@ -467,7 +467,7 @@ jobs: node-version: ${{ env.NODE_VERSION }} package-manager-file: ${{ inputs.candidate_artifact_name == '' && 'source/package.json' || 'workflow/package.json' }} lockfile-path: ${{ inputs.candidate_artifact_name == '' && 'source/pnpm-lock.yaml' || 'workflow/pnpm-lock.yaml' }} - use-actions-cache: ${{ inputs.candidate_artifact_name == '' && 'true' || 'false' }} + cache-mode: ${{ inputs.candidate_artifact_name == '' && 'restore' || 'off' }} - name: Resolve provider-owned companion requirements id: provider_requirements @@ -907,7 +907,7 @@ jobs: node-version: ${{ env.NODE_VERSION }} package-manager-file: workflow/package.json lockfile-path: workflow/pnpm-lock.yaml - use-actions-cache: "false" + cache-mode: off - name: Download candidate artifact id: download_candidate diff --git a/.github/workflows/openclaw-live-and-e2e-checks-reusable.yml b/.github/workflows/openclaw-live-and-e2e-checks-reusable.yml index 069d628438f3..ac7e64c541ba 100644 --- a/.github/workflows/openclaw-live-and-e2e-checks-reusable.yml +++ b/.github/workflows/openclaw-live-and-e2e-checks-reusable.yml @@ -944,6 +944,7 @@ jobs: - name: Setup Node environment uses: ./.github/actions/setup-node-env with: + cache-mode: restore node-version: ${{ env.NODE_VERSION }} install-bun: "true" @@ -993,6 +994,7 @@ jobs: - name: Setup Node environment uses: ./.github/actions/setup-node-env with: + cache-mode: restore node-version: ${{ env.NODE_VERSION }} install-bun: "true" build-all-cache-scope: full @@ -1040,6 +1042,7 @@ jobs: - name: Setup Node environment uses: ./.github/actions/setup-node-env with: + cache-mode: restore node-version: ${{ env.NODE_VERSION }} install-bun: "true" build-all-cache-scope: full @@ -1280,6 +1283,7 @@ jobs: if: contains(matrix.profiles, inputs.release_test_profile) uses: ./.github/actions/setup-node-env with: + cache-mode: restore node-version: ${{ env.NODE_VERSION }} install-bun: "true" @@ -1661,6 +1665,7 @@ jobs: - name: Setup Node environment uses: ./.github/actions/setup-node-env with: + cache-mode: restore node-version: ${{ env.NODE_VERSION }} install-bun: "true" @@ -1953,10 +1958,10 @@ jobs: - name: Setup Node environment uses: ./.github/actions/setup-node-env with: + cache-mode: off node-version: ${{ env.NODE_VERSION }} install-bun: "false" install-deps: "false" - use-actions-cache: "false" - name: Validate Open WebUI credentials shell: bash @@ -2213,6 +2218,7 @@ jobs: if: (steps.plan.outputs.needs_package == '1' && inputs.package_artifact_name == '' && inputs.package_artifact_run_id == '') || (inputs.enable_prepublish_plugin_registry && steps.plan.outputs.needs_prepublish_plugin_registry == '1' && inputs.prepublish_plugin_registry_artifact_id == '') uses: ./.github/actions/setup-node-env with: + cache-mode: restore node-version: ${{ env.NODE_VERSION }} install-bun: "true" @@ -3051,6 +3057,7 @@ jobs: if: contains(matrix.profiles, inputs.release_test_profile) uses: ./.github/actions/setup-node-env with: + cache-mode: restore node-version: ${{ env.NODE_VERSION }} install-bun: "true" @@ -3218,6 +3225,7 @@ jobs: - name: Setup Node environment uses: ./.github/actions/setup-node-env with: + cache-mode: restore node-version: ${{ env.NODE_VERSION }} install-bun: "true" @@ -3608,6 +3616,7 @@ jobs: if: contains(matrix.profiles, inputs.release_test_profile) && (inputs.live_suite_filter == '' || inputs.live_suite_filter == matrix.suite_id || (inputs.live_suite_filter == 'native-live-src-gateway-profiles-anthropic' && startsWith(matrix.suite_id, 'native-live-src-gateway-profiles-anthropic-')) || (inputs.live_suite_filter == 'native-live-src-gateway-profiles-opencode-go' && startsWith(matrix.suite_id, 'native-live-src-gateway-profiles-opencode-go-'))) uses: ./.github/actions/setup-node-env with: + cache-mode: restore node-version: ${{ env.NODE_VERSION }} install-bun: "true" @@ -3931,6 +3940,7 @@ jobs: if: steps.codex_compat.outputs.run_lane != 'false' && contains(matrix.profiles, inputs.release_test_profile) && (inputs.live_suite_filter == '' || inputs.live_suite_filter == matrix.suite_id || inputs.live_suite_filter == matrix.suite_group) uses: ./.github/actions/setup-node-env with: + cache-mode: restore node-version: ${{ env.NODE_VERSION }} install-bun: "true" @@ -4163,6 +4173,7 @@ jobs: if: contains(matrix.profiles, inputs.release_test_profile) && (inputs.live_suite_filter == '' || inputs.live_suite_filter == matrix.suite_id || (inputs.live_suite_filter == 'native-live-extensions-media-video' && startsWith(matrix.suite_id, 'native-live-extensions-media-video-'))) uses: ./.github/actions/setup-node-env with: + cache-mode: restore node-version: ${{ env.NODE_VERSION }} install-bun: "true" diff --git a/.github/workflows/openclaw-npm-release.yml b/.github/workflows/openclaw-npm-release.yml index 152e53ca7448..320421f2c168 100644 --- a/.github/workflows/openclaw-npm-release.yml +++ b/.github/workflows/openclaw-npm-release.yml @@ -149,8 +149,10 @@ jobs: fi - name: Setup Node environment + id: setup-node-env uses: ./.github/actions/setup-node-env with: + cache-mode: read-write node-version: ${{ env.NODE_VERSION }} install-bun: "true" build-all-cache-scope: full @@ -281,7 +283,7 @@ jobs: run: pnpm ui:build - name: Save preflight build outputs - if: steps.dist_build_cache.outputs.cache-hit != 'true' + if: steps.setup-node-env.outputs.cache-mode == 'read-write' && steps.dist_build_cache.outputs.cache-hit != 'true' continue-on-error: true uses: actions/cache/save@27d5ce7f107fe9357f9df03efb73ab90386fccae # v5.0.5 with: @@ -994,6 +996,7 @@ jobs: - name: Setup Node environment uses: ./.github/actions/setup-node-env with: + cache-mode: restore node-version: ${{ env.NODE_VERSION }} install-bun: "false" diff --git a/.github/workflows/openclaw-performance.yml b/.github/workflows/openclaw-performance.yml index db6f0d9da410..e3b85def4865 100644 --- a/.github/workflows/openclaw-performance.yml +++ b/.github/workflows/openclaw-performance.yml @@ -299,6 +299,7 @@ jobs: if: steps.lane.outputs.run == 'true' uses: ./.github/actions/setup-node-env with: + cache-mode: restore install-bun: "false" - name: Prepare systemd user session @@ -669,6 +670,7 @@ jobs: - name: Set up source performance environment uses: ./.github/actions/setup-node-env with: + cache-mode: restore install-bun: "false" - name: Fetch previous source performance baseline diff --git a/.github/workflows/openclaw-release-checks.yml b/.github/workflows/openclaw-release-checks.yml index 2fb796db70ae..61f4140d29fe 100644 --- a/.github/workflows/openclaw-release-checks.yml +++ b/.github/workflows/openclaw-release-checks.yml @@ -812,6 +812,7 @@ jobs: if: inputs.candidate_artifact_json == '' uses: ./.github/actions/setup-node-env with: + cache-mode: restore node-version: ${{ env.NODE_VERSION }} install-bun: "true" install-deps: "true" @@ -1413,6 +1414,7 @@ jobs: - name: Setup Node environment uses: ./.github/actions/setup-node-env with: + cache-mode: restore node-version: ${{ env.NODE_VERSION }} install-bun: "true" @@ -1550,6 +1552,7 @@ jobs: - name: Setup Node environment uses: ./.github/actions/setup-node-env with: + cache-mode: restore node-version: ${{ env.NODE_VERSION }} install-bun: "true" @@ -1677,6 +1680,7 @@ jobs: - name: Setup Node environment uses: ./.github/actions/setup-node-env with: + cache-mode: restore node-version: ${{ env.NODE_VERSION }} install-bun: "true" @@ -2004,6 +2008,7 @@ jobs: - name: Setup Node environment uses: ./.github/actions/setup-node-env with: + cache-mode: restore node-version: ${{ env.NODE_VERSION }} install-bun: "true" @@ -2258,6 +2263,7 @@ jobs: - name: Setup Node environment uses: ./.github/actions/setup-node-env with: + cache-mode: restore node-version: ${{ env.NODE_VERSION }} install-bun: "true" @@ -2376,6 +2382,7 @@ jobs: - name: Setup Node environment uses: ./.github/actions/setup-node-env with: + cache-mode: restore node-version: ${{ env.NODE_VERSION }} install-bun: "true" @@ -2472,6 +2479,7 @@ jobs: - name: Setup Node environment uses: ./.github/actions/setup-node-env with: + cache-mode: restore node-version: ${{ env.NODE_VERSION }} install-bun: "true" diff --git a/.github/workflows/openclaw-release-publish.yml b/.github/workflows/openclaw-release-publish.yml index b3325d9f32e4..d065795ba8de 100644 --- a/.github/workflows/openclaw-release-publish.yml +++ b/.github/workflows/openclaw-release-publish.yml @@ -759,6 +759,7 @@ jobs: - name: Setup Node environment uses: ./.github/actions/setup-node-env with: + cache-mode: restore install-deps: "false" install-bun: "false" diff --git a/.github/workflows/openclaw-release-telegram-qa.yml b/.github/workflows/openclaw-release-telegram-qa.yml index 742e576393e7..49c0fba311d6 100644 --- a/.github/workflows/openclaw-release-telegram-qa.yml +++ b/.github/workflows/openclaw-release-telegram-qa.yml @@ -283,10 +283,10 @@ jobs: - name: Setup candidate build toolchain uses: ./.github/actions/setup-node-env with: + cache-mode: off node-version: ${{ env.NODE_VERSION }} install-bun: "true" install-deps: "false" - use-actions-cache: "false" - name: Checkout candidate runtime uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6.0.3 @@ -774,10 +774,10 @@ jobs: - name: Setup trusted harness Node environment uses: ./.github/actions/setup-node-env with: + cache-mode: off node-version: ${{ env.NODE_VERSION }} install-bun: "true" install-deps: "true" - use-actions-cache: "false" - name: Build trusted QA harness id: build_harness diff --git a/.github/workflows/package-acceptance.yml b/.github/workflows/package-acceptance.yml index 720d1ef0180f..976673f32d71 100644 --- a/.github/workflows/package-acceptance.yml +++ b/.github/workflows/package-acceptance.yml @@ -466,6 +466,7 @@ jobs: - name: Setup Node environment uses: ./.github/actions/setup-node-env with: + cache-mode: restore node-version: ${{ env.NODE_VERSION }} install-bun: ${{ inputs.source == 'ref' && 'true' || 'false' }} install-deps: "true" @@ -808,6 +809,7 @@ jobs: - name: Setup package validation dependencies uses: ./.github/actions/setup-node-env with: + cache-mode: restore node-version: ${{ env.NODE_VERSION }} install-bun: "false" install-deps: "true" diff --git a/.github/workflows/plugin-clawhub-new.yml b/.github/workflows/plugin-clawhub-new.yml index 57b960156556..800b57c1b010 100644 --- a/.github/workflows/plugin-clawhub-new.yml +++ b/.github/workflows/plugin-clawhub-new.yml @@ -199,6 +199,7 @@ jobs: - name: Setup Node environment uses: ./.github/actions/setup-node-env with: + cache-mode: restore node-version: ${{ env.NODE_VERSION }} install-bun: "false" @@ -497,6 +498,7 @@ jobs: - name: Setup Node environment uses: ./.github/actions/setup-node-env with: + cache-mode: restore node-version: ${{ env.NODE_VERSION }} install-bun: "true" install-deps: "true" diff --git a/.github/workflows/plugin-clawhub-release.yml b/.github/workflows/plugin-clawhub-release.yml index 142c82687f77..aa61059512bf 100644 --- a/.github/workflows/plugin-clawhub-release.yml +++ b/.github/workflows/plugin-clawhub-release.yml @@ -136,6 +136,7 @@ jobs: - name: Setup Node environment uses: ./.github/actions/setup-node-env with: + cache-mode: restore node-version: ${{ env.NODE_VERSION }} install-bun: "false" @@ -335,6 +336,7 @@ jobs: - name: Setup Node environment uses: ./.github/actions/setup-node-env with: + cache-mode: restore node-version: ${{ env.NODE_VERSION }} install-bun: "true" install-deps: "true" @@ -436,10 +438,10 @@ jobs: - name: Setup Node environment uses: ./.github/actions/setup-node-env with: + cache-mode: off node-version: ${{ env.NODE_VERSION }} install-bun: "false" install-deps: "false" - use-actions-cache: "false" - name: Download published package input uses: actions/download-artifact@3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c # v8 diff --git a/.github/workflows/plugin-init-scaffold-validation.yml b/.github/workflows/plugin-init-scaffold-validation.yml index 600bf002cfcd..8bac22018b6f 100644 --- a/.github/workflows/plugin-init-scaffold-validation.yml +++ b/.github/workflows/plugin-init-scaffold-validation.yml @@ -45,6 +45,7 @@ jobs: - name: Setup Node environment uses: ./.github/actions/setup-node-env with: + cache-mode: restore install-bun: "false" - name: Generate and validate provider scaffold diff --git a/.github/workflows/plugin-npm-release.yml b/.github/workflows/plugin-npm-release.yml index eb4a3ca00b99..c9b07fe23f14 100644 --- a/.github/workflows/plugin-npm-release.yml +++ b/.github/workflows/plugin-npm-release.yml @@ -218,6 +218,7 @@ jobs: - name: Setup Node environment uses: ./.github/actions/setup-node-env with: + cache-mode: restore node-version: ${{ env.NODE_VERSION }} install-bun: "false" @@ -392,6 +393,7 @@ jobs: - name: Setup Node environment uses: ./.github/actions/setup-node-env with: + cache-mode: restore node-version: ${{ env.NODE_VERSION }} install-bun: "false" @@ -1297,6 +1299,7 @@ jobs: if: steps.publication_evidence.outputs.publish_route == 'npm-token-bootstrap' || steps.publication_evidence.outputs.publish_route == 'npm-readback' uses: ./.github/actions/setup-node-env with: + cache-mode: restore node-version: ${{ env.NODE_VERSION }} install-bun: "false" @@ -1313,6 +1316,7 @@ jobs: if: steps.publication_evidence.outputs.publish_route == 'npm-oidc' uses: ./.github/actions/setup-node-env with: + cache-mode: restore node-version: ${{ env.NODE_VERSION }} install-bun: "false" @@ -1514,6 +1518,7 @@ jobs: - name: Setup Node environment uses: ./.github/actions/setup-node-env with: + cache-mode: restore node-version: ${{ env.NODE_VERSION }} install-bun: "false" diff --git a/.github/workflows/plugin-prerelease.yml b/.github/workflows/plugin-prerelease.yml index c95a9919eeeb..64e7f3faec88 100644 --- a/.github/workflows/plugin-prerelease.yml +++ b/.github/workflows/plugin-prerelease.yml @@ -142,6 +142,7 @@ jobs: - name: Setup manifest pnpm uses: ./.github/actions/setup-pnpm-store-cache with: + cache-mode: restore node-version: "24.x" - name: Install manifest dependencies @@ -339,6 +340,7 @@ jobs: - name: Setup Node environment uses: ./.github/actions/setup-node-env with: + cache-mode: restore install-bun: "false" - name: Run plugin prerelease static shard @@ -401,6 +403,7 @@ jobs: - name: Setup Node environment uses: ./.github/actions/setup-node-env with: + cache-mode: restore install-bun: "false" - name: Configure Node test resources @@ -484,6 +487,7 @@ jobs: - name: Setup Node environment uses: ./.github/actions/setup-node-env with: + cache-mode: restore install-bun: "false" - name: Run extension shard @@ -516,6 +520,7 @@ jobs: - name: Setup Node environment uses: ./.github/actions/setup-node-env with: + cache-mode: restore install-bun: "false" - name: Run plugin inspector advisory sweep diff --git a/.github/workflows/qa-live-transports-convex.yml b/.github/workflows/qa-live-transports-convex.yml index ce99431450ee..fdc0dc3c3156 100644 --- a/.github/workflows/qa-live-transports-convex.yml +++ b/.github/workflows/qa-live-transports-convex.yml @@ -274,6 +274,7 @@ jobs: - name: Setup Node environment uses: ./.github/actions/setup-node-env with: + cache-mode: restore node-version: ${{ env.NODE_VERSION }} install-bun: "true" @@ -343,6 +344,7 @@ jobs: - name: Setup Node environment uses: ./.github/actions/setup-node-env with: + cache-mode: restore node-version: ${{ env.NODE_VERSION }} install-bun: "true" @@ -448,6 +450,7 @@ jobs: - name: Setup Node environment uses: ./.github/actions/setup-node-env with: + cache-mode: restore node-version: ${{ env.NODE_VERSION }} install-bun: "true" @@ -512,6 +515,7 @@ jobs: - name: Setup Node environment uses: ./.github/actions/setup-node-env with: + cache-mode: restore node-version: ${{ env.NODE_VERSION }} install-bun: "true" @@ -647,6 +651,7 @@ jobs: - name: Setup Node environment uses: ./.github/actions/setup-node-env with: + cache-mode: restore node-version: ${{ env.NODE_VERSION }} install-bun: "true" @@ -744,6 +749,7 @@ jobs: - name: Setup Node environment uses: ./.github/actions/setup-node-env with: + cache-mode: restore node-version: ${{ env.NODE_VERSION }} install-bun: "true" @@ -832,6 +838,7 @@ jobs: - name: Setup Node environment uses: ./.github/actions/setup-node-env with: + cache-mode: restore node-version: ${{ env.NODE_VERSION }} install-bun: "true" @@ -907,6 +914,7 @@ jobs: - name: Setup Node environment uses: ./.github/actions/setup-node-env with: + cache-mode: restore node-version: ${{ env.NODE_VERSION }} install-bun: "true" diff --git a/.github/workflows/qa-profile-evidence.yml b/.github/workflows/qa-profile-evidence.yml index 307c72ca2c47..ed8783b11924 100644 --- a/.github/workflows/qa-profile-evidence.yml +++ b/.github/workflows/qa-profile-evidence.yml @@ -358,10 +358,10 @@ jobs: - name: Setup Node environment uses: ./.github/actions/setup-node-env with: + cache-mode: off node-version: ${{ env.NODE_VERSION }} install-bun: "false" install-deps: "false" - use-actions-cache: "false" - name: Checkout selected ref env: @@ -520,10 +520,10 @@ jobs: - name: Setup Node environment uses: ./.github/actions/setup-node-env with: + cache-mode: off node-version: ${{ env.NODE_VERSION }} install-bun: "true" install-deps: "false" - use-actions-cache: "false" - name: Checkout selected ref env: @@ -825,10 +825,10 @@ jobs: - name: Setup Node environment uses: ./.github/actions/setup-node-env with: + cache-mode: off node-version: ${{ env.NODE_VERSION }} install-bun: "false" install-deps: "false" - use-actions-cache: "false" - name: Checkout selected ref env: diff --git a/.github/workflows/shared-openclawkit-periphery.yml b/.github/workflows/shared-openclawkit-periphery.yml index e3fc412addce..2b59e1502f89 100644 --- a/.github/workflows/shared-openclawkit-periphery.yml +++ b/.github/workflows/shared-openclawkit-periphery.yml @@ -104,6 +104,7 @@ jobs: - name: Setup Node environment uses: ./.github/actions/setup-node-env with: + cache-mode: restore install-bun: "false" - name: Install iOS scan tooling diff --git a/.github/workflows/test-performance-agent.yml b/.github/workflows/test-performance-agent.yml index 4a146cd4c96d..c80c9aeb76aa 100644 --- a/.github/workflows/test-performance-agent.yml +++ b/.github/workflows/test-performance-agent.yml @@ -110,6 +110,7 @@ jobs: if: steps.gate.outputs.run_agent == 'true' uses: ./.github/actions/setup-node-env with: + cache-mode: restore install-bun: "false" - name: Ensure test performance agent key exists diff --git a/.github/workflows/vitest-cache-warm.yml b/.github/workflows/vitest-cache-warm.yml index bcd4e01e4803..1fccda6fdc5b 100644 --- a/.github/workflows/vitest-cache-warm.yml +++ b/.github/workflows/vitest-cache-warm.yml @@ -1,6 +1,8 @@ name: Vitest Cache Warm on: + push: + branches: [main] workflow_dispatch: repository_dispatch: types: [vitest-cache-warm] @@ -16,27 +18,32 @@ concurrency: jobs: warm: - # Dependency snapshots are serialized before main CI fanout. This workflow - # writes only the transform and compile caches from scheduled/trusted runs. + # Shared CI cache publication is isolated from pull-request execution. + # Trusted release/proof workflows may save only through the same authority contract. if: github.repository == 'openclaw/openclaw' runs-on: ${{ vars.OPENCLAW_CI_RUNNER_BACKEND == 'github' && 'ubuntu-24.04' || 'blacksmith-8vcpu-ubuntu-2404' }} - timeout-minutes: 30 + timeout-minutes: 60 steps: - name: Checkout uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6.0.3 - name: Setup Node environment + id: setup-node-env uses: ./.github/actions/setup-node-env with: + build-all-cache-scope: full + cache-mode: read-write + dependency-cache: "true" install-bun: "false" node-compile-cache: "true" node-compile-cache-scope: "test" - save-actions-cache: "true" - save-node-compile-cache: "true" - save-vitest-fs-cache: "true" - use-actions-cache: "true" vitest-fs-cache: "true" + - name: Warm build cache + env: + NODE_OPTIONS: --max-old-space-size=8192 + run: pnpm build + - name: Select broad cache seed shell: bash run: | @@ -64,3 +71,79 @@ jobs: # (run 31874567859), skipping every cache save for the day. NODE_OPTIONS: --max-old-space-size=8192 run: node --import tsx scripts/ci-run-node-test-shard.mts + + - name: Save Node toolchain cache + if: ${{ steps.setup-node-env.outputs.cache-mode == 'read-write' && runner.os != 'Windows' && runner.environment != 'github-hosted' && steps.setup-node-env.outputs.node-toolchain-populated == 'true' && steps.setup-node-env.outputs.node-toolchain-cache-matched-key != steps.setup-node-env.outputs.node-toolchain-cache-key }} + continue-on-error: true + uses: actions/cache/save@27d5ce7f107fe9357f9df03efb73ab90386fccae # v5 + with: + path: ${{ steps.setup-node-env.outputs.node-toolchain-cache-path }} + key: ${{ steps.setup-node-env.outputs.node-toolchain-cache-key }} + + - name: Save exact dependency cache + if: ${{ steps.setup-node-env.outputs.cache-mode == 'read-write' && steps.setup-node-env.outputs.dependency-cache-key != '' && steps.setup-node-env.outputs.dependency-cache-hit != 'true' }} + continue-on-error: true + uses: actions/cache/save@27d5ce7f107fe9357f9df03efb73ab90386fccae # v5 + with: + path: | + node_modules + ui/node_modules + packages/*/node_modules + examples/*/node_modules + .cache/openclaw-pnpm-store + key: ${{ steps.setup-node-env.outputs.dependency-cache-key }} + + # Prefix restores accumulate old lockfile generations. Keep the trusted + # writer bounded before publishing the coarser fallback store. + - name: Prune pnpm store before save + if: ${{ steps.setup-node-env.outputs.cache-mode == 'read-write' && steps.setup-node-env.outputs.pnpm-store-cache-key != '' && steps.setup-node-env.outputs.pnpm-store-cache-hit != 'true' }} + shell: bash + run: | + du -sh "${{ steps.setup-node-env.outputs.pnpm-store-cache-path }}" || true + pnpm store prune + du -sh "${{ steps.setup-node-env.outputs.pnpm-store-cache-path }}" || true + + - name: Save pnpm store cache + if: ${{ steps.setup-node-env.outputs.cache-mode == 'read-write' && steps.setup-node-env.outputs.pnpm-store-cache-key != '' && steps.setup-node-env.outputs.pnpm-store-cache-hit != 'true' }} + continue-on-error: true + uses: actions/cache/save@27d5ce7f107fe9357f9df03efb73ab90386fccae # v5 + with: + path: ${{ steps.setup-node-env.outputs.pnpm-store-cache-path }} + key: ${{ steps.setup-node-env.outputs.pnpm-store-cache-key }} + + - name: Save Vitest transform cache + if: ${{ steps.setup-node-env.outputs.cache-mode == 'read-write' && steps.setup-node-env.outputs.vitest-cache-key != '' }} + continue-on-error: true + uses: actions/cache/save@27d5ce7f107fe9357f9df03efb73ab90386fccae # v5 + with: + path: /var/tmp/openclaw-vitest-fs-cache + key: ${{ steps.setup-node-env.outputs.vitest-cache-key }} + + - name: Save Node compile cache + if: ${{ steps.setup-node-env.outputs.cache-mode == 'read-write' && steps.setup-node-env.outputs.node-compile-cache-key != '' }} + continue-on-error: true + uses: actions/cache/save@27d5ce7f107fe9357f9df03efb73ab90386fccae # v5 + with: + path: /var/tmp/openclaw-node-compile-cache + key: ${{ steps.setup-node-env.outputs.node-compile-cache-key }} + + - name: Save build-all cache + if: ${{ steps.setup-node-env.outputs.cache-mode == 'read-write' && steps.setup-node-env.outputs.build-all-cache-key != '' }} + continue-on-error: true + uses: actions/cache/save@27d5ce7f107fe9357f9df03efb73ab90386fccae # v5 + with: + path: .artifacts/build-all-cache + key: ${{ steps.setup-node-env.outputs.build-all-cache-key }} + + - name: Save dist build cache + if: ${{ steps.setup-node-env.outputs.cache-mode == 'read-write' }} + continue-on-error: true + uses: actions/cache/save@27d5ce7f107fe9357f9df03efb73ab90386fccae # v5 + with: + path: | + dist/ + dist-runtime/ + packages/*/dist/ + extensions/*/src/host/**/.bundle.hash + extensions/*/src/host/**/*.bundle.js + key: ${{ runner.os }}-dist-build-v3-${{ github.sha }} diff --git a/.github/workflows/windows-testbox-probe.yml b/.github/workflows/windows-testbox-probe.yml index 74ea191bc62a..1073c302e98c 100644 --- a/.github/workflows/windows-testbox-probe.yml +++ b/.github/workflows/windows-testbox-probe.yml @@ -236,6 +236,7 @@ jobs: if: ${{ inputs.run_windows_ci }} uses: ./.github/actions/setup-pnpm-store-cache with: + cache-mode: restore node-version: 22.x - name: Runtime versions diff --git a/.github/workflows/workflow-sanity.yml b/.github/workflows/workflow-sanity.yml index 12a20361cba2..c0db3e7c2c8b 100644 --- a/.github/workflows/workflow-sanity.yml +++ b/.github/workflows/workflow-sanity.yml @@ -202,6 +202,7 @@ jobs: - name: Setup Node environment uses: ./.github/actions/setup-node-env with: + cache-mode: restore install-bun: "false" - name: Check config docs drift statefile diff --git a/scripts/lib/ci-node-test-plan.mts b/scripts/lib/ci-node-test-plan.mts index e976882111f4..ed9f9114c6f6 100644 --- a/scripts/lib/ci-node-test-plan.mts +++ b/scripts/lib/ci-node-test-plan.mts @@ -32,7 +32,7 @@ type NodeTestShardGroup = { env?: Record; }; -export type NodeTestShard = { +type NodeTestShard = { checkName: string; shardName: string; configs: string[]; @@ -44,7 +44,6 @@ export type NodeTestShard = { timeoutMinutes?: number; planConcurrency?: number; predictedSeconds?: number; - saveVitestFsCache?: boolean; }; type NodeTestPlanOptions = { @@ -2120,26 +2119,6 @@ export function createNodeTestShardBundles( return [...unbundled, ...bundled].toSorted(compareFullNodeTestAdmissionOrder); } -/** - * Mark one semantic cache producer without coupling persistence to matrix order. - * The broad core unit graph is shared by most shards; precise changed plans - * fall back to their first (normally only) job. - */ -export function assignVitestFsCacheWriter>( - shards: T[], -): Array { - const preferredIndex = shards.findIndex( - (shard) => - shard.shardName.startsWith("core-unit-fast") || - shard.groups?.some((group) => group.shard_name.startsWith("core-unit-fast")), - ); - const writerIndex = preferredIndex >= 0 ? preferredIndex : shards.length > 0 ? 0 : -1; - return shards.map((shard, index) => ({ - ...shard, - saveVitestFsCache: index === writerIndex, - })); -} - function listAgentSupportTestFiles(): string[] { const owner = agentVitestProjectOwners.support; return listTestFiles(owner.root).filter( diff --git a/test/scripts/ci-node-test-plan.test.ts b/test/scripts/ci-node-test-plan.test.ts index e10698be063a..370cbc47d5ef 100644 --- a/test/scripts/ci-node-test-plan.test.ts +++ b/test/scripts/ci-node-test-plan.test.ts @@ -3,12 +3,10 @@ import { existsSync, globSync, readdirSync } from "node:fs"; import { isAbsolute, join, relative, resolve } from "node:path"; import { describe, expect, it } from "vitest"; import { - assignVitestFsCacheWriter, createNodeTestShardBundles, createNodeTestShards, createVitestCacheWarmGroups, resolvePolicyTestTargets, - type NodeTestShard, } from "../../scripts/lib/ci-node-test-plan.mts"; import { expectNoNodeFsScans } from "../../src/test-utils/fs-scan-assertions.js"; import { listGitTrackedFiles, sortRepoPaths, toRepoPath } from "../../src/test-utils/repo-files.js"; @@ -112,40 +110,6 @@ describe("scripts/lib/ci-node-test-plan.mts", () => { expect(resolvePolicyTestTargets(["docs/web/control-ui.md"])).toEqual([]); }); - it("assigns one semantic Vitest cache writer without changing shard order", () => { - const full = createNodeTestShardBundles({ includeReleaseOnlyPluginShards: false }); - const compact = createNodeTestShardBundles({ - includeReleaseOnlyPluginShards: false, - compactMode: "push", - }); - - const expectWriter = (plan: Array>) => { - const marked = assignVitestFsCacheWriter(plan); - expect(marked.map((shard) => shard.shardName)).toEqual(plan.map((shard) => shard.shardName)); - expect(marked.filter((shard) => shard.saveVitestFsCache)).toHaveLength(1); - expect( - marked.find((shard) => shard.saveVitestFsCache)?.shardName.startsWith("core-unit-fast") || - marked - .find((shard) => shard.saveVitestFsCache) - ?.groups?.some((group) => group.shard_name.startsWith("core-unit-fast")), - ).toBe(true); - }; - expectWriter(full); - expectWriter(compact); - - expect(assignVitestFsCacheWriter([])).toEqual([]); - const changedOnly = { - checkName: "checks-node-changed-only", - configs: ["test/vitest/vitest.unit.config.ts"], - requiresDist: false, - runner: DEFAULT_NODE_TEST_RUNNER, - shardName: "changed-only", - }; - expect(assignVitestFsCacheWriter([changedOnly])).toEqual([ - { ...changedOnly, saveVitestFsCache: true }, - ]); - }); - it("projects cache-warm groups from the owned node test plan", () => { const groups = createVitestCacheWarmGroups(); expect(groups).toHaveLength(10); diff --git a/test/scripts/ci-workflow-guards.test.ts b/test/scripts/ci-workflow-guards.test.ts index a264b7ffe8d9..fe00a8b78e2b 100644 --- a/test/scripts/ci-workflow-guards.test.ts +++ b/test/scripts/ci-workflow-guards.test.ts @@ -3326,9 +3326,7 @@ NODE expect(evaluateWorkflowExpression(setup.with?.["dependency-cache"], context), jobName).toBe( "false", ); - expect(evaluateWorkflowExpression(setup.with?.["use-actions-cache"], context), jobName).toBe( - "true", - ); + expect(setup.with?.["cache-mode"], jobName).toBe("restore"); } }); @@ -3423,6 +3421,130 @@ NODE }); }); + it("keeps setup cache access explicit and isolates every cache write", () => { + const setupActionPaths = [ + ".github/actions/setup-node-env/action.yml", + ".github/actions/setup-pnpm-store-cache/action.yml", + ]; + const legacyInputs = [ + "save-actions-cache", + "save-dependency-cache", + "save-node-compile-cache", + "save-vitest-fs-cache", + "use-actions-cache", + ]; + for (const actionPath of setupActionPaths) { + const action = parse(readFileSync(actionPath, "utf8")); + const steps = action.runs.steps as WorkflowStep[]; + expect(action.inputs["cache-mode"].default, actionPath).toBe("off"); + for (const legacyInput of legacyInputs) { + expect(action.inputs, `${actionPath}: ${legacyInput}`).not.toHaveProperty(legacyInput); + } + expect( + steps.filter( + (step) => + step.uses?.startsWith("actions/cache@") || step.uses?.startsWith("actions/cache/save@"), + ), + actionPath, + ).toEqual([]); + expect( + steps.filter((step) => step.uses?.startsWith("actions/cache/restore@")).length, + actionPath, + ).toBeGreaterThan(0); + const validation = expectDefined( + steps.find((step) => step.run?.includes("off|restore|read-write")), + `${actionPath} cache-mode validation`, + ); + expect(validation.run).toContain("Invalid cache-mode input"); + } + + const callers: Array<{ file: string; mode: unknown; step: WorkflowStep }> = []; + const directCaches: Array<{ file: string; step: WorkflowStep }> = []; + for (const file of [ + ...findYamlFiles(".github/workflows"), + ...findYamlFiles(".github/actions"), + ]) { + const parsed = parse(readFileSync(file, "utf8")); + const stepLists = [ + ...Object.values(parsed?.jobs ?? {}).map( + (job) => (job as { steps?: WorkflowStep[] }).steps ?? [], + ), + (parsed?.runs?.steps ?? []) as WorkflowStep[], + ]; + for (const step of stepLists.flat()) { + if (step.uses?.startsWith("actions/cache")) { + directCaches.push({ file, step }); + } + if ( + step.uses === "./.github/actions/setup-node-env" || + step.uses?.endsWith("/.github/actions/setup-node-env") || + step.uses === "./.github/actions/setup-pnpm-store-cache" || + step.uses?.endsWith("/.github/actions/setup-pnpm-store-cache") + ) { + callers.push({ file, mode: step.with?.["cache-mode"], step }); + } + } + } + expect(callers.length).toBeGreaterThan(0); + for (const caller of callers) { + const staticMode = ["off", "restore", "read-write"].includes(String(caller.mode)); + const conditionalMode = + typeof caller.mode === "string" && + caller.mode.startsWith("${{") && + caller.mode.includes("'restore'") && + (caller.mode.includes("'off'") || caller.mode.includes("'read-write'")); + expect(staticMode || conditionalMode, `${caller.file}: ${caller.step.name}`).toBe(true); + for (const legacyInput of legacyInputs) { + expect(caller.step.with, `${caller.file}: ${legacyInput}`).not.toHaveProperty(legacyInput); + } + } + const writeAuthorizedCallers = callers.filter( + (caller) => + caller.mode === "read-write" || + (typeof caller.mode === "string" && caller.mode.includes("'read-write'")), + ); + expect(writeAuthorizedCallers).toHaveLength(4); + expect(writeAuthorizedCallers).toEqual( + expect.arrayContaining([ + { + file: ".github/workflows/ci-build-artifacts-testbox.yml", + mode: expect.stringContaining("'read-write'"), + step: expect.objectContaining({ name: "Setup Node environment" }), + }, + { + file: ".github/workflows/mantis-telegram-desktop-proof.yml", + mode: "read-write", + step: expect.objectContaining({ name: "Setup Node environment" }), + }, + { + file: ".github/workflows/openclaw-npm-release.yml", + mode: "read-write", + step: expect.objectContaining({ name: "Setup Node environment" }), + }, + { + file: ".github/workflows/vitest-cache-warm.yml", + mode: "read-write", + step: expect.objectContaining({ name: "Setup Node environment" }), + }, + ]), + ); + + const nodeCachePathPattern = + /(?:^|\n)\s*(?:\.artifacts\/build-all-cache|dist\/|dist-runtime\/|packages\/\*\/dist\/|extensions\/\*\/dist\/|~\/\.cache\/ms-playwright|~\/\.local\/share\/pnpm|~\/\.cache\/pnpm|node_modules)(?:\n|$)/u; + for (const { file, step } of directCaches) { + if (step.uses?.startsWith("actions/cache/save@")) { + expect(String(step.if), `${file}: ${step.name}`).toContain( + ".outputs.cache-mode == 'read-write'", + ); + } + if (step.uses?.startsWith("actions/cache@")) { + expect(nodeCachePathPattern.test(String(step.with?.path)), `${file}: ${step.name}`).toBe( + false, + ); + } + } + }); + it("owns one exact immutable semantic dependency cache", () => { const actionSource = readFileSync(".github/actions/setup-node-env/action.yml", "utf8"); const ciSource = readFileSync(".github/workflows/ci.yml", "utf8"); @@ -3442,21 +3564,25 @@ NODE const setupPnpm = step("Setup pnpm"); const install = step("Install dependencies"); const installScript = expectDefined(install.run, "Install dependencies script"); - const save = step("Save exact dependency cache"); const cachePaths = "node_modules\nui/node_modules\npackages/*/node_modules\nexamples/*/node_modules\n.cache/openclaw-pnpm-store\n"; + expect(action.inputs["cache-mode"].default).toBe("off"); expect(action.inputs["dependency-cache"].default).toBe("false"); - expect(action.inputs["save-dependency-cache"].default).toBe("false"); + expect(action.inputs).not.toHaveProperty("save-dependency-cache"); + expect(action.inputs).not.toHaveProperty("save-actions-cache"); + expect(action.inputs).not.toHaveProperty("use-actions-cache"); expect(action.inputs).not.toHaveProperty("sticky-disk"); expect(action.inputs).not.toHaveProperty("save-sticky-disk"); expect(actionSource).not.toContain("useblacksmith/stickydisk"); - expect(configureStore.if).toBe("inputs.dependency-cache == 'true'"); + expect(configureStore.if).toBe( + "inputs.cache-mode != 'off' && inputs.dependency-cache == 'true'", + ); expect(configureStore.run).toContain( 'echo "PNPM_CONFIG_STORE_DIR=$GITHUB_WORKSPACE/.cache/openclaw-pnpm-store"', ); - expect(resolve.if).toBe("inputs.dependency-cache == 'true'"); + expect(resolve.if).toBe("inputs.cache-mode != 'off' && inputs.dependency-cache == 'true'"); expect(resolve.run).toContain('node "$GITHUB_ACTION_PATH/dependency-fingerprint.mjs"'); expect(resolve.run).toContain("${GITHUB_REPOSITORY:?}-node-deps-v2"); expect(resolve.run).toContain("${RUNNER_OS:?}-arch-${RUNNER_ARCH:?}"); @@ -3471,7 +3597,7 @@ NODE } expect(actionSteps.indexOf(prepare)).toBeLessThan(actionSteps.indexOf(restore)); expect(restore).toMatchObject({ - if: "inputs.dependency-cache == 'true'", + if: "inputs.cache-mode != 'off' && inputs.dependency-cache == 'true'", uses: CACHE_V5, with: { key: "${{ steps.dependency-cache-key.outputs.key }}", path: cachePaths }, }); @@ -3485,12 +3611,11 @@ NODE ); expect(actionSteps.indexOf(restore)).toBeLessThan(actionSteps.indexOf(prepareFallback)); expect(actionSteps.indexOf(prepareFallback)).toBeLessThan(actionSteps.indexOf(setupPnpm)); - expect(setupPnpm.with?.["use-actions-cache"]).toContain( + expect(setupPnpm.with?.["cache-mode"]).toContain( "steps.dependency-cache.outputs.cache-hit != 'true'", ); - expect(setupPnpm.with?.["use-actions-cache"]).toContain( - "inputs.dependency-cache != 'true' && inputs.use-actions-cache == 'true'", - ); + expect(setupPnpm.with?.["cache-mode"]).toContain("inputs.cache-mode != 'off'"); + expect(setupPnpm.with?.["cache-mode"]).toContain("'restore' || 'off'"); expect(actionSteps.indexOf(restore)).toBeLessThan(actionSteps.indexOf(setupPnpm)); expect(installScript).toContain("install_args+=(--package-import-method=hardlink)"); @@ -3506,16 +3631,13 @@ NODE expect(installScript).toContain( 'echo "pnpm_config_verify_deps_before_run=false" >> "$GITHUB_ENV"', ); - expect(save).toMatchObject({ - uses: "actions/cache/save@27d5ce7f107fe9357f9df03efb73ab90386fccae", - with: { key: "${{ steps.dependency-cache-key.outputs.key }}", path: cachePaths }, - }); - expect(save.if).toContain("inputs.save-dependency-cache == 'true'"); - expect(save.if).toContain("steps.dependency-cache.outputs.cache-hit != 'true'"); - expect((save as WorkflowStep & { "continue-on-error"?: boolean })["continue-on-error"]).toBe( - true, - ); - expect(actionSteps.indexOf(save)).toBe(actionSteps.indexOf(install) + 1); + expect( + actionSteps.some( + (candidate) => + candidate.uses?.startsWith("actions/cache@") || + candidate.uses?.startsWith("actions/cache/save@"), + ), + ).toBe(false); const dependencySetups = Object.entries(workflow.jobs).flatMap(([jobName, job]) => ((job as { steps?: WorkflowStep[] }).steps ?? []).flatMap((candidate) => @@ -3525,24 +3647,19 @@ NODE : [], ), ); - const writers = dependencySetups.filter( - ({ step: candidate }) => candidate.with?.["save-dependency-cache"] === "true", - ); - expect(writers).toHaveLength(1); - expect(writers[0]?.jobName).toBe("preflight"); - expect(writers[0]?.step).toMatchObject({ + const preflightRestore = dependencySetups.find(({ jobName }) => jobName === "preflight"); + expect(preflightRestore?.step).toMatchObject({ if: expect.stringContaining("steps.manifest.outputs.run_node == 'true'"), with: { + "cache-mode": "restore", "dependency-cache": "true", - "save-actions-cache": "true", - "save-dependency-cache": "true", - "use-actions-cache": "true", + "install-bun": "false", }, }); - expect(writers[0]?.step.if).toContain("github.ref == 'refs/heads/main'"); - expect(writers[0]?.step.if).toContain("github.event_name == 'pull_request'"); - expect(writers[0]?.step.if).toContain("vars.OPENCLAW_CI_RUNNER_BACKEND != 'github'"); - expect(writers[0]?.step.if).toContain("vars.OPENCLAW_CI_RUNNER_BACKEND != 'hybrid'"); + expect(preflightRestore?.step.if).toContain("github.ref == 'refs/heads/main'"); + expect(preflightRestore?.step.if).toContain("github.event_name == 'pull_request'"); + expect(preflightRestore?.step.if).toContain("vars.OPENCLAW_CI_RUNNER_BACKEND != 'github'"); + expect(preflightRestore?.step.if).toContain("vars.OPENCLAW_CI_RUNNER_BACKEND != 'hybrid'"); expect(workflow.jobs["pnpm-store-warmup"].if).toContain( "vars.OPENCLAW_CI_RUNNER_BACKEND == 'github'", ); @@ -3575,7 +3692,7 @@ NODE expect(Array.isArray(needs) ? needs : [needs], jobName).toContain("preflight"); expect(consumer.with, jobName).not.toHaveProperty("save-dependency-cache"); expect(consumer.with?.["dependency-cache"], jobName).toContain("'true' || 'false'"); - expect(consumer.with?.["use-actions-cache"], jobName).toContain("'false' || 'true'"); + expect(consumer.with?.["cache-mode"], jobName).toBe("restore"); expect(consumer.with?.["dependency-cache"], jobName).toContain( "vars.OPENCLAW_CI_RUNNER_BACKEND", ); @@ -3590,16 +3707,6 @@ NODE }), `${jobName} ${runnerBackend} dependency cache`, ).toBe("false"); - expect( - evaluateWorkflowExpression(consumer.with?.["use-actions-cache"], { - eventName: "push", - matrix: { node_version: "24.x" }, - repository: "openclaw/openclaw", - runnerBackend, - runAttempt: 1, - }), - `${jobName} ${runnerBackend} Actions cache`, - ).toBe("true"); } } for (const { jobName, step: setup } of Object.entries(workflow.jobs).flatMap(([jobName, job]) => @@ -3609,7 +3716,21 @@ NODE )) { expect(setup.with, jobName).not.toHaveProperty("sticky-disk"); expect(setup.with, jobName).not.toHaveProperty("save-sticky-disk"); + expect(["off", "restore", "read-write"], jobName).toContain(setup.with?.["cache-mode"]); } + + const warmer = parse(readFileSync(".github/workflows/vitest-cache-warm.yml", "utf8")); + const dependencySave = warmer.jobs.warm.steps.find( + (candidate: WorkflowStep) => candidate.name === "Save exact dependency cache", + ); + expect(dependencySave).toMatchObject({ + uses: "actions/cache/save@27d5ce7f107fe9357f9df03efb73ab90386fccae", + with: { + key: "${{ steps.setup-node-env.outputs.dependency-cache-key }}", + path: cachePaths, + }, + }); + expect(dependencySave.if).toContain("steps.setup-node-env.outputs.cache-mode == 'read-write'"); }); it.skipIf(process.platform === "win32")( @@ -3800,13 +3921,13 @@ server.listen(0, "127.0.0.1", () => writeFileSync(readyPath, String(server.addre (step: WorkflowStep) => step.name === "Install dependencies", ); const cacheStep = action.runs.steps.find( - (step: WorkflowStep) => step.name === "Restore and save build-all cache", + (step: WorkflowStep) => step.name === "Restore build-all cache", ); expect(action.inputs["build-all-cache-scope"].default).toBe(""); expect(cacheStep).toMatchObject({ - if: "inputs.build-all-cache-scope != ''", - uses: "actions/cache@27d5ce7f107fe9357f9df03efb73ab90386fccae", + if: "inputs.cache-mode != 'off' && inputs.build-all-cache-scope != ''", + uses: CACHE_V5, with: { path: ".artifacts/build-all-cache" }, }); expect(cacheStep.with.key).toContain("build-all-v1-${{ inputs.build-all-cache-scope }}"); @@ -3817,6 +3938,18 @@ server.listen(0, "127.0.0.1", () => writeFileSync(readyPath, String(server.addre expect(action.runs.steps.indexOf(installStep)).toBeLessThan( action.runs.steps.indexOf(cacheStep), ); + const warmer = parse(readFileSync(".github/workflows/vitest-cache-warm.yml", "utf8")); + const buildSave = warmer.jobs.warm.steps.find( + (step: WorkflowStep) => step.name === "Save build-all cache", + ); + expect(buildSave).toMatchObject({ + uses: "actions/cache/save@27d5ce7f107fe9357f9df03efb73ab90386fccae", + with: { + key: "${{ steps.setup-node-env.outputs.build-all-cache-key }}", + path: ".artifacts/build-all-cache", + }, + }); + expect(buildSave.if).toContain("steps.setup-node-env.outputs.cache-mode == 'read-write'"); const privateQaWorkflows = [ ".github/workflows/mantis-discord-smoke.yml", @@ -4058,9 +4191,6 @@ server.listen(0, "127.0.0.1", () => writeFileSync(readyPath, String(server.addre (step: WorkflowStep) => step.name === "Setup Node environment", ); const action = parse(readFileSync(".github/actions/setup-node-env/action.yml", "utf8")); - const writerStep = action.runs.steps.find( - (step: WorkflowStep) => step.name === "Restore and save Vitest transform cache", - ); const readerStep = action.runs.steps.find( (step: WorkflowStep) => step.name === "Restore Vitest transform cache", ); @@ -4070,9 +4200,6 @@ server.listen(0, "127.0.0.1", () => writeFileSync(readyPath, String(server.addre const compileEpochStep = action.runs.steps.find( (step: WorkflowStep) => step.name === "Select Node compile cache epoch", ); - const compileWriterStep = action.runs.steps.find( - (step: WorkflowStep) => step.name === "Restore and save Node compile cache", - ); const compileReaderStep = action.runs.steps.find( (step: WorkflowStep) => step.name === "Restore Node compile cache", ); @@ -4098,10 +4225,9 @@ server.listen(0, "127.0.0.1", () => writeFileSync(readyPath, String(server.addre "${{ (vars.OPENCLAW_CI_RUNNER_BACKEND == 'github' || vars.OPENCLAW_CI_RUNNER_BACKEND == 'hybrid') && (matrix.task == 'bundled-protocol' || matrix.task == 'contracts-plugins-ci-routing' || matrix.task == 'ci-routing' || matrix.task == 'bun-launcher') && 'true' || 'false' }}"; expect(setupNodeStep.with).toMatchObject({ + "cache-mode": "restore", "node-compile-cache": "true", "node-compile-cache-scope": "test", - "save-vitest-fs-cache": - "${{ (vars.OPENCLAW_CI_RUNNER_BACKEND == 'github' || vars.OPENCLAW_CI_RUNNER_BACKEND == 'hybrid') && matrix.save_vitest_fs_cache && 'true' || 'false' }}", "vitest-fs-cache": "true", }); expect(setupNodeStep.with).not.toHaveProperty("save-node-compile-cache"); @@ -4109,10 +4235,10 @@ server.listen(0, "127.0.0.1", () => writeFileSync(readyPath, String(server.addre expect(action.inputs).not.toHaveProperty("runtime-cache-sticky-disk"); expect(action.inputs["vitest-fs-cache"].default).toBe("false"); expect(action.inputs["restore-test-caches"].default).toBe("false"); - expect(action.inputs["save-vitest-fs-cache"].default).toBe("false"); + expect(action.inputs).not.toHaveProperty("save-vitest-fs-cache"); expect(action.inputs["node-compile-cache"].default).toBe("false"); expect(action.inputs["node-compile-cache-scope"].default).toBe("test"); - expect(action.inputs["save-node-compile-cache"].default).toBe("false"); + expect(action.inputs).not.toHaveProperty("save-node-compile-cache"); expect( action.runs.steps.some((step: WorkflowStep) => step.name?.includes("transform cache sticky disk"), @@ -4123,25 +4249,15 @@ server.listen(0, "127.0.0.1", () => writeFileSync(readyPath, String(server.addre step.name?.includes("compile cache sticky disk"), ), ).toBe(false); - expect(writerStep.uses).toBe("actions/cache@27d5ce7f107fe9357f9df03efb73ab90386fccae"); - expect(writerStep.if).toContain("inputs.save-vitest-fs-cache == 'true'"); - expect(writerStep.if).toContain("runner.os != 'Windows'"); - expect(writerStep.if).not.toMatch(/runner\.(?:environment|labels|name)/u); - expect(writerStep.with.key).toContain("vitest-fs-v3-protected-"); - expect(writerStep.with.key).toContain("github.run_id"); - expect(writerStep.with.key).toContain("github.run_attempt"); - expect(writerStep.with.key).not.toContain("pull_request"); - expect(writerStep.with["restore-keys"]).toContain("**/tsconfig*.json"); - expect(writerStep.with.key).toContain("src/state/*.sql"); - expect(writerStep.with["restore-keys"]).toContain("src/state/*.sql"); - expect(writerStep.with.key).toContain("!**/node_modules/**"); - expect(writerStep.with["restore-keys"]).toContain("!**/node_modules/**"); expect(readerStep.uses).toBe(CACHE_V5); + expect(readerStep.if).toContain("inputs.cache-mode != 'off'"); expect(readerStep.if).toContain("inputs.restore-test-caches == 'true'"); - expect(readerStep.if).toContain("inputs.save-vitest-fs-cache != 'true'"); expect(readerStep.if).toContain("runner.os != 'Windows'"); expect(readerStep.if).not.toMatch(/runner\.(?:environment|labels|name)/u); - expect(readerStep.with["restore-keys"]).toBe(writerStep.with["restore-keys"]); + expect(readerStep.with.key).toContain("vitest-fs-v3-protected-"); + expect(readerStep.with.key).toContain("github.run_id"); + expect(readerStep.with.key).toContain("github.run_attempt"); + expect(readerStep.with["restore-keys"]).toContain("**/tsconfig*.json"); expect(readerStep.with.key).toContain("!**/node_modules/**"); expect(readerStep.with.key).toContain("src/state/*.sql"); expect(configureStep.env.CACHE_GENERATION).toContain("!**/node_modules/**"); @@ -4150,32 +4266,27 @@ server.listen(0, "127.0.0.1", () => writeFileSync(readyPath, String(server.addre expect(configureStep.run).toContain("OPENCLAW_VITEST_FS_MODULE_CACHE_PATH=$cache_root"); expect(configureStep.run).toContain(".openclaw-transform-generation"); expect(configureStep.run).not.toContain("protected Vitest transform seed"); - expect(configureStep.env.CACHE_WRITER).toBe( - "${{ inputs.save-vitest-fs-cache == 'true' && '1' || '0' }}", - ); + expect(configureStep.env.CACHE_WRITER).toBe("0"); expect(configureStep.run).toContain("OPENCLAW_VITEST_FS_MODULE_CACHE_WRITER="); expect(compileEpochStep.run).toContain('if [ "$CACHE_SCOPE" = "build" ]'); expect(compileEpochStep.run).toContain("date -u +%Y%m%d"); expect(compileEpochStep.run).toContain("GITHUB_RUN_ID"); - expect(compileWriterStep.with.key).toContain( + expect(compileReaderStep.with.key).toContain( "node-compile-v3-${{ inputs.node-compile-cache-scope }}-protected-", ); - expect(compileWriterStep.with.key).toContain("steps.node-compile-cache-epoch.outputs.value"); - expect(compileWriterStep.with.key).not.toContain("pull_request"); + expect(compileReaderStep.with.key).toContain("steps.node-compile-cache-epoch.outputs.value"); + expect(compileReaderStep.with.key).not.toContain("pull_request"); expect(compileEpochStep.if).toContain("inputs.restore-test-caches == 'true'"); + expect(compileReaderStep.if).toContain("inputs.cache-mode != 'off'"); expect(compileReaderStep.if).toContain("inputs.restore-test-caches == 'true'"); - expect(compileReaderStep.with["restore-keys"]).toBe(compileWriterStep.with["restore-keys"]); expect(compileConfigureStep.if).toContain("inputs.restore-test-caches == 'true'"); expect(compileConfigureStep.run).toContain("NODE_COMPILE_CACHE=$cache_root"); expect(compileConfigureStep.run).toContain("NODE_COMPILE_CACHE_PORTABLE=1"); - expect(compileConfigureStep.env.CACHE_WRITER).toBe( - "${{ inputs.save-node-compile-cache == 'true' && '1' || '0' }}", - ); + expect(compileConfigureStep.run).toContain("OPENCLAW_NODE_COMPILE_CACHE_WRITER=0"); expect(buildSetupNodeStep.with).toMatchObject({ + "cache-mode": "restore", "node-compile-cache": "true", "node-compile-cache-scope": "build", - "save-node-compile-cache": - "${{ github.event_name == 'push' && github.ref == 'refs/heads/main' && 'true' || 'false' }}", }); expect(buildSetupNodeStep.with["node-compile-cache-scope"]).not.toBe( setupNodeStep.with["node-compile-cache-scope"], @@ -4281,15 +4392,14 @@ server.listen(0, "127.0.0.1", () => writeFileSync(readyPath, String(server.addre const warmStep = warmer.jobs.warm.steps.find( (step: WorkflowStep) => step.name === "Warm transform and compile caches", ); - const maintainStoreStep = warmer.jobs.warm.steps.find( - (step: WorkflowStep) => step.name === "Maintain dependency store budget", - ); + const warmerSteps = warmer.jobs.warm.steps as WorkflowStep[]; expect(warmer.concurrency["cancel-in-progress"]).toBe(false); expect(warmer.concurrency.group).toBe("vitest-cache-warm"); // hosted-mode cache recovery needs a maintainer-operated fallback when the // scheduled seed is missing or stale. expect(warmer.on).toHaveProperty("workflow_dispatch"); + expect(warmer.on.push.branches).toEqual(["main"]); expect(warmer.on.repository_dispatch.types).toEqual(["vitest-cache-warm"]); expect(warmer.jobs.warm.if).toContain("github.repository == 'openclaw/openclaw'"); expect(warmer.jobs.warm["runs-on"]).toBe( @@ -4307,19 +4417,45 @@ server.listen(0, "127.0.0.1", () => writeFileSync(readyPath, String(server.addre expect(warmerSource).not.toContain("OPENCLAW_NODE_TEST_CONFIGS_JSON"); expect(warmerSource).toContain('"OPENCLAW_NODE_TEST_PLAN_CONCURRENCY=1"'); expect(warmerSetup.with).toMatchObject({ + "build-all-cache-scope": "full", + "cache-mode": "read-write", + "dependency-cache": "true", "node-compile-cache-scope": "test", - "save-actions-cache": "true", - "save-node-compile-cache": "true", - "save-vitest-fs-cache": "true", - "use-actions-cache": "true", + "node-compile-cache": "true", + "vitest-fs-cache": "true", }); - expect(warmerSetup.with).not.toHaveProperty("dependency-cache"); + for (const legacyInput of [ + "save-actions-cache", + "save-dependency-cache", + "save-node-compile-cache", + "save-vitest-fs-cache", + "use-actions-cache", + ]) { + expect(warmerSetup.with).not.toHaveProperty(legacyInput); + } + const saveSteps = warmerSteps.filter((step) => step.uses?.startsWith("actions/cache/save@")); + expect(saveSteps.map((step) => step.name)).toEqual([ + "Save Node toolchain cache", + "Save exact dependency cache", + "Save pnpm store cache", + "Save Vitest transform cache", + "Save Node compile cache", + "Save build-all cache", + "Save dist build cache", + ]); + for (const saveStep of saveSteps) { + expect(saveStep.if, saveStep.name).toContain( + "steps.setup-node-env.outputs.cache-mode == 'read-write'", + ); + } + expect(warmerSteps.indexOf(warmStep)).toBeLessThan( + warmerSteps.findIndex((step) => step.name === "Save Vitest transform cache"), + ); // No close-time cleanup workflow is needed; Actions cache LRU/TTL expires // old hosted-writer and warmer generations. expect(existsSync(".github/workflows/pr-cache-cleanup.yml")).toBe(false); expect(seedStep.if).toBeUndefined(); expect(warmStep.if).toBeUndefined(); - expect(maintainStoreStep).toBeUndefined(); }); it("uses bundled Node shards and telemetry-backed runner sizes", () => { @@ -4429,7 +4565,7 @@ server.listen(0, "127.0.0.1", () => writeFileSync(readyPath, String(server.addre expect(hostedLintCache.if).toBe( "matrix.task == 'lint' && (vars.OPENCLAW_CI_RUNNER_BACKEND == 'github' || vars.OPENCLAW_CI_RUNNER_BACKEND == 'hybrid' || (github.event_name == 'pull_request' && github.event.pull_request.head.repo.full_name != github.repository))", ); - expect(hostedLintCache.uses).toBe("actions/cache@27d5ce7f107fe9357f9df03efb73ab90386fccae"); + expect(hostedLintCache.uses).toBe(CACHE_V5); expect(hostedLintCache.with).toEqual(boundaryCache.with); const fingerprintReference = "${{ steps.extension-boundary-inputs.outputs.fingerprint }}"; expect(boundaryCache.with.key).toBe( @@ -5269,8 +5405,8 @@ server.listen(0, "127.0.0.1", () => writeFileSync(readyPath, String(server.addre (step: WorkflowStep) => step.name === "Setup Node environment", ); expect(macosNodeSetup.with).toMatchObject({ + "cache-mode": "restore", "install-bun": "false", - "save-actions-cache": "true", }); }); @@ -6404,7 +6540,7 @@ printf '%s\n' "\${CURL_SUCCESS_IP:-203.0.113.7}" ); expect(uiBrowserCache).toMatchObject({ if: "needs.preflight.outputs.compatibility_target != 'true'", - uses: "actions/cache@27d5ce7f107fe9357f9df03efb73ab90386fccae", + uses: CACHE_V5, with: { key: "${{ runner.os }}-playwright-chromium-" + playwrightVersion, path: "~/.cache/ms-playwright", @@ -6485,12 +6621,11 @@ printf '%s\n' "\${CURL_SUCCESS_IP:-203.0.113.7}" ); expect(uiE2eSetup.uses).toBe("./.github/actions/setup-node-env"); const expectedSharedUiE2eSetup = { + "cache-mode": "restore", "node-version": "24.x", "install-bun": "false", "dependency-cache": "${{ (vars.OPENCLAW_CI_RUNNER_BACKEND == 'github' || vars.OPENCLAW_CI_RUNNER_BACKEND == 'hybrid' || github.event_name == 'workflow_dispatch' || (github.event_name == 'pull_request' && github.run_attempt > 1)) && 'false' || (github.repository == 'openclaw/openclaw' && (github.event_name != 'pull_request' || github.event.pull_request.head.repo.full_name == 'openclaw/openclaw') && 'true' || 'false') }}", - "use-actions-cache": - "${{ (vars.OPENCLAW_CI_RUNNER_BACKEND == 'github' || vars.OPENCLAW_CI_RUNNER_BACKEND == 'hybrid' || github.event_name == 'workflow_dispatch' || (github.event_name == 'pull_request' && github.run_attempt > 1)) && 'true' || (github.repository == 'openclaw/openclaw' && (github.event_name != 'pull_request' || github.event.pull_request.head.repo.full_name == 'openclaw/openclaw') && 'false' || 'true') }}", } as const; const expectedUiE2eSetup = { ...expectedSharedUiE2eSetup, @@ -6540,7 +6675,7 @@ printf '%s\n' "\${CURL_SUCCESS_IP:-203.0.113.7}" repository: "openclaw/openclaw", runAttempt: 1, }, - expected: { blacksmith: true, dependencyCache: "true", useActionsCache: "false" }, + expected: { blacksmith: true, dependencyCache: "true" }, }, { name: "same-repo pull request with GitHub backend", @@ -6551,7 +6686,7 @@ printf '%s\n' "\${CURL_SUCCESS_IP:-203.0.113.7}" runnerBackend: "github", runAttempt: 1, }, - expected: { blacksmith: false, dependencyCache: "false", useActionsCache: "true" }, + expected: { blacksmith: false, dependencyCache: "false" }, }, { name: "same-repo pull request with hybrid backend", @@ -6562,7 +6697,7 @@ printf '%s\n' "\${CURL_SUCCESS_IP:-203.0.113.7}" runnerBackend: "hybrid", runAttempt: 1, }, - expected: { blacksmith: false, dependencyCache: "false", useActionsCache: "true" }, + expected: { blacksmith: false, dependencyCache: "false" }, }, { name: "same-repo pull request retry", @@ -6572,7 +6707,7 @@ printf '%s\n' "\${CURL_SUCCESS_IP:-203.0.113.7}" repository: "openclaw/openclaw", runAttempt: 2, }, - expected: { blacksmith: false, dependencyCache: "false", useActionsCache: "true" }, + expected: { blacksmith: false, dependencyCache: "false" }, }, { // Runner routing follows contributor trust; the exact dependency cache @@ -6585,7 +6720,7 @@ printf '%s\n' "\${CURL_SUCCESS_IP:-203.0.113.7}" repository: "openclaw/openclaw", runAttempt: 1, }, - expected: { blacksmith: true, dependencyCache: "false", useActionsCache: "true" }, + expected: { blacksmith: true, dependencyCache: "false" }, }, { name: "fork pull request from unknown author", @@ -6596,7 +6731,7 @@ printf '%s\n' "\${CURL_SUCCESS_IP:-203.0.113.7}" repository: "openclaw/openclaw", runAttempt: 1, }, - expected: { blacksmith: false, dependencyCache: "false", useActionsCache: "true" }, + expected: { blacksmith: false, dependencyCache: "false" }, }, { name: "workflow dispatch", @@ -6605,7 +6740,7 @@ printf '%s\n' "\${CURL_SUCCESS_IP:-203.0.113.7}" repository: "openclaw/openclaw", runAttempt: 1, }, - expected: { blacksmith: false, dependencyCache: "false", useActionsCache: "true" }, + expected: { blacksmith: false, dependencyCache: "false" }, }, { name: "canonical push retry", @@ -6614,7 +6749,7 @@ printf '%s\n' "\${CURL_SUCCESS_IP:-203.0.113.7}" repository: "openclaw/openclaw", runAttempt: 2, }, - expected: { blacksmith: true, dependencyCache: "true", useActionsCache: "false" }, + expected: { blacksmith: true, dependencyCache: "true" }, }, ] as const; for (const { @@ -6640,10 +6775,7 @@ printf '%s\n' "\${CURL_SUCCESS_IP:-203.0.113.7}" evaluateWorkflowExpression(setup.with?.["dependency-cache"], context), assertionName, ).toBe(expected.dependencyCache); - expect( - evaluateWorkflowExpression(setup.with?.["use-actions-cache"], context), - assertionName, - ).toBe(expected.useActionsCache); + expect(setup.with?.["cache-mode"], assertionName).toBe("restore"); } } @@ -6921,7 +7053,7 @@ printf '%s\n' "\${CURL_SUCCESS_IP:-203.0.113.7}" expect(workflow.jobs["ci-gate"].needs).toContain("sqlite-session-lifecycle"); }); - it("restores the dist build cache before building and saves only cache misses", () => { + it("restores dist in PR CI and saves it only from the trusted warmer", () => { const workflow = readCiWorkflow(); const buildArtifactSteps = workflow.jobs["build-artifacts"].steps; const stepNames = buildArtifactSteps.map((step: WorkflowStep) => step.name); @@ -6931,8 +7063,11 @@ printf '%s\n' "\${CURL_SUCCESS_IP:-203.0.113.7}" const buildDistStep = buildArtifactSteps.find( (step: WorkflowStep) => step.name === "Build dist", ); - const saveStep = buildArtifactSteps.find( - (step: WorkflowStep) => step.name === "Save dist build cache", + const warmer = parse(readFileSync(".github/workflows/vitest-cache-warm.yml", "utf8")); + const warmerSteps = warmer.jobs.warm.steps as WorkflowStep[]; + const saveStep = expectDefined( + warmerSteps.find((step) => step.name === "Save dist build cache"), + "trusted dist cache save", ); expect(stepNames.indexOf("Restore dist build cache")).toBeLessThan( @@ -6941,18 +7076,16 @@ printf '%s\n' "\${CURL_SUCCESS_IP:-203.0.113.7}" expect(stepNames.indexOf("Build dist")).toBeLessThan( stepNames.indexOf("Pack built runtime artifacts"), ); - expect(stepNames.indexOf("Run built artifact checks")).toBeLessThan( - stepNames.indexOf("Save dist build cache"), - ); + expect(stepNames).not.toContain("Save dist build cache"); expect(restoreStep.uses).toBe(CACHE_V5); expect(buildDistStep.if).toBe("steps.dist_build_cache.outputs.cache-hit != 'true'"); expect(saveStep.uses).toBe("actions/cache/save@27d5ce7f107fe9357f9df03efb73ab90386fccae"); - expect(saveStep.if).toBe("steps.dist_build_cache.outputs.cache-hit != 'true'"); - expect(saveStep.with.key).toBe("${{ steps.dist_build_cache.outputs.cache-primary-key }}"); + expect(saveStep.if).toContain("steps.setup-node-env.outputs.cache-mode == 'read-write'"); + expect(saveStep.with?.key).toBe("${{ runner.os }}-dist-build-v3-${{ github.sha }}"); expect(restoreStep.with.path).toContain("dist/"); expect(restoreStep.with.path).toContain("dist-runtime/"); expect(restoreStep.with.path).toContain("packages/*/dist/"); - expect(saveStep.with.path).toContain("packages/*/dist/"); + expect(saveStep.with?.path).toContain("packages/*/dist/"); expect(restoreStep.with.key).toContain("dist-build-v3-"); expect( buildArtifactSteps.find((step: WorkflowStep) => step.name === "Pack built runtime artifacts") @@ -6960,6 +7093,9 @@ printf '%s\n' "\${CURL_SUCCESS_IP:-203.0.113.7}" ).toContain("packages/*/dist"); expect(restoreStep.with.path).toContain("extensions/*/src/host/**/.bundle.hash"); expect(restoreStep.with.path).toContain("extensions/*/src/host/**/*.bundle.js"); + expect(warmerSteps.indexOf(saveStep)).toBeGreaterThan( + warmerSteps.findIndex((step) => step.name === "Warm build cache"), + ); expect(buildArtifactSteps.map((step: WorkflowStep) => step.name)).not.toContain( "Cache dist build", ); @@ -6971,6 +7107,10 @@ printf '%s\n' "\${CURL_SUCCESS_IP:-203.0.113.7}" const resolveSeedsStep = steps.find( (step: WorkflowStep) => step.name === "Resolve release dist cache seeds", ); + const setupStep = expectDefined( + steps.find((step: WorkflowStep) => step.name === "Setup Node environment"), + "Testbox Node setup", + ); const restoreStep = steps.find( (step: WorkflowStep) => step.name === "Restore dist build cache", ); @@ -6983,6 +7123,8 @@ printf '%s\n' "\${CURL_SUCCESS_IP:-203.0.113.7}" expect(verifyStep.run).toContain("test -f packages/ai/dist/internal/runtime.mjs"); expect(saveStep.with.path).toContain("packages/*/dist/"); expect(saveStep.with.key).toContain("dist-build-v2-"); + expect(setupStep.with["cache-mode"]).toContain("'read-write'"); + expect(saveStep.if).toContain("steps.setup-node-env.outputs.cache-mode == 'read-write'"); }); it("keeps the full built TUI PTY suite out of the artifact canary gate", () => { @@ -7758,7 +7900,7 @@ printf '%s\n' "\${CURL_SUCCESS_IP:-203.0.113.7}" false, ); expect(setupStep.with?.["install-deps"]).toBe("false"); - expect(setupStep.with?.["use-actions-cache"]).toBe("false"); + expect(setupStep.with?.["cache-mode"]).toBe("off"); expect(selectedCheckout).toMatchObject({ env: { EXPECTED_SHA: "${{ needs.validate_selected_ref.outputs.selected_revision }}", diff --git a/test/scripts/mantis-telegram-desktop-proof-workflow.test.ts b/test/scripts/mantis-telegram-desktop-proof-workflow.test.ts index 5358219c2a9e..0389ba00a717 100644 --- a/test/scripts/mantis-telegram-desktop-proof-workflow.test.ts +++ b/test/scripts/mantis-telegram-desktop-proof-workflow.test.ts @@ -685,6 +685,7 @@ describe("Mantis Telegram Desktop proof workflow", () => { const workflow = parse(readFileSync(WORKFLOW, "utf8")) as Workflow; const steps = workflow.jobs?.run_telegram_desktop_proof?.steps ?? []; const create = workflowStep("Create exact proof worktrees"); + const setup = workflowStep("Setup Node environment"); const restore = workflowStep("Restore exact baseline build"); const baseline = workflowStep("Prepare baseline proof build"); const save = workflowStep("Save exact baseline build"); @@ -708,6 +709,8 @@ describe("Mantis Telegram Desktop proof workflow", () => { expect(createRun).toContain('git worktree add --detach "$baseline_root" "$BASELINE_SHA"'); expect(createRun).toContain('git worktree add --detach "$candidate_root" "$CANDIDATE_SHA"'); expect(restore.uses).toContain("actions/cache/restore@"); + expect(setup.with?.["cache-mode"]).toBe("read-write"); + expect(save.if).toContain("steps.setup-node-env.outputs.cache-mode == 'read-write'"); expect(restore.with?.key).toContain("needs.resolve_request.outputs.baseline_revision"); expect(restore.with?.key).toContain("steps.proof_worktrees.outputs.lockfile_sha256"); expect(restore.with?.key).toContain("steps.proof_worktrees.outputs.node_version"); @@ -723,7 +726,7 @@ describe("Mantis Telegram Desktop proof workflow", () => { expect(baselineRun).toContain(".artifacts/build-all-cache"); expect(baselineRun).toContain("for phase in tsdown-ai tsdown-packages tsdown-unified"); expect(baselineRun).toContain("-type f -links +1"); - expect(save.if).toBe("steps.baseline_build_cache.outputs.cache-hit != 'true'"); + expect(save.if).toContain("steps.baseline_build_cache.outputs.cache-hit != 'true'"); expect(save.uses).toContain("actions/cache/save@"); expect(save.with?.path).toBe(restore.with?.path); expect(candidate.if).toBeUndefined(); diff --git a/test/scripts/openclaw-npm-extended-stable-workflow.test.ts b/test/scripts/openclaw-npm-extended-stable-workflow.test.ts index e3d478f61fad..2ca2caf69277 100644 --- a/test/scripts/openclaw-npm-extended-stable-workflow.test.ts +++ b/test/scripts/openclaw-npm-extended-stable-workflow.test.ts @@ -333,7 +333,10 @@ describe("minimal npm extended-stable workflow", () => { expect(step(preflight, "Verify prepared npm tarball install").if).toBeUndefined(); const save = step(preflight, "Save preflight build outputs"); + const setup = step(preflight, "Setup Node environment"); + expect(setup.with?.["cache-mode"]).toBe("read-write"); expect(save.uses).toContain("actions/cache/save@"); + expect(save.if).toContain("steps.setup-node-env.outputs.cache-mode == 'read-write'"); expect(save.with?.key).toBe("${{ steps.dist_build_cache.outputs.cache-primary-key }}"); }); diff --git a/test/scripts/package-acceptance-workflow.test.ts b/test/scripts/package-acceptance-workflow.test.ts index 33c84509832a..fea4d2ae2270 100644 --- a/test/scripts/package-acceptance-workflow.test.ts +++ b/test/scripts/package-acceptance-workflow.test.ts @@ -2209,7 +2209,7 @@ describe("package acceptance workflow", () => { expect(setupPnpmAction).toContain('case "$package_manager" in'); expect(setupPnpmAction).toContain('corepack prepare "$package_manager" --activate'); expect(setupPnpmAction).toContain( - "if: ${{ inputs.use-actions-cache == 'true' && runner.os != 'Windows' }}", + "if: ${{ inputs.cache-mode != 'off' && runner.os != 'Windows' }}", ); expect(setupPnpmAction).toContain( "key: pnpm-store-${{ runner.os }}-${{ runner.arch }}-${{ inputs.node-version }}-${{ hashFiles(inputs.package-manager-file) }}-${{ hashFiles(inputs.lockfile-path) }}", @@ -4434,7 +4434,7 @@ describe("package artifact reuse", () => { expect(workflow).not.toContain("PNPM_CONFIG_VIRTUAL_STORE_DIR"); expect(setupNodeWith).not.toHaveProperty("dependency-cache"); expect(setupNodeWith).not.toHaveProperty("sticky-disk"); - expect(setupNodeWith["use-actions-cache"]).toBe("true"); + expect(setupNodeWith["cache-mode"]).toBe("restore"); expect(checkTestboxJob["timeout-minutes"]).toBe( "${{ fromJSON(inputs.timeout_minutes || '120') }}", ); @@ -5517,9 +5517,9 @@ describe("package artifact reuse", () => { expect(job["runs-on"]).toBe("blacksmith-32vcpu-ubuntu-2404"); expect(job.env?.OPENCLAW_DOCKER_ALL_RELEASE_PROFILE).toBe("${{ inputs.release_test_profile }}"); expect(setupNode.with).toMatchObject({ + "cache-mode": "off", "install-bun": "false", "install-deps": "false", - "use-actions-cache": "false", }); }); diff --git a/test/scripts/plugin-prerelease-test-plan.test.ts b/test/scripts/plugin-prerelease-test-plan.test.ts index 5fa5d4599386..5ef611eb5cb1 100644 --- a/test/scripts/plugin-prerelease-test-plan.test.ts +++ b/test/scripts/plugin-prerelease-test-plan.test.ts @@ -543,6 +543,7 @@ describe("scripts/lib/plugin-prerelease-test-plan.mts", () => { name: "Setup Node environment", uses: "./.github/actions/setup-node-env", with: { + "cache-mode": "restore", "install-bun": "false", }, }, @@ -766,6 +767,7 @@ describe("scripts/lib/plugin-prerelease-test-plan.mts", () => { expect( inspector.steps.find((step: WorkflowStep) => step.name === "Setup Node environment").with, ).toEqual({ + "cache-mode": "restore", "install-bun": "false", }); const inspectorRun = inspector.steps.find(