mirror of
https://github.com/garrytan/gstack.git
synced 2026-11-05 16:34:00 +01:00
pregate: rename run-scratch module (strays rule) and map starter.ts to its test
Co-authored-by: capy-ai[bot] <230910855+capy-ai[bot]@users.noreply.github.com>
This commit is contained in:
1 parent
0232f78e91
commit
e9f9c217d9
9 files changed
+20
-9
No files matched your search
+1
-1
@@ -8,7 +8,7 @@
|
||||
- **No policy → warn plus a starter, never a hard `PREGATE_NO_LANE`** (`lib/pregate/starter.ts`, `lib/pregate/checks/lanes.ts`). When the base branch carries no `.gstack/pregate.json` and no user-owned policy exists, a touched file with no lane is a `warn` row under the new code `PREGATE_NO_POLICY`; the selected tests still run (a red lane is still `fail`, an unknown platform matrix is still a `requires-remote` obligation), and the table prints a starter policy drafted from the repository: the detected runner, the test directories, the CI lanes (naming a planner-computed matrix that needs `selection`), the JSON between `PREGATE_STARTER: begin` and `end` with a `dependencies` entry per unmapped touched file mapped to the tests nearest it or sharing its stem (never `[]`), and a `TODO` line per file nothing covers. `run` writes nothing; `gstack-pregate init` is the explicit write. Once any policy exists (base copy, user-owned file, or `--policy-from head`), an unmapped file is `PREGATE_NO_LANE` as before.
|
||||
- **`allowlist` in `.gstack/pregate.json`** (`lib/pregate/allowlist.ts`, `lib/pregate/config.ts`, `lib/pregate/checks/secrets.ts`): `[{ finding, paths?, pattern?, reason }]`, where `finding` is a gstack-redact finding id or one of the categories `pii`, `internal`, `hygiene`, `legal`; `paths` are contained globs; `pattern` is a regex matched at the finding's position in the added line and ending at a token boundary; one scope is required and `reason` always is (64 entries at most). The `secrets` row says `N allowlisted (<ids>)`, lists each allowlisted finding with its entry and reason, and records the entries' digest as `inputs.allowlist`. **`env.value` (a credential-named environment variable's real value in the added lines, #3449) and the whole `secret` category can never be allowlisted:** validation refuses the entry (`PREGATE_CONFIG_INVALID` at `allowlist[i].finding`) and the matcher never consults the list for `env.value`, so a stale or hand-edited policy cannot silence it. An unparsed scanner report keeps its exit-code verdict.
|
||||
- **Environment failures reported apart from diff failures** (`lib/pregate/environment.ts`; new code `PREGATE_ENVIRONMENT` with the troubleshooting anchor `pregate-environment`). A runner or guard binary missing from PATH (exit 127, `command not found`, a spawn `ENOENT`, a tool `not on PATH`), a network error or a quota refusal before any test was counted, a full disk, an unverified runtime or gstack's own scanner not finishing is `incomplete` with `cause: "environment"` and `environment: { kind, evidence }` in `pregate.json`, an `environment: <kind> — <evidence>` table line and a fix that says the diff is not at fault; `PREGATE_LANE_FAILED`, `PREGATE_GUARD_FAILED` and `PREGATE_SECRETS` carry `cause: "diff"`. The classifier is conservative: a local lane that exited non-zero is read from its output only when its runner reported no test summary; a counted failure is the diff's whatever its log says; a remote gate is environment only when it printed no `PREGATE_REMOTE:` line; stalls and timeouts keep their codes. `gstack-pregate verify` prints `PREGATE_ENVIRONMENT: <n> check(s) blocked by the environment (<kinds>), not by the diff` when every incomplete check is environment-caused and names the kinds beside `PREGATE_INCOMPLETE` otherwise, so a lane waives by cause, never by test name.
|
||||
- **One scratch directory per run under the state root** (`lib/pregate/scratch.ts`): `<state root>/projects/<slug>/pregate/runs/<run id>/` (0700; `--run <id>` names it, else `<UTC stamp>-<pid>-<4 hex>`) holds the secrets scan's added-line copies (deleted after the scan), `lanes/lanes.log`, `guards/<id>.log` and the remote gate's log and file list, never `os.tmpdir()`. `pregate.json` records it as `scratch`, the table prints `scratch: <dir>`, `--explain` names it; runs older than 7 days under the same project are pruned when a new run starts; an unwritable state root falls back to a private per-run directory under `os.tmpdir()` and says so.
|
||||
- **One scratch directory per run under the state root** (`lib/pregate/run-scratch.ts`): `<state root>/projects/<slug>/pregate/runs/<run id>/` (0700; `--run <id>` names it, else `<UTC stamp>-<pid>-<4 hex>`) holds the secrets scan's added-line copies (deleted after the scan), `lanes/lanes.log`, `guards/<id>.log` and the remote gate's log and file list, never `os.tmpdir()`. `pregate.json` records it as `scratch`, the table prints `scratch: <dir>`, `--explain` names it; runs older than 7 days under the same project are pruned when a new run starts; an unwritable state root falls back to a private per-run directory under `os.tmpdir()` and says so.
|
||||
|
||||
### Changed
|
||||
- `docs/pregate.md`: the `allowlist` row, the "No policy yet" paragraph, and three sections (allowlist, environment failures, scratch). `docs/troubleshooting.md`: `PREGATE_NO_POLICY` and `PREGATE_ENVIRONMENT` anchors; `PREGATE_NO_LANE` and `PREGATE_INCOMPLETE` point at them. `lib/headless-artifacts.ts`: the pregate schema accepts `cause`, `environment` and `scratch` (additive). `gstack-pregate --help` documents `--run`.
|
||||
|
||||
@@ -19,7 +19,7 @@ import type { Guard } from '../config';
|
||||
import { baseInputs, sha256 } from '../context';
|
||||
import { classifyEnvironment, environmentLine, environmentResult } from '../environment';
|
||||
import { launchRuntime } from '../runtime';
|
||||
import { checkScratch } from '../scratch';
|
||||
import { checkScratch } from '../run-scratch';
|
||||
import { readRange, supervise } from '../supervise';
|
||||
import type { EnvironmentCause } from '../types';
|
||||
import type { CheckDef, CheckResult, PregateContext } from '../types';
|
||||
|
||||
@@ -19,7 +19,7 @@ import * as path from 'node:path';
|
||||
import { baseInputs, sha256 } from '../context';
|
||||
import { PREGATE_CONFIG_PATH } from '../config';
|
||||
import { classifyEnvironment, environmentLine, environmentResult } from '../environment';
|
||||
import { checkScratch } from '../scratch';
|
||||
import { checkScratch } from '../run-scratch';
|
||||
import { starterPolicy } from '../starter';
|
||||
import { isDeviceDart, listRepoFiles, readRepoFile } from '../import-graph';
|
||||
import { selectLanes, type Selection } from '../lane-selection';
|
||||
|
||||
@@ -11,7 +11,7 @@ import { runScriptSync } from '../../script-invocation';
|
||||
import { allowlistDigest, applyAllowlist, describeAllowlisted, type AllowlistFinding } from '../allowlist';
|
||||
import { baseInputs, sha256 } from '../context';
|
||||
import { environmentResult } from '../environment';
|
||||
import { checkScratch } from '../scratch';
|
||||
import { checkScratch } from '../run-scratch';
|
||||
import type { CheckDef, CheckResult, PregateContext } from '../types';
|
||||
|
||||
const BIN_DIR = path.resolve(path.dirname(Bun.fileURLToPath(import.meta.url)), '..', '..', '..', 'bin');
|
||||
|
||||
@@ -21,7 +21,7 @@ import { policyUnavailableLine, resolveBaseBranch } from '../ship-policy';
|
||||
import { PREGATE_CONFIG_PATH, isConfigUnavailable, loadConfigFromBase, loadConfigFromHead, loadConfigFromUser, type ConfigError, type ConfigLoad, type PregateConfig } from './config';
|
||||
import type { CheckResult, PregateContext, TouchedFile } from './types';
|
||||
import { discoverLanes } from './workflows';
|
||||
import { createScratch, scratchRunId } from './scratch';
|
||||
import { createScratch, scratchRunId } from './run-scratch';
|
||||
import type { RegistryError } from '../regen';
|
||||
|
||||
export interface BuildContextOptions {
|
||||
@@ -179,7 +179,7 @@ export function defaultUserConfigPath(root: string): string {
|
||||
return path.join(resolveStateRoot(), 'projects', slugFromEnvironment(undefined, root), 'pregate.json');
|
||||
}
|
||||
|
||||
/** `<state root>/projects/<slug>/pregate/runs`: where every run's scratch directory lives (lib/pregate/scratch.ts), from the caller's environment. */
|
||||
/** `<state root>/projects/<slug>/pregate/runs`: where every run's scratch directory lives (lib/pregate/run-scratch.ts), from the caller's environment. */
|
||||
export function defaultScratchRoot(root: string, env: NodeJS.ProcessEnv = process.env): string {
|
||||
const stateRoot = resolveStateRoot(env);
|
||||
return path.join(stateRoot, 'projects', slugFromEnvironment(stateRoot, root), 'pregate', 'runs');
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/**
|
||||
* pregate/scratch — one scratch directory per run under the state root
|
||||
* pregate/run-scratch — one scratch directory per run under the state root
|
||||
* (Lane D, GSTA-112): `<state root>/projects/<slug>/pregate/runs/<run id>/`.
|
||||
* Every check keeps its working files there (the secrets scan's added-line
|
||||
* copies, the lane and guard logs, the remote gate's file list) instead of
|
||||
@@ -35,7 +35,7 @@ export interface PregateContext {
|
||||
configLabel: string;
|
||||
/** Where the policy came from: the base branch, the unreviewed head, the user-owned file, or nowhere (defaults). */
|
||||
configKind: 'base' | 'head' | 'user' | 'none';
|
||||
/** This run's scratch directory under the state root (lib/pregate/scratch.ts): logs and scan files, never os.tmpdir(). */
|
||||
/** This run's scratch directory under the state root (lib/pregate/run-scratch.ts): logs and scan files, never os.tmpdir(). */
|
||||
scratch: string;
|
||||
/** The policy file to edit: the user-owned path when it is the active policy, else `.gstack/pregate.json`. */
|
||||
configFile: string;
|
||||
|
||||
@@ -11,7 +11,7 @@ import * as fs from 'node:fs';
|
||||
import * as os from 'node:os';
|
||||
import * as path from 'node:path';
|
||||
import { EXIT } from '../lib/headless-artifacts';
|
||||
import { pruneScratch, scratchRunId } from '../lib/pregate/scratch';
|
||||
import { pruneScratch, scratchRunId } from '../lib/pregate/run-scratch';
|
||||
import { BASE_PREGATE, cleanup, commit, makeFixtureRepo, runBin, type FixtureRepo } from './helpers/pregate-fixture';
|
||||
|
||||
const repos: FixtureRepo[] = [];
|
||||
|
||||
@@ -13,6 +13,7 @@ import * as path from 'node:path';
|
||||
import { EXIT } from '../lib/headless-artifacts';
|
||||
import { validateConfig } from '../lib/pregate/config';
|
||||
import { CHECK_IDS } from '../lib/pregate/index';
|
||||
import { nearestTestsFor } from '../lib/pregate/starter';
|
||||
import { BASE_PREGATE, cleanup, commit, makeFixtureRepo, runBin, type FixtureRepo } from './helpers/pregate-fixture';
|
||||
|
||||
const repos: FixtureRepo[] = [];
|
||||
@@ -119,3 +120,13 @@ describe('no policy anywhere: warn plus a printed starter (never PREGATE_NO_LANE
|
||||
expect(BASE_PREGATE.runner).toBe('node --test {files}');
|
||||
});
|
||||
});
|
||||
|
||||
describe('nearestTestsFor: the nearest directory with tests, else the tests sharing the stem', () => {
|
||||
const tests = ['lib/render/a.test.ts', 'lib/render/b.test.ts', 'test/report.test.js', 'test/report-golden.test.js', 'test/reporter.test.js'];
|
||||
test('a file under a directory with tests takes those tests; one with none takes its stem; a stem with no match is empty', () => {
|
||||
expect(nearestTestsFor(tests, 'lib/render/draw.ts')).toEqual(['lib/render/a.test.ts', 'lib/render/b.test.ts']);
|
||||
expect(nearestTestsFor(tests, 'src/report.js')).toEqual(['test/report.test.js', 'test/report-golden.test.js']);
|
||||
expect(nearestTestsFor(tests, 'src/orphan.js')).toEqual([]);
|
||||
expect(nearestTestsFor(tests, 'README')).toEqual([]);
|
||||
});
|
||||
});
|
||||
Reference in new issue
Block a user