@@ -35,10 +35,26 @@ inputs:
3535 alongside.
3636 required : false
3737 default : " false"
38+ runtime-cache-sticky-disk :
39+ description : Whether runtime caches should use Blacksmith sticky disks instead of actions/cache.
40+ required : false
41+ default : " false"
3842 vitest-fs-cache :
3943 description : Whether to persist Vitest's experimental filesystem module cache.
4044 required : false
4145 default : " false"
46+ node-compile-cache :
47+ description : Whether to persist Node's on-disk V8 compile cache.
48+ required : false
49+ default : " false"
50+ node-compile-cache-scope :
51+ description : Cache namespace for isolating workloads with different writer ownership.
52+ required : false
53+ default : " test"
54+ save-node-compile-cache :
55+ description : Whether this job may save the Node compile cache.
56+ required : false
57+ default : " false"
4258 save-vitest-fs-cache :
4359 description : >
4460 Whether this job may save the shared Vitest filesystem module cache.
@@ -103,46 +119,123 @@ runs:
103119 # detection, so an incomplete first hydration cannot seed this key.
104120 commit : if-missing
105121
122+ - name : Mount protected Vitest transform seed
123+ if : inputs.vitest-fs-cache == 'true' && (inputs.sticky-disk == 'true' || inputs.runtime-cache-sticky-disk == 'true') && github.event_name == 'pull_request' && runner.os != 'Windows'
124+ uses : useblacksmith/stickydisk@5b350170ae4ef55b536b548ef5f5896e76a6b54f # v1.4.0
125+ with :
126+ # Same-repository PRs read the protected snapshot but never write it.
127+ # Content-addressed misses stay in the PR-scoped overlay below.
128+ key : ${{ github.repository }}-vitest-fs-v2-protected-${{ runner.os }}-${{ runner.arch }}-node-${{ inputs.node-version }}
129+ path : /var/tmp/openclaw-vitest-fs-protected-seed
130+ commit : false
131+
106132 - name : Mount Vitest transform cache sticky disk
107- if : inputs.vitest-fs-cache == 'true' && inputs.sticky-disk == 'true' && runner.os != 'Windows'
133+ if : inputs.vitest-fs-cache == 'true' && ( inputs.sticky-disk == 'true' || inputs.runtime-cache-sticky-disk == 'true') && runner.os != 'Windows'
108134 uses : useblacksmith/stickydisk@5b350170ae4ef55b536b548ef5f5896e76a6b54f # v1.4.0
109135 with :
110- # Match dependency-cache trust scoping. Vitest content-addresses module
111- # entries; the lockfile key rotates dependency/plugin generations .
112- key : ${{ github.repository }}-vitest-fs-v1 -${{ 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') }}
136+ # One stable disk per trust scope. The generation guard below clears
137+ # incompatible transform inputs without leaking one disk per lockfile .
138+ 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 }}
113139 path : /var/tmp/openclaw-vitest-fs-cache
114- commit : ${{ inputs.save-vitest-fs-cache == 'true' && 'on-change' || 'false' }}
140+ # One semantic writer only. Explicit true avoids stickydisk's allocated-
141+ # byte heuristic missing equal-size replacements or prune-balanced writes.
142+ commit : ${{ inputs.save-vitest-fs-cache == 'true' && 'true' || 'false' }}
115143
116144 - name : Restore and save Vitest transform cache
117- if : inputs.vitest-fs-cache == 'true' && inputs.sticky-disk != 'true' && inputs.save-vitest-fs-cache == 'true' && runner.os != 'Windows'
145+ 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'
118146 uses : actions/cache@27d5ce7f107fe9357f9df03efb73ab90386fccae # v5
119147 with :
120148 path : /var/tmp/openclaw-vitest-fs-cache
121149 # actions/cache entries are immutable. One matrix job writes a new
122150 # run key; every job restores the newest cache through coarse prefixes.
123- key : ${{ github.repository }}-vitest-fs-v1 -${{ 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') }}-${{ github.run_id }}
151+ 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 }}
124152 restore-keys : |
125- ${{ github.repository }}-vitest-fs-v1 -${{ 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') }}-
153+ ${{ 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/** ') }}-
126154
127155 - name : Restore Vitest transform cache
128- if : inputs.vitest-fs-cache == 'true' && inputs.sticky-disk != 'true' && inputs.save-vitest-fs-cache != 'true' && runner.os != 'Windows'
156+ 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'
129157 uses : actions/cache/restore@27d5ce7f107fe9357f9df03efb73ab90386fccae # v5
130158 with :
131159 path : /var/tmp/openclaw-vitest-fs-cache
132- key : ${{ github.repository }}-vitest-fs-v1 -${{ 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') }}-${{ github.run_id }}
160+ 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 }}
133161 restore-keys : |
134- ${{ github.repository }}-vitest-fs-v1 -${{ 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') }}-
162+ ${{ 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/** ') }}-
135163
136164 - name : Configure Vitest transform cache
137165 if : inputs.vitest-fs-cache == 'true' && runner.os != 'Windows'
166+ env :
167+ CACHE_GENERATION : ${{ hashFiles('pnpm-lock.yaml', 'pnpm-workspace.yaml', '**/package.json', '**/tsconfig*.json', 'vitest.config.*', 'test/vitest/**') }}
168+ CACHE_WRITER : ${{ inputs.save-vitest-fs-cache == 'true' && '1' || '0' }}
138169 shell : bash
139170 run : |
140171 set -euo pipefail
141172 cache_root=/var/tmp/openclaw-vitest-fs-cache
173+ seed_root=/var/tmp/openclaw-vitest-fs-protected-seed
174+ generation_file="$cache_root/.openclaw-transform-generation"
142175 mkdir -p "$cache_root"
176+ cache_generation=""
177+ if [[ -f "$generation_file" ]]; then
178+ cache_generation="$(<"$generation_file")"
179+ fi
180+ cache_entry="$(find "$cache_root" -mindepth 1 -maxdepth 1 -print -quit)"
181+ if [[ -n "$cache_entry" ]] && [[ "$cache_generation" != "$CACHE_GENERATION" ]]; then
182+ echo "Vitest transform inputs changed; clearing incompatible cache generation"
183+ find "$cache_root" -mindepth 1 -maxdepth 1 -exec rm -rf -- {} +
184+ fi
185+ seed_generation_file="$seed_root/.openclaw-transform-generation"
186+ if [[ -f "$seed_generation_file" ]] && [[ "$(<"$seed_generation_file")" == "$CACHE_GENERATION" ]]; then
187+ echo "Merging matching protected Vitest transform seed"
188+ cp -a --no-clobber "$seed_root"/. "$cache_root"/
189+ elif [[ -f "$seed_generation_file" ]]; then
190+ echo "Ignoring protected Vitest transform seed from an incompatible generation"
191+ fi
192+ printf '%s\n' "$CACHE_GENERATION" > "$generation_file"
143193 # The shard runner treats this as a persistent root and assigns one
144194 # isolated subdirectory per concurrent Vitest worker.
145195 echo "OPENCLAW_VITEST_FS_MODULE_CACHE_PATH=$cache_root" >> "$GITHUB_ENV"
196+ echo "OPENCLAW_VITEST_FS_MODULE_CACHE_WRITER=$CACHE_WRITER" >> "$GITHUB_ENV"
197+
198+ - name : Mount Node compile cache sticky disk
199+ if : inputs.node-compile-cache == 'true' && (inputs.sticky-disk == 'true' || inputs.runtime-cache-sticky-disk == 'true') && runner.os != 'Windows'
200+ uses : useblacksmith/stickydisk@5b350170ae4ef55b536b548ef5f5896e76a6b54f # v1.4.0
201+ with :
202+ # Trusted PRs read the protected dependency bytecode seed but cannot
203+ # publish feature-branch code into it. Node isolates exact versions and
204+ # invalidates bytecode when module contents change.
205+ key : ${{ github.repository }}-node-compile-v2-${{ inputs.node-compile-cache-scope }}-protected-${{ runner.os }}-${{ runner.arch }}-node-${{ inputs.node-version }}
206+ path : /var/tmp/openclaw-node-compile-cache
207+ commit : ${{ inputs.save-node-compile-cache == 'true' && github.event_name != 'pull_request' && 'true' || 'false' }}
208+
209+ - name : Restore and save Node compile cache
210+ 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'
211+ uses : actions/cache@27d5ce7f107fe9357f9df03efb73ab90386fccae # v5
212+ with :
213+ path : /var/tmp/openclaw-node-compile-cache
214+ 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 }}
215+ restore-keys : |
216+ ${{ 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 }}-
217+
218+ - name : Restore Node compile cache
219+ 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'
220+ uses : actions/cache/restore@27d5ce7f107fe9357f9df03efb73ab90386fccae # v5
221+ with :
222+ path : /var/tmp/openclaw-node-compile-cache
223+ 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 }}
224+ restore-keys : |
225+ ${{ 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 }}-
226+
227+ - name : Configure Node compile cache
228+ if : inputs.node-compile-cache == 'true' && runner.os != 'Windows'
229+ env :
230+ 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' }}
231+ shell : bash
232+ run : |
233+ set -euo pipefail
234+ cache_root=/var/tmp/openclaw-node-compile-cache
235+ mkdir -p "$cache_root"
236+ echo "NODE_COMPILE_CACHE=$cache_root" >> "$GITHUB_ENV"
237+ echo "NODE_COMPILE_CACHE_PORTABLE=1" >> "$GITHUB_ENV"
238+ echo "OPENCLAW_NODE_COMPILE_CACHE_WRITER=$CACHE_WRITER" >> "$GITHUB_ENV"
146239
147240 # Post actions run last-in-first-out. Register after stickydisk so this bind
148241 # is gone before stickydisk flushes, unmounts, and snapshots its filesystem.
0 commit comments