mirror of
https://github.com/garrytan/gstack.git
synced 2026-11-05 16:34:00 +01:00
fix(ship-queue): a PREGATE_ENVIRONMENT local record is final; no mirror stands in for it
Merging origin/mainb004c06(#3480) into this branch created a semantic conflict between two individually correct parents: #3480 made verifyPregate return PREGATE_ENVIRONMENT (instead of PREGATE_INCOMPLETE) when every incomplete check was caused by the environment, and this branch's by-tree mirror reader in ready() treated only PREGATE_LANE_FAILED and PREGATE_INCOMPLETE as final. Merged, a local record whose secrets scan never finished fell through to a same-tree tests-only mirror and ready passed without a secrets scan, against the rule this branch documents ("a record whose own checks failed or stayed incomplete is never overridden"). ready() now keeps a PREGATE_ENVIRONMENT record final as well. Regression in test/gstack-ship-queue.test.ts ("one READY block ..."): an environment-incomplete secrets record beside a passing same-tree mirror is refused with (PREGATE_ENVIRONMENT) and no "from the state root" suffix. Red on the bare merge28b64d7(expected exit 3, received 0), green with this fix. Found by the pass-4 delta review of the merge. Co-authored-by: capy-ai[bot] <230910855+capy-ai[bot]@users.noreply.github.com>
This commit is contained in:
1 parent
28b64d7115
commit
020bdefb41
2 files changed
+10
-3
No files matched your search
+3
-2
@@ -231,14 +231,15 @@ export function ready(ctx: QueueContext, o: { pr: string; pregate?: string; gate
|
||||
let pregateOk = false;
|
||||
const treeNow = wtreeFingerprint({ cwd: ctx.repoRoot });
|
||||
// The mirror stands in for a record that is missing, unreadable, recorded for another tree, or current but
|
||||
// waiting on a remote receipt another checkout holds; a record whose own checks failed or stayed incomplete is final.
|
||||
// waiting on a remote receipt another checkout holds; a record whose own checks failed or stayed incomplete (the diff's doing
|
||||
// or the environment's) is final.
|
||||
let useMirror = true;
|
||||
if (fs.existsSync(pregateFile)) {
|
||||
const read = readPregateFile(pregateFile);
|
||||
if (read.file) {
|
||||
const v = verifyPregate(ctx.repoRoot, read.file, treeNow);
|
||||
pregateOk = v.ok;
|
||||
useMirror = !v.ok && v.code !== 'PREGATE_LANE_FAILED' && v.code !== 'PREGATE_INCOMPLETE';
|
||||
useMirror = !v.ok && v.code !== 'PREGATE_LANE_FAILED' && v.code !== 'PREGATE_INCOMPLETE' && v.code !== 'PREGATE_ENVIRONMENT';
|
||||
pregateLine = `PREGATE: ${read.file.verdict} tree=${read.file.tree.slice(0, 12)} head=${(read.file.head ?? '').slice(0, 12)} checks=${read.file.checks.length}${v.ok ? '' : ` — ${v.message} (${v.code})`}`;
|
||||
} else pregateLine = `PREGATE: unreadable (${read.errors[0]?.message ?? 'invalid'})`;
|
||||
}
|
||||
|
||||
@@ -329,7 +329,8 @@ describe('ready', () => {
|
||||
expect([current.status, current.err]).toEqual([EXIT.ok, '']);
|
||||
expect(line(current.out, 'PREGATE:')).toBe(`PREGATE: pass tree=${movedTree.slice(0, 12)} head=${moved.slice(0, 12)} checks=1`);
|
||||
// Value: protects=a same-tree record that only waits on a remote receipt (the small-machine run) is cleared by the clone's mirror, an
|
||||
// unreadable record falls through to the mirror, and a record whose own checks failed or stayed incomplete is never overridden by a green mirror;
|
||||
// unreadable record falls through to the mirror, and a record whose own checks failed or stayed incomplete is never overridden by a green mirror,
|
||||
// including an incomplete check the environment caused (PREGATE_ENVIRONMENT, #3480: a secrets scanner that never finished is not a scanned diff);
|
||||
// fails_when=ready reads the mirror only for another tree (the documented lane case refuses PREGATE_REMOTE_UNCLEARED) or lets a mirror hide a red
|
||||
// or incomplete local run; why_new=the rows above cover only stale, missing and current records; seam=none; no_claim=the mirror's writer
|
||||
const local = path.join(repo.root, 'pregate-local.json');
|
||||
@@ -350,6 +351,11 @@ describe('ready', () => {
|
||||
const incompleteLocal = queue(repo, ['ready', '--pr', '1', '--pregate', local], { ...movedShim, GSTACK_STATE_ROOT: stateRoot });
|
||||
expect(incompleteLocal.status).toBe(EXIT.refused);
|
||||
expect(line(incompleteLocal.out, 'PREGATE:')).toMatch(/\(PREGATE_INCOMPLETE\)$/);
|
||||
fs.writeFileSync(local, JSON.stringify({ schema_version: 1, tree: movedTree, head: moved, verdict: 'incomplete', checks: [{ id: 'secrets', status: 'incomplete', cause: 'environment', environment: { kind: 'missing-binary', evidence: 'gstack-redact: not on PATH', proof: 'launch' } }] }));
|
||||
const environmentLocal = queue(repo, ['ready', '--pr', '1', '--pregate', local], { ...movedShim, GSTACK_STATE_ROOT: stateRoot });
|
||||
expect(environmentLocal.status).toBe(EXIT.refused);
|
||||
expect(line(environmentLocal.out, 'PREGATE:')).toMatch(/\(PREGATE_ENVIRONMENT\)$/);
|
||||
expect(environmentLocal.out).not.toContain(suffix);
|
||||
fs.appendFileSync(path.join(repo.work, 'pr-a.txt'), 'dirty\n');
|
||||
expect(queue(repo, ['ready', '--pr', '1'], shim(moved, green)).err).toMatch(/uncommitted tracked changes: pr-a\.txt.*\(QUEUE_NOT_READY\)/);
|
||||
}, 180_000);
|
||||
|
||||
Reference in new issue
Block a user