fix(ci): persist warm runtime caches across runs (#110363)

* ci: make cache persistence verifiable

* test: fix cache guard CI failures

* test: narrow cache maintenance env

* fix(ci): stabilize vitest cache fingerprint

* test: stabilize lagging-history outbox check
This commit is contained in:
Peter Steinberger
2026-07-18 08:21:26 +01:00
committed by GitHub
parent f93bfc240b
commit 64f3397874
8 changed files with 233 additions and 236 deletions
+42 -68
View File
@@ -43,10 +43,6 @@ inputs:
snapshot.
required: false
default: "false"
runtime-cache-sticky-disk:
description: Whether runtime caches should use Blacksmith sticky disks instead of actions/cache.
required: false
default: "false"
vitest-fs-cache:
description: Whether to persist Vitest's experimental filesystem module cache.
required: false
@@ -64,10 +60,7 @@ inputs:
required: false
default: "false"
save-vitest-fs-cache:
description: >
Whether this job may save the shared Vitest filesystem module cache.
Only honored outside pull_request events for sticky disks; PR jobs
always mount the protected snapshot read-only.
description: Whether this job may save the shared Vitest filesystem module cache.
required: false
default: "false"
build-all-cache-scope:
@@ -122,15 +115,15 @@ runs:
- name: Mount dependency sticky disk
if: inputs.sticky-disk == 'true'
uses: useblacksmith/stickydisk@5b350170ae4ef55b536b548ef5f5896e76a6b54f # v1.4.0
uses: useblacksmith/stickydisk@6d373c96a74cbde0c99fedc5ea5d3a7ba66ba494 # main (post-v1.4.0 hot-attach fix)
with:
# One stable disk per Node line. v4 resets the v3 lineage after
# Blacksmith repeatedly acknowledged commits but kept restoring its
# original snapshot. The v2 per-PR/per-manifest-hash keys
# One stable disk per Node line. v5 starts a fresh lineage for the
# preflight-serialized writer after Blacksmith acknowledged repeated v4
# commits but kept restoring its original snapshot. The v2 per-PR/per-manifest-hash keys
# saturated Blacksmith's installation-wide sticky-disk budget. Install
# inputs, runner platform, and the exact Node patch live in the runtime
# marker below, so changes refresh this disk in place.
key: ${{ github.repository }}-node-deps-bind-v4-${{ inputs.node-version }}
key: ${{ github.repository }}-node-deps-bind-v5-${{ inputs.node-version }}
path: /var/tmp/openclaw-node-deps
# Single semantic writer: only the designated trusted-push job may
# commit, so pull_request clones stay read-only. Like every sticky
@@ -139,58 +132,37 @@ runs:
# and same-repo PR authors already hold repository write access.
# Explicit true (not on-change) because the allocated-byte heuristic
# can miss a fingerprint refresh whose reinstall keeps disk usage
# stable, permanently stranding consumers on a stale marker. v1.4.0
# skips commit after failed/cancelled steps, so a broken install
# cannot seed this key.
# stable, permanently stranding consumers on a stale marker. The action
# skips commit after failed/cancelled steps, so a broken install cannot
# seed this key.
commit: ${{ inputs.save-sticky-disk == 'true' && github.event_name != 'pull_request' && 'true' || 'false' }}
- name: Mount Vitest transform cache sticky disk
if: inputs.vitest-fs-cache == 'true' && (inputs.sticky-disk == 'true' || inputs.runtime-cache-sticky-disk == 'true') && runner.os != 'Windows'
uses: useblacksmith/stickydisk@5b350170ae4ef55b536b548ef5f5896e76a6b54f # v1.4.0
with:
# One stable disk per runner shape. The old per-PR overlay disks
# minted a new backing disk for every PR and helped saturate
# Blacksmith's installation-wide sticky-disk budget, 429-failing every
# mount. Cache entries are content-hash keyed (id+content+env), so PRs
# can safely read the protected snapshot directly; PR-local misses are
# rebuilt in the discarded mount. The generation guard below clears
# incompatible transform inputs without leaking one disk per lockfile.
key: ${{ github.repository }}-vitest-fs-v2-protected-${{ runner.os }}-${{ runner.arch }}-node-${{ inputs.node-version }}
path: /var/tmp/openclaw-vitest-fs-cache
# Single semantic writer: only the designated non-PR writer commits;
# pull_request mounts stay read-only clones. Explicit true avoids
# stickydisk's allocated-byte heuristic missing equal-size
# replacements or prune-balanced writes.
commit: ${{ inputs.save-vitest-fs-cache == 'true' && github.event_name != 'pull_request' && 'true' || 'false' }}
- name: Restore and save Vitest transform cache
if: inputs.vitest-fs-cache == 'true' && inputs.sticky-disk != 'true' && inputs.runtime-cache-sticky-disk != 'true' && inputs.save-vitest-fs-cache == 'true' && runner.os != 'Windows'
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
# actions/cache entries are immutable. One matrix job writes a new
# run key; every job restores the newest cache through coarse prefixes.
key: ${{ github.repository }}-vitest-fs-v2-${{ github.event_name == 'pull_request' && format('pr-{0}', github.event.pull_request.number) || 'protected' }}-${{ runner.os }}-${{ runner.arch }}-node-${{ inputs.node-version }}-${{ hashFiles('pnpm-lock.yaml', 'pnpm-workspace.yaml', '**/package.json', '**/tsconfig*.json', 'vitest.config.*', 'test/vitest/**') }}-${{ github.run_id }}-${{ github.run_attempt }}
# 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/**', '!**/node_modules/**') }}-${{ github.run_id }}-${{ github.run_attempt }}
restore-keys: |
${{ github.repository }}-vitest-fs-v2-${{ github.event_name == 'pull_request' && format('pr-{0}', github.event.pull_request.number) || 'protected' }}-${{ runner.os }}-${{ runner.arch }}-node-${{ inputs.node-version }}-${{ hashFiles('pnpm-lock.yaml', 'pnpm-workspace.yaml', '**/package.json', '**/tsconfig*.json', 'vitest.config.*', 'test/vitest/**') }}-
${{ github.repository }}-vitest-fs-v3-protected-${{ runner.os }}-${{ runner.arch }}-node-${{ inputs.node-version }}-${{ hashFiles('pnpm-lock.yaml', 'pnpm-workspace.yaml', '**/package.json', '**/tsconfig*.json', 'vitest.config.*', 'test/vitest/**', '!**/node_modules/**') }}-
- name: Restore Vitest transform cache
if: inputs.vitest-fs-cache == 'true' && inputs.sticky-disk != 'true' && inputs.runtime-cache-sticky-disk != 'true' && inputs.save-vitest-fs-cache != 'true' && runner.os != 'Windows'
if: inputs.vitest-fs-cache == 'true' && inputs.save-vitest-fs-cache != 'true' && runner.os != 'Windows'
uses: actions/cache/restore@27d5ce7f107fe9357f9df03efb73ab90386fccae # v5
with:
path: /var/tmp/openclaw-vitest-fs-cache
key: ${{ github.repository }}-vitest-fs-v2-${{ github.event_name == 'pull_request' && format('pr-{0}', github.event.pull_request.number) || 'protected' }}-${{ runner.os }}-${{ runner.arch }}-node-${{ inputs.node-version }}-${{ hashFiles('pnpm-lock.yaml', 'pnpm-workspace.yaml', '**/package.json', '**/tsconfig*.json', 'vitest.config.*', 'test/vitest/**') }}-${{ github.run_id }}-${{ github.run_attempt }}
key: ${{ github.repository }}-vitest-fs-v3-protected-${{ runner.os }}-${{ runner.arch }}-node-${{ inputs.node-version }}-${{ hashFiles('pnpm-lock.yaml', 'pnpm-workspace.yaml', '**/package.json', '**/tsconfig*.json', 'vitest.config.*', 'test/vitest/**', '!**/node_modules/**') }}-${{ github.run_id }}-${{ github.run_attempt }}
restore-keys: |
${{ github.repository }}-vitest-fs-v2-${{ github.event_name == 'pull_request' && format('pr-{0}', github.event.pull_request.number) || 'protected' }}-${{ runner.os }}-${{ runner.arch }}-node-${{ inputs.node-version }}-${{ hashFiles('pnpm-lock.yaml', 'pnpm-workspace.yaml', '**/package.json', '**/tsconfig*.json', 'vitest.config.*', 'test/vitest/**') }}-
${{ github.repository }}-vitest-fs-v3-protected-${{ runner.os }}-${{ runner.arch }}-node-${{ inputs.node-version }}-${{ hashFiles('pnpm-lock.yaml', 'pnpm-workspace.yaml', '**/package.json', '**/tsconfig*.json', 'vitest.config.*', 'test/vitest/**', '!**/node_modules/**') }}-
- name: Configure Vitest transform cache
if: inputs.vitest-fs-cache == 'true' && runner.os != 'Windows'
env:
CACHE_GENERATION: ${{ hashFiles('pnpm-lock.yaml', 'pnpm-workspace.yaml', '**/package.json', '**/tsconfig*.json', 'vitest.config.*', 'test/vitest/**') }}
# Sticky PR mounts never commit, so pruning there would only burn
# shard wall clock on a discarded clone; non-sticky (actions/cache)
# writers still prune their PR-scoped archives.
CACHE_WRITER: ${{ inputs.save-vitest-fs-cache == 'true' && ((inputs.sticky-disk != 'true' && inputs.runtime-cache-sticky-disk != 'true') || github.event_name != 'pull_request') && '1' || '0' }}
CACHE_GENERATION: ${{ hashFiles('pnpm-lock.yaml', 'pnpm-workspace.yaml', '**/package.json', '**/tsconfig*.json', 'vitest.config.*', 'test/vitest/**', '!**/node_modules/**') }}
CACHE_WRITER: ${{ inputs.save-vitest-fs-cache == 'true' && '1' || '0' }}
shell: bash
run: |
set -euo pipefail
@@ -202,9 +174,8 @@ runs:
cache_generation="$(<"$generation_file")"
fi
cache_entry="$(find "$cache_root" -mindepth 1 -maxdepth 1 -print -quit)"
# Read-only PR mounts clear a mismatched generation too: the wipe only
# affects the discarded local clone, and mixing generations could
# false-hit transforms produced under incompatible tsconfig/lockfile.
# Every restore is runner-local. Never mix incompatible transform
# inputs even when an older archive was selected through a prefix.
if [[ -n "$cache_entry" ]] && [[ "$cache_generation" != "$CACHE_GENERATION" ]]; then
echo "Vitest transform inputs changed; clearing incompatible cache generation"
find "$cache_root" -mindepth 1 -maxdepth 1 -exec rm -rf -- {} +
@@ -215,39 +186,42 @@ runs:
echo "OPENCLAW_VITEST_FS_MODULE_CACHE_PATH=$cache_root" >> "$GITHUB_ENV"
echo "OPENCLAW_VITEST_FS_MODULE_CACHE_WRITER=$CACHE_WRITER" >> "$GITHUB_ENV"
- name: Mount Node compile cache sticky disk
if: inputs.node-compile-cache == 'true' && (inputs.sticky-disk == 'true' || inputs.runtime-cache-sticky-disk == 'true') && runner.os != 'Windows'
uses: useblacksmith/stickydisk@5b350170ae4ef55b536b548ef5f5896e76a6b54f # v1.4.0
with:
# Trusted PRs read the protected dependency bytecode seed but cannot
# publish feature-branch code into it. Node isolates exact versions and
# invalidates bytecode when module contents change.
key: ${{ github.repository }}-node-compile-v2-${{ inputs.node-compile-cache-scope }}-protected-${{ runner.os }}-${{ runner.arch }}-node-${{ inputs.node-version }}
path: /var/tmp/openclaw-node-compile-cache
commit: ${{ inputs.save-node-compile-cache == 'true' && github.event_name != 'pull_request' && 'true' || 'false' }}
- name: Select Node compile cache epoch
id: node-compile-cache-epoch
if: inputs.node-compile-cache == 'true' && runner.os != 'Windows'
shell: bash
env:
CACHE_SCOPE: ${{ inputs.node-compile-cache-scope }}
run: |
set -euo pipefail
if [ "$CACHE_SCOPE" = "build" ]; then
echo "value=$(date -u +%Y%m%d)" >> "$GITHUB_OUTPUT"
else
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.sticky-disk != 'true' && inputs.runtime-cache-sticky-disk != 'true' && inputs.save-node-compile-cache == 'true' && runner.os != 'Windows'
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-v2-${{ inputs.node-compile-cache-scope }}-${{ github.event_name == 'pull_request' && format('pr-{0}', github.event.pull_request.number) || 'protected' }}-${{ runner.os }}-${{ runner.arch }}-node-${{ inputs.node-version }}-${{ github.run_id }}-${{ github.run_attempt }}
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-v2-${{ inputs.node-compile-cache-scope }}-${{ github.event_name == 'pull_request' && format('pr-{0}', github.event.pull_request.number) || 'protected' }}-${{ runner.os }}-${{ runner.arch }}-node-${{ inputs.node-version }}-
${{ github.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.sticky-disk != 'true' && inputs.runtime-cache-sticky-disk != 'true' && inputs.save-node-compile-cache != 'true' && runner.os != 'Windows'
if: inputs.node-compile-cache == 'true' && inputs.save-node-compile-cache != 'true' && runner.os != 'Windows'
uses: actions/cache/restore@27d5ce7f107fe9357f9df03efb73ab90386fccae # v5
with:
path: /var/tmp/openclaw-node-compile-cache
key: ${{ github.repository }}-node-compile-v2-${{ inputs.node-compile-cache-scope }}-${{ github.event_name == 'pull_request' && format('pr-{0}', github.event.pull_request.number) || 'protected' }}-${{ runner.os }}-${{ runner.arch }}-node-${{ inputs.node-version }}-${{ github.run_id }}-${{ github.run_attempt }}
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-v2-${{ inputs.node-compile-cache-scope }}-${{ github.event_name == 'pull_request' && format('pr-{0}', github.event.pull_request.number) || 'protected' }}-${{ runner.os }}-${{ runner.arch }}-node-${{ inputs.node-version }}-
${{ github.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' && runner.os != 'Windows'
env:
CACHE_WRITER: ${{ inputs.save-node-compile-cache == 'true' && ((inputs.sticky-disk != 'true' && inputs.runtime-cache-sticky-disk != 'true') || github.event_name != 'pull_request') && '1' || '0' }}
CACHE_WRITER: ${{ inputs.save-node-compile-cache == 'true' && '1' || '0' }}
shell: bash
run: |
set -euo pipefail
+22 -1
View File
@@ -1 +1,22 @@
[]
[
{
"key": "openclaw/openclaw-node-compile-v2-build-protected-Linux-X64-node-24.x",
"architecture": "amd64",
"region": "eu-west"
},
{
"key": "openclaw/openclaw-node-compile-v2-test-protected-Linux-X64-node-24.x",
"architecture": "amd64",
"region": "eu-west"
},
{
"key": "openclaw/openclaw-node-deps-bind-v4-24.x",
"architecture": "amd64",
"region": "eu-west"
},
{
"key": "openclaw/openclaw-vitest-fs-v2-protected-Linux-X64-node-24.x",
"architecture": "amd64",
"region": "eu-west"
}
]
+53 -13
View File
@@ -745,6 +745,51 @@ jobs:
if: steps.manifest.outputs.run_protocol_event_coverage == 'true'
run: node scripts/check-protocol-event-coverage.mjs
# Canonical main cannot be cancelled by a newer push. Publish the sole
# dependency snapshot here before fanout. Blacksmith may expose a fresh
# commit only to a later run; readers retain the marker-checked fallback.
- name: Refresh sticky dependency snapshot
if: github.repository == 'openclaw/openclaw' && github.event_name == 'push' && github.ref == 'refs/heads/main' && steps.manifest.outputs.run_node == 'true'
uses: ./.github/actions/setup-node-env
with:
install-bun: "false"
save-sticky-disk: "true"
sticky-disk: "true"
use-actions-cache: "false"
- name: Maintain sticky dependency store budget
if: github.repository == 'openclaw/openclaw' && github.event_name == 'push' && github.ref == 'refs/heads/main' && steps.manifest.outputs.run_node == 'true'
shell: bash
env:
OPENCLAW_PNPM_STORE_MAX_KIB: "8388608"
run: |
set -euo pipefail
store_dir="${PNPM_CONFIG_STORE_DIR:?}"
before_kib="$(du -sk "$store_dir" | cut -f1)"
after_kib="$before_kib"
pruned=false
if [ "$before_kib" -gt "$OPENCLAW_PNPM_STORE_MAX_KIB" ]; then
echo "pnpm store is ${before_kib} KiB; pruning above ${OPENCLAW_PNPM_STORE_MAX_KIB} KiB ceiling"
PNPM_CONFIG_STORE_DIR="$store_dir" pnpm store prune
after_kib="$(du -sk "$store_dir" | cut -f1)"
pruned=true
else
echo "pnpm store is ${before_kib} KiB; below ${OPENCLAW_PNPM_STORE_MAX_KIB} KiB ceiling"
fi
{
echo "### Dependency store maintenance"
echo
echo "- Before: ${before_kib} KiB"
echo "- After: ${after_kib} KiB"
echo "- Pruned: ${pruned}"
} >> "$GITHUB_STEP_SUMMARY"
if [ "$after_kib" -gt "$OPENCLAW_PNPM_STORE_MAX_KIB" ]; then
echo "::warning::pnpm store remains above its 8 GiB maintenance ceiling after prune"
fi
# Run dependency-free security checks on a hosted runner in parallel with
# scope detection. No downstream job waits for Python/pre-commit setup.
security-fast:
@@ -909,13 +954,13 @@ jobs:
- name: Audit production dependencies
run: node scripts/pre-commit/pnpm-audit-prod.mjs --audit-level=high
# Warm the lockfile- and pnpm-pinned store without blocking Linux Node shards.
# On a cold key this job owns the save for later workflow runs.
# Warm the lockfile- and pnpm-pinned Actions cache for PR and manual runs.
# Canonical main publishes its sticky snapshot in preflight before fanout.
pnpm-store-warmup:
permissions:
contents: read
needs: [preflight]
if: needs.preflight.outputs.run_node == 'true' || needs.preflight.outputs.run_check_docs == 'true'
if: ${{ (needs.preflight.outputs.run_node == 'true' || needs.preflight.outputs.run_check_docs == 'true') && !(github.repository == 'openclaw/openclaw' && github.event_name == 'push' && github.ref == 'refs/heads/main') }}
runs-on: ${{ github.event_name == 'workflow_dispatch' && 'ubuntu-24.04' || (github.repository == 'openclaw/openclaw' && (github.event_name != 'pull_request' || github.event.pull_request.head.repo.full_name == 'openclaw/openclaw') && 'blacksmith-4vcpu-ubuntu-2404' || 'ubuntu-24.04') }}
timeout-minutes: 20
steps:
@@ -1007,7 +1052,6 @@ jobs:
# repository-global state and must never be produced by fork code.
sticky-disk: ${{ 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: ${{ 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' }}
runtime-cache-sticky-disk: ${{ 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' }}
save-node-compile-cache: ${{ github.event_name == 'push' && github.ref == 'refs/heads/main' && 'true' || 'false' }}
- name: Restore build-all step cache
@@ -1805,16 +1849,12 @@ jobs:
# Fork PRs must keep actions/cache: sticky snapshots are writable,
# repository-global state and must never be produced by fork code.
sticky-disk: ${{ (matrix.node_version == null || matrix.node_version == '24.x') && 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' }}
runtime-cache-sticky-disk: ${{ (matrix.node_version == null || matrix.node_version == '24.x') && 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') && 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"
# One matrix job is the designated warm-seed writer; the action only
# honors it outside pull_request events, so PR shards always read the
# protected snapshot without committing competing writes.
save-vitest-fs-cache: ${{ matrix.save_vitest_fs_cache && 'true' || 'false' }}
save-node-compile-cache: ${{ matrix.save_vitest_fs_cache && 'true' || 'false' }}
# CI shards only read the protected immutable seed. The scheduled or
# explicitly dispatched warmer is the sole test-cache writer.
- name: Setup Go for docs i18n
if: matrix.requires_go == true
@@ -1962,7 +2002,7 @@ jobs:
# protected boundary lane may publish this repository-global snapshot.
- name: Mount extension boundary sticky disk
if: matrix.task == 'lint' && github.event_name != 'workflow_dispatch' && github.repository == 'openclaw/openclaw' && (github.event_name != 'pull_request' || github.event.pull_request.head.repo.full_name == 'openclaw/openclaw')
uses: useblacksmith/stickydisk@5b350170ae4ef55b536b548ef5f5896e76a6b54f # v1.4.0
uses: useblacksmith/stickydisk@6d373c96a74cbde0c99fedc5ea5d3a7ba66ba494 # main (post-v1.4.0 hot-attach fix)
with:
# One stable disk for the whole repository. The v1 per-PR/per-config
# keys minted a new backing disk for every PR and toolchain change
@@ -2205,7 +2245,7 @@ jobs:
# repository-global snapshots, so they keep the GitHub cache path.
- name: Mount extension boundary sticky disk
if: matrix.group == 'extension-package-boundary' && github.event_name != 'workflow_dispatch' && github.repository == 'openclaw/openclaw' && (github.event_name != 'pull_request' || github.event.pull_request.head.repo.full_name == 'openclaw/openclaw')
uses: useblacksmith/stickydisk@5b350170ae4ef55b536b548ef5f5896e76a6b54f # v1.4.0
uses: useblacksmith/stickydisk@6d373c96a74cbde0c99fedc5ea5d3a7ba66ba494 # main (post-v1.4.0 hot-attach fix)
with:
# One stable disk for the whole repository. The v1 per-PR/per-config
# keys minted a new backing disk for every PR and toolchain change
@@ -3131,7 +3171,7 @@ jobs:
# runs-on already selects non-Blacksmith ubuntu-24.04 runners).
- name: Mount Gradle sticky disk
if: github.event_name != 'workflow_dispatch' && github.repository == 'openclaw/openclaw' && (github.event_name != 'pull_request' || github.event.pull_request.head.repo.full_name == 'openclaw/openclaw')
uses: useblacksmith/stickydisk@5b350170ae4ef55b536b548ef5f5896e76a6b54f # v1.4.0
uses: useblacksmith/stickydisk@6d373c96a74cbde0c99fedc5ea5d3a7ba66ba494 # main (post-v1.4.0 hot-attach fix)
with:
# One stable disk per matrix task. The v1 per-PR/per-dependency-hash
# keys minted a new backing disk for every PR and dependency bump
+10 -59
View File
@@ -5,10 +5,6 @@ on:
types: [vitest-cache-warm]
schedule:
- cron: "17 8 * * *"
workflow_run: # zizmor: ignore[dangerous-triggers] successful main-push CI only; read-only default-branch checkout, no artifacts or untrusted ref
workflows: [CI]
branches: [main]
types: [completed]
permissions:
contents: read
@@ -19,73 +15,29 @@ concurrency:
jobs:
warm:
# Canonical main CI is single-flight, so only a completed green integration
# cycle may refresh the dependency snapshot. Scheduled/dispatch runs remain
# the transform and compile cache writers.
if: github.repository == 'openclaw/openclaw' && (github.event_name != 'workflow_run' || (github.event.workflow_run.event == 'push' && github.event.workflow_run.conclusion == 'success' && github.event.workflow_run.head_branch == 'main'))
# Dependency snapshots are serialized before main CI fanout. This workflow
# writes only the transform and compile caches from scheduled/trusted runs.
if: github.repository == 'openclaw/openclaw'
runs-on: blacksmith-8vcpu-ubuntu-2404
timeout-minutes: 30
steps:
- name: Checkout
uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6
with:
# workflow_run's github.sha follows the current default-branch tip,
# which can be newer than the green run that authorized this write.
ref: ${{ github.event_name == 'workflow_run' && github.event.workflow_run.head_sha || github.sha }}
- name: Setup Node environment
uses: ./.github/actions/setup-node-env
with:
install-bun: "false"
node-compile-cache: ${{ github.event_name != 'workflow_run' && 'true' || 'false' }}
node-compile-cache: "true"
node-compile-cache-scope: "test"
runtime-cache-sticky-disk: ${{ github.event_name != 'workflow_run' && 'true' || 'false' }}
save-node-compile-cache: ${{ github.event_name != 'workflow_run' && 'true' || 'false' }}
# This disposable, non-cancelled workflow is the sole dependency
# snapshot writer. Main CI completion coalesces refreshes without
# putting required build jobs in a cancel-on-pending mutex.
save-sticky-disk: "true"
save-vitest-fs-cache: ${{ github.event_name != 'workflow_run' && 'true' || 'false' }}
sticky-disk: "true"
use-actions-cache: "false"
vitest-fs-cache: ${{ github.event_name != 'workflow_run' && 'true' || 'false' }}
- name: Maintain dependency store budget
shell: bash
env:
# The current store is well below this ceiling. Prune only after
# accumulated retired package versions exceed 8 GiB.
OPENCLAW_PNPM_STORE_MAX_KIB: "8388608"
run: |
set -euo pipefail
store_dir="${PNPM_CONFIG_STORE_DIR:?}"
before_kib="$(du -sk "$store_dir" | cut -f1)"
after_kib="$before_kib"
pruned=false
if [ "$before_kib" -gt "$OPENCLAW_PNPM_STORE_MAX_KIB" ]; then
echo "pnpm store is ${before_kib} KiB; pruning above ${OPENCLAW_PNPM_STORE_MAX_KIB} KiB ceiling"
PNPM_CONFIG_STORE_DIR="$store_dir" pnpm store prune
after_kib="$(du -sk "$store_dir" | cut -f1)"
pruned=true
else
echo "pnpm store is ${before_kib} KiB; below ${OPENCLAW_PNPM_STORE_MAX_KIB} KiB ceiling"
fi
{
echo "### Dependency store maintenance"
echo
echo "- Before: ${before_kib} KiB"
echo "- After: ${after_kib} KiB"
echo "- Pruned: ${pruned}"
} >> "$GITHUB_STEP_SUMMARY"
if [ "$after_kib" -gt "$OPENCLAW_PNPM_STORE_MAX_KIB" ]; then
echo "::warning::pnpm store remains above its 8 GiB maintenance ceiling after prune"
fi
save-actions-cache: "true"
save-node-compile-cache: "true"
save-vitest-fs-cache: "true"
sticky-disk: "false"
use-actions-cache: "true"
vitest-fs-cache: "true"
- name: Select broad cache seed
if: github.event_name != 'workflow_run'
shell: bash
run: |
set -euo pipefail
@@ -113,5 +65,4 @@ jobs:
EOF
- name: Warm transform and compile caches
if: github.event_name != 'workflow_run'
run: node scripts/ci-run-node-test-shard.mjs