mirror of
https://github.com/garrytan/gstack.git
synced 2026-11-10 10:40:32 +01:00
Merge commit '0c84f4ee14c32734313e68b700417e0748ad1d55' into capy/gst620-sealed-identifiers
Co-authored-by: capy-ai[bot] <230910855+capy-ai[bot]@users.noreply.github.com> # Conflicts: # test/helpers/free-fixtures-data.ts
This commit is contained in:
commit
c0d1482b05
568 files changed
+36850
-8414
No files matched your search
+12
-30
@@ -4,7 +4,7 @@
|
||||
*
|
||||
* gstack-pregate [run] [--stage preflight|tests|all] [--only <check,..>] [--base <branch>]
|
||||
* [--allow-repo-commands] [--policy-from base|head] [--out <pregate.json>]
|
||||
* [--timeout <s>] [--stall-timeout <s>] [--remote --spend-cap <usd>] [--run <id>] [--explain] [--json]
|
||||
* [--timeout <s>] [--stall-timeout <s>] [--remote --spend-cap <usd>] [--run <id>] [--from-run <id|url>] [--explain] [--json]
|
||||
* gstack-pregate verify [--out <pregate.json>] [--json] publication check: current tree, no fail/incomplete, obligations cleared
|
||||
* gstack-pregate clear --remote <workflow.yml/job> --run-url <url> | --run <id|url> [--run …] [--allow-repo-commands] | --bundle-lane <label> | --receipt <file> [--out <pregate.json>]
|
||||
* gstack-pregate lanes [--explain] the lane list and platform tiers read from .github/workflows
|
||||
@@ -27,17 +27,15 @@ import { spawnSync } from 'node:child_process';
|
||||
import * as fs from 'node:fs';
|
||||
import * as path from 'node:path';
|
||||
import { EXIT, exitTableHelp, formatResult, type PregateFile } from '../lib/headless-artifacts';
|
||||
import { PREGATE_CONFIG_PATH, explainConfig, isConfigUnavailable, loadConfigFromBase, loadConfigFromHead } from '../lib/pregate/config';
|
||||
import { PREGATE_CONFIG_PATH, explainConfig, loadConfigFromHead } from '../lib/pregate/config';
|
||||
import { describeDraft, draftConfig } from '../lib/pregate/init-draft';
|
||||
import { buildRunCheck } from '../lib/pregate/run-check';
|
||||
import { buildContext, defaultUserConfigPath, describeRefusal, loadedDotenvFiles } from '../lib/pregate/context';
|
||||
import { CHECKS, CHECK_IDS, PREGATE_OUT_REL, PREGATE_TIER2, clearDecision, clearObligation, readPregateFile, renderTable, runPregate, verifyPregate, writePregateFile, type RunCheck } from '../lib/pregate/index';
|
||||
import { ghRunDeps, runInventory } from '../lib/run-evidence';
|
||||
import { wtreeFingerprint } from '../lib/script-invocation';
|
||||
import { CHECKS, CHECK_IDS, PREGATE_OUT_REL, PREGATE_TIER2, clearDecision, clearObligation, readPregateFile, renderTable, runPregate, verifyPregate, writePregateFile } from '../lib/pregate/index';
|
||||
import { describeLane, discoverLanes } from '../lib/pregate/workflows';
|
||||
import { REMOTE_GATE_LANE } from '../lib/pregate/checks/lanes';
|
||||
import { describeSubstitution } from '../lib/pregate/remote-receipt';
|
||||
import { ensureTmpExcluded } from '../lib/restamp';
|
||||
import { normalizeRemoteUrl } from '../lib/ship-queue-train-record';
|
||||
import { RESULT_CODES, resultCodeSuffix, resultLine } from '../lib/result-codes';
|
||||
import { DEFAULT_STALL_TIMEOUT_S } from '../lib/pregate/context';
|
||||
import { cancelActive } from '../lib/pregate/supervise';
|
||||
@@ -90,6 +88,9 @@ const HELP = `gstack-pregate — cheap checks before anything paid or remote (do
|
||||
spent, reserved and unknown
|
||||
--run <id> the run label recorded in pregate.json and the name of this run's scratch directory (a taken name gets a numbered sibling, <id>-2 … -9)
|
||||
(<state root>/projects/<slug>/pregate/runs/<id>: lane and guard logs, scan files; default a unique id)
|
||||
--from-run <id|url> a Free Tests run on this exact tree, verified against the base branch's run profile for this OS
|
||||
whose workflow holds a local lane (docs/pregate.md#from-run): the selected test files its tree.json lists count as passed and
|
||||
are recorded with its receipt; the rest still run. A refused run prints REUSE: refused and runs everything
|
||||
--explain print each selected test's provenance and each uncertain mapping
|
||||
--json one JSON object on stdout; a refusal before any check runs (unavailable base, invalid pin
|
||||
file, WTREE_UNAVAILABLE) is {ok:false,status:'refused',code,detail,fix,docs} with exit 3,
|
||||
@@ -108,7 +109,7 @@ function flag(args: string[], name: string): string | undefined { const i = args
|
||||
|
||||
/** Flags each verb accepts: `[flags that take a value, bare flags]`. Anything else is usage (exit 2), never a silent default run. */
|
||||
const VERB_FLAGS: Record<string, [string[], string[]]> = {
|
||||
run: [['--stage', '--only', '--base', '--policy-from', '--out', '--timeout', '--stall-timeout', '--spend-cap', '--run'], ['--allow-repo-commands', '--explain', '--json', '--remote']],
|
||||
run: [['--stage', '--only', '--base', '--policy-from', '--out', '--timeout', '--stall-timeout', '--spend-cap', '--run', '--from-run'], ['--allow-repo-commands', '--explain', '--json', '--remote']],
|
||||
verify: [['--out'], ['--json']],
|
||||
clear: [['--remote', '--run-url', '--run', '--bundle-lane', '--receipt', '--out'], ['--allow-repo-commands']],
|
||||
lanes: [[], ['--explain', '--json']],
|
||||
@@ -141,6 +142,8 @@ function outPath(root: string, rest: string[]): string { const o = flag(rest, '-
|
||||
async function cmdRun(root: string, rest: string[], json: boolean): Promise<number> {
|
||||
const stage = (flag(rest, '--stage') ?? 'all') as 'preflight' | 'tests' | 'all';
|
||||
if (!['preflight', 'tests', 'all'].includes(stage)) { process.stderr.write('gstack-pregate: --stage needs preflight, tests or all\n'); return EXIT.usage; }
|
||||
const fromRun = flag(rest, '--from-run');
|
||||
if (fromRun !== undefined && stage === 'preflight') { process.stderr.write('gstack-pregate: --from-run applies to the tests stage (--stage tests or all)\n'); return EXIT.usage; }
|
||||
const only = flag(rest, '--only')?.split(',').map(s => s.trim()).filter(Boolean);
|
||||
const unknown = (only ?? []).filter(id => !CHECK_IDS.includes(id));
|
||||
if (unknown.length) { process.stderr.write(`gstack-pregate: unknown check(s) ${unknown.join(', ')}${unknown.some(u => (PREGATE_TIER2 as readonly string[]).includes(u)) ? ' (tier 2, not in this gstack)' : ''}; known: ${CHECK_IDS.join(', ')}\n`); return EXIT.usage; }
|
||||
@@ -172,6 +175,7 @@ async function cmdRun(root: string, rest: string[], json: boolean): Promise<numb
|
||||
return EXIT.refused;
|
||||
}
|
||||
const ctx = loaded.ctx;
|
||||
if (fromRun !== undefined) ctx.fromRun = { ref: fromRun, check: buildRunCheck(root, null, allow, base) };
|
||||
ensureTmpExcluded(root);
|
||||
for (const signal of ['SIGINT', 'SIGTERM'] as const) {
|
||||
process.once(signal, () => {
|
||||
@@ -228,7 +232,7 @@ function cmdClear(root: string, rest: string[]): number {
|
||||
const runs = rest.flatMap((a, i) => a === '--run' ? [rest[i + 1]!] : []);
|
||||
const runUrl = flag(rest, '--run-url');
|
||||
const bundleLane = flag(rest, '--bundle-lane');
|
||||
const checked = runs.length || runUrl || bundleLane ? runCheck(root, lane, rest.includes('--allow-repo-commands')) : undefined;
|
||||
const checked = runs.length || runUrl || bundleLane ? buildRunCheck(root, lane, rest.includes('--allow-repo-commands')) : undefined;
|
||||
if (checked && 'error' in checked && (runs.length || checked.profiled)) { process.stderr.write(`gstack-pregate clear: ${checked.error}${checked.profiled ? `; ${lane} may have a run profile on the base branch, so it clears only with a verified --run` : ''}\n`); return EXIT.fail; }
|
||||
const evidence = checked && !('error' in checked) ? checked : undefined;
|
||||
const decision = clearDecision(read.file, lane, { runUrl, runs, bundleLane, receiptPath: flag(rest, '--receipt'), evidence }, root);
|
||||
@@ -248,28 +252,6 @@ function cmdClear(root: string, rest: string[]): number {
|
||||
return EXIT.ok;
|
||||
}
|
||||
|
||||
/** The run-evidence inputs for `clear --run`: the base branch's run profiles, this repository on GitHub, and the gate's inventory command (a pin-file command, so only with --allow-repo-commands). */
|
||||
function runCheck(root: string, lane: string, allowRepoCommands: boolean): RunCheck | { error: string; profiled?: boolean } {
|
||||
const base = resolveBaseBranch(root);
|
||||
const loaded = loadConfigFromBase(root, base);
|
||||
// Unknown profiles are treated as present: a lane is cleared unverified only once the base shows it has no profile.
|
||||
if (isConfigUnavailable(loaded)) return { error: `the base branch's ${PREGATE_CONFIG_PATH} is unreadable (${loaded.detail}); run profiles come only from the base`, profiled: true };
|
||||
if (loaded.errors.length) return { error: `the base branch's ${PREGATE_CONFIG_PATH} is invalid: ${loaded.errors.map(e => `${e.path}: ${e.message}`).join('; ')}`, profiled: true };
|
||||
const repo = /^https:\/\/github\.com\/(.+)$/.exec(normalizeRemoteUrl(gitOut(root, ['config', '--get', 'remote.origin.url']) ?? ''))?.[1];
|
||||
if (!repo) return { error: 'origin is not a github.com repository; run evidence is read from GitHub Actions', profiled: lane === REMOTE_GATE_LANE ? !!loaded.config.remote_gate_runs : !!loaded.config.run_profiles[lane] };
|
||||
return {
|
||||
repo, deps: ghRunDeps(repo, root), profiles: loaded.config.run_profiles, gateRuns: loaded.config.remote_gate_runs, now: new Date(),
|
||||
inventory: (command, tree) => {
|
||||
if (!allowRepoCommands) return { error: 'the inventory command is a pin-file command; pass --allow-repo-commands' };
|
||||
const at = () => { const fp = wtreeFingerprint({ cwd: root }); return 'tree' in fp ? fp.tree : null; };
|
||||
if (at() !== tree) return { error: `the working tree is not pregate.json's tree ${tree.slice(0, 12)}; the inventory must list that tree's gate files` };
|
||||
const listed = runInventory(command, root);
|
||||
if (at() !== tree) return { error: `the working tree changed while ${command} ran` };
|
||||
return listed;
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
function gitOut(root: string, args: string[]): string | null {
|
||||
const r = spawnSync('git', args, { cwd: root, encoding: 'utf8', timeout: 30_000 });
|
||||
return r.status === 0 ? r.stdout.trim() : null;
|
||||
|
||||
Reference in new issue
Block a user