mirror of
https://github.com/garrytan/gstack.git
synced 2026-11-09 02:08:19 +01:00
fix(ship): record-ci reuses its own CI row at the same tree, run and selection (GST-1795) (#4127)
* fix(ship): record-ci reuses its own CI row at the same tree, run and selection (GST-1795) Co-authored-by: capy-ai[bot] <230910855+capy-ai[bot]@users.noreply.github.com> * test(windows): curate ship-record-ci-reuse like its ship-bin-fixture siblings (run 42382484796) Co-authored-by: capy-ai[bot] <230910855+capy-ai[bot]@users.noreply.github.com> * fix: pre-landing review fixes record-ci reuse: the tamper table gains a row for a CI row whose clean flag is false, so dropping reusableCiRow's dirty guard fails a test. Review-Finding: bin/gstack-evidence:730:completeness-gaps reader=core Co-authored-by: capy-ai[bot] <230910855+capy-ai[bot]@users.noreply.github.com> * fix: pre-landing review fixes record-ci reuse stands only on the remainder runs its CI row was written from: a <lane>.skipped row newer than the CI row (a zero-test rerun record-ci refused) vetoes reuse instead of passing on exit 0 alone. Review-Finding: p1/outside-voice#1 reader=outside-voice Review-Finding: p1/adversarial-native#1 reader=adversarial-native Co-authored-by: capy-ai[bot] <230910855+capy-ai[bot]@users.noreply.github.com> --------- Co-authored-by: capy-ai[bot] <230910855+capy-ai[bot]@users.noreply.github.com>
This commit is contained in:
1 parent
52d6c46957
commit
cefcb20a06
8 files changed
+208
-7
No files matched your search
+3
-2
@@ -206,7 +206,7 @@ export function importedTestLines(cwd: string, missing: string[], candidates: st
|
||||
* not-owed reason, or the last CARRY_NONE reason. A lane the cleared remote gate stood in for (--remote-gate-stands-in, GST-1688)
|
||||
* reads as its receipt, its count and why the CI carry refused.
|
||||
*/
|
||||
export function recordCiResult(stderr: string): { runs?: string; tests?: string; files?: boolean; notOwed?: string; here?: string; exception?: string; none?: string; diagnosis?: string[]; remoteGate?: string; standIn?: string } {
|
||||
export function recordCiResult(stderr: string): { runs?: string; tests?: string; files?: boolean; notOwed?: string; here?: string; exception?: string; reused?: string; none?: string; diagnosis?: string[]; remoteGate?: string; standIn?: string } {
|
||||
const lines = stderr.split('\n');
|
||||
const last = lines.findLast(x => x.startsWith('gstack-evidence: recorded label='));
|
||||
const gateRow = last && / tests_ran=(\d+) log=- source=remote_gate receipt=(.+)$/.exec(last);
|
||||
@@ -220,7 +220,8 @@ export function recordCiResult(stderr: string): { runs?: string; tests?: string;
|
||||
const got = / tests_ran=(\d+|n\/a)( unit=files)? log=- source=ci runs=(\S+)/.exec(lines[at]!);
|
||||
const here = / skipped_here=(\d+) tests_ran_here=(\S+)( not_owed_here=\d+)?$/.exec(lines[at]!);
|
||||
const notOwed = lines.slice(at + 1).find(x => x.startsWith('gstack-evidence: not owed: '))?.slice('gstack-evidence: not owed: '.length);
|
||||
return { runs: got?.[3], tests: got?.[1], files: !!got?.[2], ...(notOwed ? { notOwed } : {}), ...(here ? { here: `skipped_here=${here[1]} file(s) tests_ran_here=${here[2]}${here[3] ?? ''}` } : {}), ...(exception ? { exception } : {}) };
|
||||
const reused = lines.slice(at + 1).map(x => /^CI_RECORD_REUSED: .* tree=([0-9a-f]+) /.exec(x)?.[1]).find(Boolean);
|
||||
return { runs: got?.[3], tests: got?.[1], files: !!got?.[2], ...(notOwed ? { notOwed } : {}), ...(here ? { here: `skipped_here=${here[1]} file(s) tests_ran_here=${here[2]}${here[3] ?? ''}` } : {}), ...(exception ? { exception } : {}), ...(reused ? { reused } : {}) };
|
||||
}
|
||||
|
||||
/** The per_file runner command for `files`: `{files}` replaced by the paths, each anchored with ./ (Bun reads a bare relative path as a substring filter) and single-quoted for bash. */
|
||||
|
||||
@@ -204,7 +204,7 @@ export function carryLane(cwd: string, base: string, l: { label: string; command
|
||||
const rec = bin(cwd, 'gstack-evidence', ['record-ci', '--label', l.label, '--base', base, '--remote-gate-stands-in', '--', l.command], timeoutS * 1000);
|
||||
const got = recordCiResult(rec.stderr);
|
||||
if (rec.status === 0 && got.remoteGate) return { label: l.label, exit: 0, testsRan: got.tests!, log: '-', remoteGate: got.remoteGate, standIn: got.standIn ?? 'not carried' };
|
||||
if (rec.status === 0 && got.runs) return { label: l.label, exit: 0, testsRan: got.files ? `${got.tests} (files)` : got.tests!, log: '-', carried: got.runs, ...(got.notOwed ? { notOwed: got.notOwed } : {}), ...(got.here ? { here: got.here } : {}), ...(got.exception ? { exception: got.exception } : {}) };
|
||||
if (rec.status === 0 && got.runs) return { label: l.label, exit: 0, testsRan: got.files ? `${got.tests} (files)` : got.tests!, log: '-', carried: got.runs, ...(got.notOwed ? { notOwed: got.notOwed } : {}), ...(got.here ? { here: got.here } : {}), ...(got.exception ? { exception: got.exception } : {}), ...(got.reused ? { reused: got.reused } : {}) };
|
||||
return { label: l.label, exit: null, testsRan: 'unknown', log: '-', carryNone: got.none ?? `record_failed: ${firstLine(rec.stderr)}`, ...(got.diagnosis ? { diagnosis: got.diagnosis } : {}) };
|
||||
}
|
||||
|
||||
@@ -245,7 +245,7 @@ export async function runLanes(cwd: string, list: { label: string; command: stri
|
||||
/** Step 5's lane verdict: a lane passes on its runner count, never exit 0 alone; a carried lane names its CI runs. */
|
||||
export function laneLine(l: LaneResult): { line: string; ok: boolean } {
|
||||
if (l.notOwed) return { line: `LANE: ${l.label} not owed: ${l.notOwed} run=${l.carried}`, ok: true };
|
||||
if (l.carried) return { line: `LANE: ${l.label} carried run=${l.carried} tests_ran=${l.testsRan}${l.here ? ` ${l.here}` : ''}`, ok: true };
|
||||
if (l.carried) return { line: `LANE: ${l.label} carried run=${l.carried} tests_ran=${l.testsRan}${l.here ? ` ${l.here}` : ''}${l.reused ? ` (reused the CI record at tree ${l.reused})` : ''}`, ok: true };
|
||||
if (l.remoteGate) return { line: `LANE: ${l.label} satisfied by remote_gate receipt=${l.remoteGate}`, ok: true };
|
||||
const zero = l.exit === 0 && l.testsRan === '0';
|
||||
const state = l.exit !== 0 ? 'FAIL' : zero ? 'ZERO-RUN' : l.testsRan === 'unknown' ? 'pass (count unavailable)' : 'pass';
|
||||
|
||||
Reference in new issue
Block a user