mirror of
https://github.com/garrytan/gstack.git
synced 2026-09-01 10:50:39 +02:00
test(ci): bind the three-way image-tag hashFiles() expressions
evals.yml, evals-periodic.yml, and ci-image.yml each compute the CI
image tag from hashFiles('.github/docker/Dockerfile.ci', 'bun.lock',
'patches/**') — synced by comment only (TODOS.md 'CI three-way
image-tag drift'). If one input list drifts, that workflow computes a
different tag for the same content: eval lanes silently rebuild the
image every run, or ci-image prebuilds a tag nobody looks up. The test
extracts each tag-computation site and fails on any mismatch.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
co-authored by
Claude Fable 5
parent
ace904d40a
commit
c8722b243d
@@ -0,0 +1,36 @@
|
||||
/**
|
||||
* The CI image tag is a content hash computed independently in three
|
||||
* workflows — evals.yml, evals-periodic.yml, ci-image.yml — and they were
|
||||
* synced by comment only (filed in TODOS.md as the "three-way image-tag
|
||||
* drift" gap). If one file's hashFiles() input list drifts, that workflow
|
||||
* computes a DIFFERENT tag for the same content: the eval lanes stop finding
|
||||
* the prebuilt image and silently rebuild it on every run (minutes per run,
|
||||
* no red check), or ci-image prebuilds a tag nobody looks up.
|
||||
*/
|
||||
import { describe, expect, test } from 'bun:test';
|
||||
import * as fs from 'node:fs';
|
||||
import * as path from 'node:path';
|
||||
|
||||
const ROOT = path.resolve(__dirname, '..');
|
||||
const FILES = ['evals.yml', 'evals-periodic.yml', 'ci-image.yml'];
|
||||
|
||||
function hashFilesCalls(name: string): string[] {
|
||||
const source = fs.readFileSync(
|
||||
path.join(ROOT, '.github', 'workflows', name), 'utf-8');
|
||||
// Only tag-computation sites: hashFiles() inside a `tag=` output line.
|
||||
return [...source.matchAll(/tag=[^\n]*?(hashFiles\([^)]*\))/g)].map((m) => m[1]);
|
||||
}
|
||||
|
||||
describe('ci image tag binding', () => {
|
||||
test('all three workflows compute the tag from the identical hashFiles() input list', () => {
|
||||
const perFile = FILES.map((f) => ({ file: f, calls: hashFilesCalls(f) }));
|
||||
for (const { file, calls } of perFile) {
|
||||
// Each workflow computes the tag exactly once; zero means the scan
|
||||
// regex rotted (must fail loudly, not vacuously pass).
|
||||
expect(calls, `${file}: expected exactly one tag hashFiles() site`).toHaveLength(1);
|
||||
}
|
||||
const expressions = [...new Set(perFile.map((p) => p.calls[0]))];
|
||||
const detail = perFile.map((p) => `${p.file} → ${p.calls[0]}`).join('\n');
|
||||
expect(expressions, `image-tag hashFiles() drift:\n${detail}`).toHaveLength(1);
|
||||
});
|
||||
});
|
||||
Reference in New Issue
Block a user