mirror of
https://github.com/garrytan/gstack.git
synced 2026-11-03 23:37:53 +01:00
gstack-evidence record-ci: recheck the working tree after the post-remainder run recheck (#3929 second fix-check)
Co-authored-by: capy-ai[bot] <230910855+capy-ai[bot]@users.noreply.github.com>
This commit is contained in:
1 parent
a8d2fee38c
commit
0f6ad9d2b6
3 files changed
+16
-1
No files matched your search
+1
-1
@@ -708,7 +708,6 @@ async function cmdRecordCi(argv: string[]): Promise<number> {
|
||||
const hereCommand = perFileCommand(m.per_file!, v.remainder);
|
||||
const exit = await cmdRun(["--label", hereLabel, "--", hereCommand]);
|
||||
if (exit !== 0) return none("skipped_failed", `the per_file run of ${v.remainder.length} skipped file(s) exited ${exit} (label=${hereLabel})`);
|
||||
if (wtree() !== tree) return none("tree_changed", `the working tree changed while the per_file run of label=${hereLabel} ran`);
|
||||
let rows: EvidenceRecord[] = [];
|
||||
try { rows = readJsonl<EvidenceRecord>(ledgerPath().file); } catch { rows = []; }
|
||||
here = { label: hereLabel, files: v.remainder, tests_ran: rows.findLast((r) => r.label === hereLabel && r.source === undefined && r.cmd_sha256 === sha256(hereCommand) && r.wtree === tree)?.tests_ran ?? "unknown" };
|
||||
@@ -717,6 +716,7 @@ async function cmdRecordCi(argv: string[]): Promise<number> {
|
||||
const changed = runChanged(check.deps, e.run_id, e.run_attempt);
|
||||
if (changed) return none("run_changed", changed);
|
||||
}
|
||||
if (wtree() !== tree) return none("tree_changed", `the working tree changed while the per_file run of label=${hereLabel} ran`);
|
||||
}
|
||||
let paths: ReturnType<typeof ledgerPath>;
|
||||
try {
|
||||
|
||||
@@ -10,6 +10,8 @@ export interface FakeWorld {
|
||||
/** Keyed `<run id>/<attempt>` for one attempt's jobs, else `<run id>` for every attempt. */
|
||||
jobs: Record<string, Array<{ name: string; conclusion: string | null; completed_at?: string }>>;
|
||||
trees: Record<string, Record<string, unknown>>;
|
||||
/** Once `marker` exists, the next run read writes `content` to `file` (a working-tree edit inside a run recheck). */
|
||||
editWhenArmed?: { marker: string; file: string; content: string };
|
||||
}
|
||||
|
||||
/** The stub body for shipFixture's gh(): run this file with the world beside it. */
|
||||
@@ -28,6 +30,8 @@ if (import.meta.main) {
|
||||
if ((m = /\/actions\/runs\/(\d+)$/.exec(route!))) {
|
||||
const run = world.runs.find(r => r.id === Number(m![1]));
|
||||
if (!run) { process.stderr.write('HTTP 404'); process.exit(1); }
|
||||
const e = world.editWhenArmed;
|
||||
if (e && fs.existsSync(e.marker)) fs.writeFileSync(e.file, e.content);
|
||||
process.stdout.write(JSON.stringify(run));
|
||||
} else if ((m = /\/actions\/workflows\/([^/]+)\/runs$/.exec(route!))) {
|
||||
lines(world.runs.filter(r => r.head_sha === q.get('head_sha') && r.path === `.github/workflows/${m![1]}`));
|
||||
|
||||
@@ -535,6 +535,17 @@ describe('GST-1190: a lane carries the covered files and runs only the skipped r
|
||||
expect(rows(f2).filter(r => r.source === 'ci')).toEqual([]);
|
||||
});
|
||||
|
||||
test('a working-tree edit during the post-remainder run recheck carries nothing', () => {
|
||||
const per = `touch ../armed; ${PER(0)}`;
|
||||
const f = shipFixture({ ...BASE, '.gstack/pregate.json': JSON.stringify({ run_profiles: { 'ci/free': PROFILE }, ship_lanes_from: { [FREE]: { profiles: ['ci/free'], inventory: "printf 'test/a.test.ts\\ntest/b.test.ts\\n'", per_file: per } } }, null, 2) + '\n' }); fixtures.push(f);
|
||||
world(f, { tree: { skipped_files: ['test/b.test.ts'] } });
|
||||
const w = JSON.parse(fs.readFileSync(path.join(f.root, 'world.json'), 'utf8'));
|
||||
fs.writeFileSync(path.join(f.root, 'world.json'), JSON.stringify({ ...w, editWhenArmed: { marker: path.join(f.root, 'armed'), file: path.join(f.work, 'src/b.ts'), content: 'export const b = 3;\n' } }));
|
||||
const rec = f.run(path.join(BIN, 'gstack-evidence'), ['record-ci', '--label', 'free', '--base', 'main', '--', FREE]);
|
||||
expect([rec.status, rec.stderr]).toEqual([1, expect.stringMatching(/^CARRY_NONE: tree_changed: /m)]);
|
||||
expect(rows(f).filter(r => r.source === 'ci')).toEqual([]);
|
||||
});
|
||||
|
||||
test('per_file is validated: it needs {files} and an inventory entry', async () => {
|
||||
const { shipLanesErrors } = await import('../lib/ci-carry');
|
||||
expect(shipLanesErrors({ x: { profiles: ['p'], inventory: 'ls', per_file: 'bun test {files}' } }, ['p'])).toEqual([]);
|
||||
|
||||
Reference in new issue
Block a user