@invarn/cibuild 1.9.2 → 1.9.3
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"cache.d.ts","sourceRoot":"","sources":["../../../../src/yaml/steps/cache.ts"],"names":[],"mappings":"AAAA;;GAEG;AAEH,OAAO,EAAE,gBAAgB,EAAE,MAAM,WAAW,CAAC;AAC7C,OAAO,KAAK,EAAE,OAAO,EAAE,QAAQ,EAAE,MAAM,gBAAgB,CAAC;AAkBxD;;;;;;GAMG;AACH,MAAM,WAAW,WAAW;IAC1B,4HAA4H;IAC5H,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,sHAAsH;IACtH,WAAW,CAAC,EAAE,MAAM,EAAE,CAAC;IACvB,SAAS,EAAE,MAAM,CAAC;IAClB,KAAK,EAAE,MAAM,EAAE,CAAC;IAChB,yGAAyG;IACzG,QAAQ,CAAC,EAAE,MAAM,CAAC;CACnB;AAED,eAAO,MAAM,aAAa,EAAE,MAAM,CAAC,MAAM,EAAE,WAAW,CAUrD,CAAC;
|
|
1
|
+
{"version":3,"file":"cache.d.ts","sourceRoot":"","sources":["../../../../src/yaml/steps/cache.ts"],"names":[],"mappings":"AAAA;;GAEG;AAEH,OAAO,EAAE,gBAAgB,EAAE,MAAM,WAAW,CAAC;AAC7C,OAAO,KAAK,EAAE,OAAO,EAAE,QAAQ,EAAE,MAAM,gBAAgB,CAAC;AAkBxD;;;;;;GAMG;AACH,MAAM,WAAW,WAAW;IAC1B,4HAA4H;IAC5H,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,sHAAsH;IACtH,WAAW,CAAC,EAAE,MAAM,EAAE,CAAC;IACvB,SAAS,EAAE,MAAM,CAAC;IAClB,KAAK,EAAE,MAAM,EAAE,CAAC;IAChB,yGAAyG;IACzG,QAAQ,CAAC,EAAE,MAAM,CAAC;CACnB;AAED,eAAO,MAAM,aAAa,EAAE,MAAM,CAAC,MAAM,EAAE,WAAW,CAUrD,CAAC;AAmFF;;GAEG;AACH,MAAM,WAAW,eAAe;IAC9B,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,WAAW,CAAC,EAAE,MAAM,EAAE,CAAC;IACvB,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,aAAa,CAAC,EAAE,OAAO,CAAC;CACzB;AAED;;GAEG;AACH,MAAM,WAAW,eAAe;IAC9B,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,WAAW,CAAC,EAAE,MAAM,EAAE,CAAC;IACvB,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,aAAa,CAAC,EAAE,OAAO,CAAC;IACxB,qBAAqB,CAAC,EAAE,OAAO,CAAC;CACjC;AAED;;;GAGG;AACH,qBAAa,qBAAsB,SAAQ,gBAAgB;IACnD,OAAO,CAAC,MAAM,EAAE,eAAe,EAAE,GAAG,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,EAAE,MAAM,EAAE,QAAQ,GAAG,OAAO,CAAC,OAAO,CAAC;YA0GzF,iBAAiB;CA8LhC;AAED;;;GAGG;AACH,qBAAa,qBAAsB,SAAQ,gBAAgB;IACnD,OAAO,CAAC,MAAM,EAAE,eAAe,EAAE,GAAG,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,EAAE,MAAM,EAAE,QAAQ,GAAG,OAAO,CAAC,OAAO,CAAC;YA4JzF,iBAAiB;CA2IhC"}
|
|
@@ -86,6 +86,26 @@ function resolvePresetChain(technology) {
|
|
|
86
86
|
}
|
|
87
87
|
return chain;
|
|
88
88
|
}
|
|
89
|
+
/**
|
|
90
|
+
* Emits the daemon exact-key pull branch (ADR 0001, Phase 3b). On a local
|
|
91
|
+
* miss, GET `<key>.tar.zst` from this runner's cache daemon ($CIBUILD_CACHE_DAEMON,
|
|
92
|
+
* injected by the runner). The daemon fans out to tailnet peers, persists the
|
|
93
|
+
* hit to ~/cache, and streams it back — so we pipe straight to extraction
|
|
94
|
+
* without re-writing it on the guest. The optional per-build bearer token
|
|
95
|
+
* ($CIBUILD_CACHE_TOKEN) is sent only when set. Inert when the env var is
|
|
96
|
+
* empty, so the same script runs with or without a daemon.
|
|
97
|
+
*/
|
|
98
|
+
function emitDaemonPullBranch() {
|
|
99
|
+
return [
|
|
100
|
+
'elif [ -n "$CIBUILD_CACHE_DAEMON" ] && curl -fsS ${CIBUILD_CACHE_TOKEN:+-H "Authorization: Bearer $CIBUILD_CACHE_TOKEN"} "$CIBUILD_CACHE_DAEMON/cache/$CACHE_KEY.tar.zst" | zstd -dc | tar -xf - -C /; then',
|
|
101
|
+
' echo "Cache found (daemon), extracting..."',
|
|
102
|
+
// LRU bump the tarball the daemon just persisted to ~/cache (same dir as
|
|
103
|
+
// the --dir mount); tolerate its absence in case the daemon served it
|
|
104
|
+
// without a local copy.
|
|
105
|
+
' touch "$CACHE_FILE" 2>/dev/null || true',
|
|
106
|
+
' echo "CACHE_SOURCE=daemon CACHE_KEY=$CACHE_KEY"',
|
|
107
|
+
];
|
|
108
|
+
}
|
|
89
109
|
/**
|
|
90
110
|
* Cache pull step executor
|
|
91
111
|
* Restores cached files from cache directory
|
|
@@ -133,7 +153,8 @@ export class CachePullStepExecutor extends BaseStepExecutor {
|
|
|
133
153
|
// Generate cache file name based on cache key
|
|
134
154
|
commands.push('');
|
|
135
155
|
commands.push('# Generate cache file path');
|
|
136
|
-
commands.push(`
|
|
156
|
+
commands.push(`CACHE_KEY="${this.escapeBash(cacheKey)}"`);
|
|
157
|
+
commands.push('CACHE_FILE="$CACHE_DIR/$CACHE_KEY.tar.zst"');
|
|
137
158
|
if (isDebugMode) {
|
|
138
159
|
commands.push('echo "Debug mode enabled"');
|
|
139
160
|
commands.push('echo "Cache file: $CACHE_FILE"');
|
|
@@ -155,6 +176,8 @@ export class CachePullStepExecutor extends BaseStepExecutor {
|
|
|
155
176
|
}
|
|
156
177
|
commands.push(' zstd -dc "$CACHE_FILE" | tar -xf - -C /');
|
|
157
178
|
commands.push(' echo "CACHE_SOURCE=local"');
|
|
179
|
+
// Daemon hit (ADR 0001) — see emitDaemonPullBranch.
|
|
180
|
+
commands.push(...emitDaemonPullBranch());
|
|
158
181
|
// Peer fallback
|
|
159
182
|
if (peerCacheDir) {
|
|
160
183
|
commands.push('elif [ -f "$PEER_CACHE_FILE" ] 2>/dev/null; then');
|
|
@@ -257,6 +280,13 @@ export class CachePullStepExecutor extends BaseStepExecutor {
|
|
|
257
280
|
// hit daily would still be reaped on its 31st day from creation.
|
|
258
281
|
commands.push(' touch "$CACHE_FILE"');
|
|
259
282
|
commands.push(' echo "CACHE_SOURCE=local CACHE_KEY=$CACHE_KEY"');
|
|
283
|
+
// 1b. Daemon hit (ADR 0001) — on a local miss, ask this runner's cache
|
|
284
|
+
// daemon for the exact key. The daemon resolves it from a tailnet peer
|
|
285
|
+
// and atomically persists it to ~/cache (the same dir as the --dir mount),
|
|
286
|
+
// so we stream the response straight to extraction with no redundant
|
|
287
|
+
// guest-side write. Gated at runtime on the env var the runner injects, so
|
|
288
|
+
// the generated script is inert when no daemon is configured.
|
|
289
|
+
commands.push(...emitDaemonPullBranch());
|
|
260
290
|
// 2. Peer hit — tee to warm local cache while extracting in one pass
|
|
261
291
|
if (peerCacheDir) {
|
|
262
292
|
commands.push('elif [ -f "$PEER_CACHE_FILE" ] 2>/dev/null; then');
|
|
@@ -268,6 +268,42 @@ describe('Step Implementations', () => {
|
|
|
268
268
|
expect(result.script).toContain('exceeds 30d cap — going cold');
|
|
269
269
|
expect(result.script).toContain('FALLBACK_REJECTED=');
|
|
270
270
|
});
|
|
271
|
+
test('routes a preset pull miss through the cache daemon when CIBUILD_CACHE_DAEMON is set', async () => {
|
|
272
|
+
const executor = new CachePullStepExecutor();
|
|
273
|
+
const result = await executor.execute({ technology: 'kmm' }, {}, testConfig);
|
|
274
|
+
// Daemon branch is gated at runtime on the injected env var (so the same
|
|
275
|
+
// generated script is a no-op when the runner doesn't inject a daemon).
|
|
276
|
+
expect(result.script).toContain('[ -n "$CIBUILD_CACHE_DAEMON" ]');
|
|
277
|
+
// Exact-key GET against the per-runner daemon.
|
|
278
|
+
expect(result.script).toContain('"$CIBUILD_CACHE_DAEMON/cache/$CACHE_KEY.tar.zst"');
|
|
279
|
+
// Stream-extract: the daemon already persisted the tarball to ~/cache via
|
|
280
|
+
// resolveMiss, so we pipe straight to extraction with no redundant write.
|
|
281
|
+
expect(result.script).toContain('CACHE_SOURCE=daemon CACHE_KEY=$CACHE_KEY');
|
|
282
|
+
});
|
|
283
|
+
test('sends the per-build bearer token only when CIBUILD_CACHE_TOKEN is set', async () => {
|
|
284
|
+
const executor = new CachePullStepExecutor();
|
|
285
|
+
const result = await executor.execute({ technology: 'kmm' }, {}, testConfig);
|
|
286
|
+
// ${VAR:+...} expands the -H flag only when the token is non-empty, so an
|
|
287
|
+
// unauthenticated daemon isn't sent a bogus "Bearer " header.
|
|
288
|
+
expect(result.script).toContain('${CIBUILD_CACHE_TOKEN:+-H "Authorization: Bearer $CIBUILD_CACHE_TOKEN"}');
|
|
289
|
+
});
|
|
290
|
+
test('daemon branch is an elif after the local hit (exact-key, daemon, fallback order)', async () => {
|
|
291
|
+
const executor = new CachePullStepExecutor();
|
|
292
|
+
const result = await executor.execute({ technology: 'kmm' }, {}, testConfig);
|
|
293
|
+
const localAt = result.script.indexOf('CACHE_SOURCE=local');
|
|
294
|
+
const daemonAt = result.script.indexOf('CACHE_SOURCE=daemon');
|
|
295
|
+
const fallbackAt = result.script.indexOf('__ci_fb_scope=');
|
|
296
|
+
expect(localAt).toBeGreaterThanOrEqual(0);
|
|
297
|
+
expect(daemonAt).toBeGreaterThan(localAt);
|
|
298
|
+
expect(fallbackAt).toBeGreaterThan(daemonAt);
|
|
299
|
+
});
|
|
300
|
+
test('routes a manual cache_key pull miss through the cache daemon', async () => {
|
|
301
|
+
const executor = new CachePullStepExecutor();
|
|
302
|
+
const result = await executor.execute({ cache_key: 'my-key' }, {}, testConfig);
|
|
303
|
+
expect(result.script).toContain('[ -n "$CIBUILD_CACHE_DAEMON" ]');
|
|
304
|
+
expect(result.script).toContain('"$CIBUILD_CACHE_DAEMON/cache/$CACHE_KEY.tar.zst"');
|
|
305
|
+
expect(result.script).toContain('CACHE_SOURCE=daemon CACHE_KEY=$CACHE_KEY');
|
|
306
|
+
});
|
|
271
307
|
});
|
|
272
308
|
describe('CachePushStepExecutor', () => {
|
|
273
309
|
test('should generate cache push script', async () => {
|
|
@@ -283,6 +319,15 @@ describe('Step Implementations', () => {
|
|
|
283
319
|
expect(result.script).toContain('node-modules-v1');
|
|
284
320
|
expect(result.script).toContain('.ci-cache');
|
|
285
321
|
});
|
|
322
|
+
test('stays a local write — never PUTs to the cache daemon (ADR 0001 v1 scope)', async () => {
|
|
323
|
+
const executor = new CachePushStepExecutor();
|
|
324
|
+
const manual = await executor.execute({ cache_key: 'my-key', cache_paths: ['build'] }, {}, testConfig);
|
|
325
|
+
const preset = await executor.execute({ technology: 'kmm' }, {}, testConfig);
|
|
326
|
+
for (const result of [manual, preset]) {
|
|
327
|
+
expect(result.script).not.toContain('CIBUILD_CACHE_DAEMON');
|
|
328
|
+
expect(result.script).not.toContain('CIBUILD_CACHE_TOKEN');
|
|
329
|
+
}
|
|
330
|
+
});
|
|
286
331
|
test('should skip gracefully when cache_key is missing', async () => {
|
|
287
332
|
const executor = new CachePushStepExecutor();
|
|
288
333
|
const inputs = {
|