@invarn/cibuild 1.5.5 → 1.5.6
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;AA8DF;;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;YAsGzF,iBAAiB;
|
|
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;AA8DF;;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;YAsGzF,iBAAiB;CAsLhC;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"}
|
|
@@ -252,6 +252,10 @@ export class CachePullStepExecutor extends BaseStepExecutor {
|
|
|
252
252
|
commands.push(' echo "Cache file size: $(du -h "$CACHE_FILE" | cut -f1)"');
|
|
253
253
|
}
|
|
254
254
|
commands.push(' zstd -dc "$CACHE_FILE" | tar -xf - -C /');
|
|
255
|
+
// LRU bump: lift this tarball to the top of `ls -t` so retention's age
|
|
256
|
+
// cap counts last-used time, not last-written. Without this, a tarball
|
|
257
|
+
// hit daily would still be reaped on its 31st day from creation.
|
|
258
|
+
commands.push(' touch "$CACHE_FILE"');
|
|
255
259
|
commands.push(' echo "CACHE_SOURCE=local CACHE_KEY=$CACHE_KEY"');
|
|
256
260
|
// 2. Peer hit — tee to warm local cache while extracting in one pass
|
|
257
261
|
if (peerCacheDir) {
|
|
@@ -301,6 +305,10 @@ export class CachePullStepExecutor extends BaseStepExecutor {
|
|
|
301
305
|
commands.push(' echo "Fallback size: $(du -h "$__ci_fb" | cut -f1)"');
|
|
302
306
|
}
|
|
303
307
|
commands.push(' zstd -dc "$__ci_fb" | tar -xf - -C /');
|
|
308
|
+
// LRU bump for the fallback tarball — we restored from it, so it earns
|
|
309
|
+
// its place at the top of the retention sort even if the exact-key
|
|
310
|
+
// tarball is the one that gets created in this run's cache-push.
|
|
311
|
+
commands.push(' touch "$__ci_fb"');
|
|
304
312
|
commands.push(' echo "CACHE_SOURCE=fallback_$__ci_fb_src CACHE_KEY=$CACHE_KEY FALLBACK_KEY=$__ci_fb_key"');
|
|
305
313
|
commands.push(' else');
|
|
306
314
|
commands.push(' echo "Cache fallback candidate $__ci_fb_key is ${__ci_fb_age_days}d old, exceeds 30d cap — going cold"');
|
|
@@ -541,34 +549,48 @@ export class CachePushStepExecutor extends BaseStepExecutor {
|
|
|
541
549
|
commands.push('fi');
|
|
542
550
|
}
|
|
543
551
|
commands.push('');
|
|
544
|
-
//
|
|
545
|
-
|
|
546
|
-
|
|
547
|
-
|
|
552
|
+
// Always re-archive on success: ~/.gradle/caches and friends are
|
|
553
|
+
// write-through accumulators — same fingerprint key does NOT imply
|
|
554
|
+
// unchanged contents (Gradle's build-cache-1 keeps appending task
|
|
555
|
+
// outputs as sources change). Skipping here would freeze the tarball
|
|
556
|
+
// at first-mint and let it drift further from current state on every
|
|
557
|
+
// build. Atomic .tmp + mv overwrites any prior tarball under this key.
|
|
558
|
+
commands.push('if [ ${#PATHS_TO_CACHE[@]} -gt 0 ]; then');
|
|
548
559
|
commands.push(' echo "Caching ${#PATHS_TO_CACHE[@]} path(s)..."');
|
|
549
|
-
// Atomic write: compress to .tmp, then mv. No lock needed — runs are
|
|
550
|
-
// serialized per runner and cross-writer races resolve to last-writer-wins
|
|
551
|
-
// without corruption. shlock was flaky on the virtiofs cache mount.
|
|
552
560
|
commands.push(' tar -cf - "${PATHS_TO_CACHE[@]}" 2>/dev/null | zstd -3 > "$CACHE_FILE.tmp" && mv "$CACHE_FILE.tmp" "$CACHE_FILE" || true');
|
|
553
561
|
commands.push(' if [ -f "$CACHE_FILE" ]; then');
|
|
554
562
|
commands.push(' echo "Cache created successfully"');
|
|
555
563
|
if (isDebugMode) {
|
|
556
564
|
commands.push(' echo "Cache size: $(du -h "$CACHE_FILE" | cut -f1)"');
|
|
557
565
|
}
|
|
558
|
-
// Retention: keep
|
|
559
|
-
//
|
|
560
|
-
//
|
|
566
|
+
// Retention per-scope: (1) keep N newest by mtime — touched on hit in
|
|
567
|
+
// cache-pull so this is LRU, not strictly creation-order; (2) delete
|
|
568
|
+
// anything past the age cap regardless of position; (3) enforce a size
|
|
569
|
+
// budget, oldest-out. Two passes are required: the size pass needs the
|
|
570
|
+
// post-cleanup set, otherwise its running sum counts tarballs that the
|
|
571
|
+
// count/age pass is about to delete and over-evicts.
|
|
561
572
|
commands.push(' __ci_ret_scope="${CACHE_KEY%-*}"');
|
|
562
|
-
|
|
563
|
-
|
|
564
|
-
commands.push('
|
|
565
|
-
commands.push('
|
|
566
|
-
commands.push('
|
|
567
|
-
commands.push('
|
|
568
|
-
commands.push('
|
|
569
|
-
commands.push('
|
|
570
|
-
commands.push('
|
|
571
|
-
commands.push('
|
|
573
|
+
commands.push(' __ci_ret_count_cap=5');
|
|
574
|
+
commands.push(' __ci_ret_age_cap=$((30 * 86400))');
|
|
575
|
+
commands.push(' __ci_ret_size_cap_kb="${CIBUILD_CACHE_SCOPE_BUDGET_KB:-10485760}"');
|
|
576
|
+
commands.push(' __ci_ret_now=$(date +%s)');
|
|
577
|
+
commands.push(' __ci_ret_idx=0');
|
|
578
|
+
commands.push(' while IFS= read -r __ci_ret_f; do');
|
|
579
|
+
commands.push(' __ci_ret_idx=$((__ci_ret_idx + 1))');
|
|
580
|
+
commands.push(' __ci_ret_age=$(( __ci_ret_now - $(stat -f %m "$__ci_ret_f" 2>/dev/null || echo "$__ci_ret_now") ))');
|
|
581
|
+
commands.push(' if [ "$__ci_ret_idx" -gt "$__ci_ret_count_cap" ] || [ "$__ci_ret_age" -gt "$__ci_ret_age_cap" ]; then');
|
|
582
|
+
commands.push(' rm -f "$__ci_ret_f"');
|
|
583
|
+
commands.push(' fi');
|
|
584
|
+
commands.push(' done < <(ls -t "$CACHE_DIR"/"$__ci_ret_scope"-*.tar.zst 2>/dev/null)');
|
|
585
|
+
commands.push(' __ci_ret_running_kb=0');
|
|
586
|
+
commands.push(' while IFS= read -r __ci_ret_f; do');
|
|
587
|
+
commands.push(' __ci_ret_size=$(du -k "$__ci_ret_f" 2>/dev/null | cut -f1)');
|
|
588
|
+
commands.push(' __ci_ret_size=${__ci_ret_size:-0}');
|
|
589
|
+
commands.push(' __ci_ret_running_kb=$((__ci_ret_running_kb + __ci_ret_size))');
|
|
590
|
+
commands.push(' if [ "$__ci_ret_running_kb" -gt "$__ci_ret_size_cap_kb" ]; then');
|
|
591
|
+
commands.push(' rm -f "$__ci_ret_f"');
|
|
592
|
+
commands.push(' fi');
|
|
593
|
+
commands.push(' done < <(ls -t "$CACHE_DIR"/"$__ci_ret_scope"-*.tar.zst 2>/dev/null)');
|
|
572
594
|
commands.push(' else');
|
|
573
595
|
commands.push(' echo "Warning: Failed to create cache file"');
|
|
574
596
|
commands.push(' fi');
|
|
@@ -379,20 +379,21 @@ describe('Step Implementations', () => {
|
|
|
379
379
|
expect(result.script).toContain('> "$CACHE_FILE.tmp"');
|
|
380
380
|
expect(result.script).toContain('mv "$CACHE_FILE.tmp" "$CACHE_FILE"');
|
|
381
381
|
});
|
|
382
|
-
test('should sweep retention (
|
|
382
|
+
test('should sweep retention (count + age + size budget) after successful preset push', async () => {
|
|
383
383
|
const executor = new CachePushStepExecutor();
|
|
384
384
|
const result = await executor.execute({ technology: 'kmm' }, {}, testConfig);
|
|
385
|
-
// Scope = prefix + projectId; retention globs that scope and keeps 5 newest
|
|
386
385
|
expect(result.script).toContain('__ci_ret_scope="${CACHE_KEY%-*}"');
|
|
387
386
|
expect(result.script).toContain('ls -t "$CACHE_DIR"/"$__ci_ret_scope"-*.tar.zst');
|
|
388
|
-
expect(result.script).toContain('
|
|
387
|
+
expect(result.script).toContain('__ci_ret_count_cap=5');
|
|
388
|
+
expect(result.script).toContain('__ci_ret_age_cap=$((30 * 86400))');
|
|
389
|
+
expect(result.script).toContain('CIBUILD_CACHE_SCOPE_BUDGET_KB');
|
|
389
390
|
expect(result.script).toContain('rm -f "$__ci_ret_f"');
|
|
390
391
|
});
|
|
391
392
|
test('should not run retention for manual cache_key push (no scope)', async () => {
|
|
392
393
|
const executor = new CachePushStepExecutor();
|
|
393
394
|
const result = await executor.execute({ cache_key: 'my-key', cache_paths: ['build'] }, {}, testConfig);
|
|
394
395
|
expect(result.script).not.toContain('__ci_ret_scope');
|
|
395
|
-
expect(result.script).not.toContain('
|
|
396
|
+
expect(result.script).not.toContain('__ci_ret_count_cap');
|
|
396
397
|
});
|
|
397
398
|
test('should use atomic write (tmp + mv) for manual cache_key push', async () => {
|
|
398
399
|
const executor = new CachePushStepExecutor();
|