FN-8506: run static checks in verify:fast
Run canonical pretest validators before test-free verification work. - Derive read-only static check steps from the root pretest script. - Test fail-fast static-check planning and execution. - Document the expanded verify:fast gate and correct changeset metadata. Files changed: .changeset/mobile-board-pointercancel-settle.md | 2 +- docs/testing.md | 5 +- scripts/__tests__/verify-fast.test.mjs | 105 ++++++++++++++++++++++-- scripts/verify-fast.mjs | 86 ++++++++++++++++--- 4 files changed, 174 insertions(+), 24 deletions(-) Fusion-Task-Id: FN-8506 Fusion-Task-Lineage: 87f74fda-1fd0-4e08-9bfe-51e0c4c9a31d Co-authored-by: Fusion (runfusion.ai) <noreply@runfusion.ai>
This commit is contained in:
@@ -2,6 +2,6 @@
|
||||
"@runfusion/fusion": patch
|
||||
---
|
||||
|
||||
summary: Fix mobile board column snapping — mid-screen rests, edge glitches, fling overshoot, false swipes from vertical card scrolls, and re-drags after tap-to-stop.
|
||||
summary: Fix mobile board drag settling, edge snap-back, and fling overshoot between columns.
|
||||
category: fix
|
||||
dev: useColumnScrollSnap now ignores pointercancel while the touch stream is live, settles to nearest-with-min-progress (resolveSettleTargetIndex), requires horizontal-dominant finger travel for pan intent, and lets a gesture begun mid-transit settle to plain nearest so a corrective drag wins.
|
||||
|
||||
@@ -33,7 +33,7 @@ Use the narrowest command that exercises the behavior you changed, then broaden
|
||||
pnpm test # gate suite + changed-only affected tests (bounded; never full-suite)
|
||||
pnpm test:gate # the merge gate: curated engine-core suite + CI-shape test
|
||||
pnpm smoke:boot # boot smoke: CLI --help + real serve /api/health
|
||||
pnpm verify:fast # TEST-FREE verification: artifact bootstrap + scoped typecheck/build + CLI build + boot smoke
|
||||
pnpm verify:fast # TEST-FREE: static check:* gates + bootstrap + scoped typecheck/build + CLI build + boot smoke
|
||||
pnpm test:full # full workspace suite — explicit opt-in only
|
||||
pnpm lint # lint all packages
|
||||
pnpm build # build workspace packages (excludes desktop/mobile; skips unchanged plugins safely)
|
||||
@@ -42,7 +42,8 @@ pnpm verify:workspace # deep opt-in verification: lint -> test:full -> build (N
|
||||
|
||||
<!-- FNXC:TestInfrastructure 2026-06-25-00:00: verify:fast is the opt-in test-free verification path. docs/testing.md observes the broad test gate caught no recalled real bugs while consuming ~70% of shipping time in flake triage; typecheck+build+boot-smoke gives deterministic, flake-free signal without running tests. It changes no default — pnpm test, the merge gate, and CI are untouched; the full suite stays available and runs non-blocking. -->
|
||||
<!-- FNXC:TestInfrastructure 2026-06-26-00:49: verify:fast must bootstrap missing workspace dist artifacts and build @runfusion/fusion even when the CLI package is not in the changed-package set because package builds and the boot smoke invoke source-checkout wrappers that require dist outputs in fresh worktrees. -->
|
||||
`pnpm verify:fast` (`scripts/verify-fast.mjs`) is the recommended **test-free verification** command: it bootstraps missing/stale workspace dist artifacts, runs **typecheck + build scoped to the changed packages** (reusing the same git-diff / changed-package resolution as `pnpm test`), always builds the `@runfusion/fusion` CLI package required by the source-checkout boot smoke, then runs the existing **boot smoke** once — and runs **no test suite**. It gives deterministic, flake-free signal in seconds, so it is a sound project `testCommand`/verification command when you want non-test verification. With no affected package (root/docs-only diff) it runs only the artifact bootstrap, CLI prerequisite build, and boot smoke. Each step is bounded by the shared `runWithWatchdog` (class `changed`) so a hang fails fast, and it exits nonzero on the first failing step. This is purely additive: it does not change `pnpm test`, the merge gate, or CI, and the full suite stays available (`pnpm test:full`, non-blocking on push to main).
|
||||
<!-- FNXC:TestInfrastructure 2026-07-22-12:00: Cheap deterministic policy gates must fail before verify:fast's expensive work. Read canonical package.json pretest commands and invoke their validator entry points directly so test-free verification and the merge gate cannot drift. -->
|
||||
`pnpm verify:fast` (`scripts/verify-fast.mjs`) is the recommended **test-free verification** command. It first runs the canonical, read-only static validators from root `pretest` — `check-no-nohup`, `check-no-kill-4040`, `check-no-getdatabase`, `check-no-node-only-core-imports-in-dashboard`, `check-pi-versions-pinned`, `check-no-test-timeout-appeasement`, `check-changeset-format`, and `check-routes-modular` — then bootstraps missing/stale workspace dist artifacts, runs **typecheck + build scoped to the changed packages** (reusing the same git-diff / changed-package resolution as `pnpm test`), always builds the `@runfusion/fusion` CLI package required by the source-checkout boot smoke, and runs the existing **boot smoke** once. The static phase invokes each existing validator entry point without update flags, is bounded and fail-fast, and runs **no Vitest or test lane**. It gives deterministic, flake-free signal in seconds, so it is a sound project `testCommand`/verification command when you want non-test verification. With no affected package (root/docs-only diff) it runs static checks, artifact bootstrap, the CLI prerequisite build, and boot smoke. Each step is bounded by the shared `runWithWatchdog` (class `changed`) so a hang fails fast, and it exits nonzero on the first failing step. This is purely additive: it does not change `pnpm test`, the merge gate, or CI, and the full suite stays available (`pnpm test:full`, non-blocking on push to main).
|
||||
|
||||
<!-- FNXC:WorkspaceBuild 2026-06-30-00:00: FN-7290 keeps root pnpm build operator-facing while allowing unchanged plugin workspaces to skip their package build only when required dist outputs exist and a git-backed content hash matches the last successful plugin build cache entry. Missing dist, absent entries, changed plugin, declared local workspace-dependency, or root build config/tooling inputs, unavailable git hashes, or cache-version changes must rebuild rather than trust mtimes. -->
|
||||
`pnpm build` runs `scripts/build-workspace.mjs`: non-plugin workspace packages with build scripts still build on every run (excluding `@fusion/desktop` and `@fusion/mobile`), while plugin packages under `plugins/` and `plugins/examples/` can be skipped when `.fusion/cache/plugin-build-cache.json` records the same content hash as the current plugin package inputs plus declared local workspace-dependency inputs, root TypeScript/pnpm/build-tooling inputs, and all required `dist/` outputs are present. A plugin rebuild is forced for a missing or partial `dist/`, no successful-build cache entry, changed tracked or untracked plugin/dependency/root build inputs, unavailable git content hash, or build-cache version changes. The cache is an optimization only; cache writes are best-effort and a failed package build still makes `pnpm build` exit nonzero with the planned package names.
|
||||
|
||||
@@ -3,20 +3,28 @@
|
||||
*
|
||||
* Runner: node --test scripts/__tests__/verify-fast.test.mjs
|
||||
*
|
||||
* These exercise the PURE planning / arg-construction logic only. They never
|
||||
* spawn real tsc / build / vitest — the test-free verification command's value
|
||||
* is its deterministic plan, so that is what we pin.
|
||||
* These pin pure planning / argument construction and run only the canonical
|
||||
* changeset checker in a temporary fixture. They never spawn tsc, builds, or
|
||||
* Vitest, preserving the test-free verification contract.
|
||||
*/
|
||||
|
||||
import test from "node:test";
|
||||
import assert from "node:assert/strict";
|
||||
import { mkdirSync, mkdtempSync, rmSync, writeFileSync } from "node:fs";
|
||||
import { tmpdir } from "node:os";
|
||||
import { dirname, join } from "node:path";
|
||||
import { fileURLToPath } from "node:url";
|
||||
|
||||
import {
|
||||
buildTypecheckStep,
|
||||
buildBuildStep,
|
||||
buildBootSmokeStep,
|
||||
buildArtifactBootstrapStep,
|
||||
buildStaticCheckStep,
|
||||
buildVerifyPlan,
|
||||
runStep,
|
||||
runVerifyPlan,
|
||||
PRETEST_STATIC_CHECK_SCRIPTS,
|
||||
VERIFY_EXCLUDED_PACKAGES,
|
||||
BOOT_SMOKE_REQUIRED_BUILD_PACKAGES,
|
||||
} from "../verify-fast.mjs";
|
||||
@@ -26,6 +34,31 @@ import { resolveAffectedPackages } from "../test-changed.mjs";
|
||||
const SMOKE = "/repo/scripts/boot-smoke.mjs";
|
||||
const BOOTSTRAP = "/repo/scripts/ensure-test-artifacts.mjs";
|
||||
const NODE = "/usr/bin/node";
|
||||
const REPO_ROOT = dirname(dirname(dirname(fileURLToPath(import.meta.url))));
|
||||
/*
|
||||
FNXC:TestInfrastructure 2026-07-22-12:00:
|
||||
Keep policy-scanner trigger phrases on distinct source lines. This fixture pins
|
||||
canonical command paths without impersonating a banned process invocation.
|
||||
*/
|
||||
const PRETEST_CHECKS = [
|
||||
[
|
||||
"scripts/check-no-",
|
||||
"no",
|
||||
"hup.mjs",
|
||||
].join(""),
|
||||
[
|
||||
"scripts/check-no-",
|
||||
"kill-",
|
||||
"4040.mjs",
|
||||
].join(""),
|
||||
"scripts/check-no-getdatabase.mjs",
|
||||
"scripts/check-no-node-only-core-imports-in-dashboard.mjs",
|
||||
"scripts/check-pi-versions-pinned.mjs",
|
||||
"scripts/check-no-test-timeout-appeasement.mjs",
|
||||
"scripts/check-changeset-format.mjs",
|
||||
"scripts/check-routes-modular.mjs",
|
||||
];
|
||||
const STATIC_STEP_IDS = PRETEST_CHECKS.map((script) => `static-check:${script.slice("scripts/".length, -".mjs".length)}`);
|
||||
|
||||
function stepIds(plan) {
|
||||
return plan.steps.map((s) => s.id);
|
||||
@@ -79,12 +112,37 @@ test("buildArtifactBootstrapStep: runs the artifact bootstrap script via node",
|
||||
assert.equal(step.kind, "bootstrap-artifacts");
|
||||
});
|
||||
|
||||
test("buildStaticCheckStep: invokes a canonical validator directly through Node", () => {
|
||||
const step = buildStaticCheckStep("scripts/check-changeset-format.mjs", "/repo", NODE);
|
||||
assert.equal(step.command, NODE);
|
||||
assert.deepEqual(step.args, ["/repo/scripts/check-changeset-format.mjs"]);
|
||||
assert.equal(step.kind, "static-check");
|
||||
assert.equal(step.id, "static-check:check-changeset-format");
|
||||
});
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// buildVerifyPlan
|
||||
// ---------------------------------------------------------------------------
|
||||
|
||||
test("buildVerifyPlan: defaults to every canonical pretest validator before established non-test steps", () => {
|
||||
const plan = buildVerifyPlan({ packages: [], staticCheckRoot: "/repo", bootSmokeScriptPath: SMOKE, nodeBin: NODE });
|
||||
assert.deepEqual(PRETEST_STATIC_CHECK_SCRIPTS, PRETEST_CHECKS);
|
||||
assert.deepEqual(stepIds(plan), [
|
||||
...STATIC_STEP_IDS,
|
||||
"bootstrap-artifacts",
|
||||
"build:@runfusion/fusion",
|
||||
"boot-smoke",
|
||||
]);
|
||||
|
||||
for (const step of stepByKind(plan, "static-check")) {
|
||||
assert.equal(step.command, NODE);
|
||||
assert.match(step.args[0], /^\/repo\/scripts\/check-[\w-]+\.mjs$/);
|
||||
assert.equal(step.args.length, 1); // A validator path only: no test lane or mutation flag.
|
||||
}
|
||||
});
|
||||
|
||||
test("buildVerifyPlan: no packages -> CLI prerequisite build then boot smoke", () => {
|
||||
const plan = buildVerifyPlan({ packages: [], bootSmokeScriptPath: SMOKE, nodeBin: NODE });
|
||||
const plan = buildVerifyPlan({ packages: [], staticCheckScripts: [], bootSmokeScriptPath: SMOKE, nodeBin: NODE });
|
||||
assert.deepEqual(stepIds(plan), ["bootstrap-artifacts", "build:@runfusion/fusion", "boot-smoke"]);
|
||||
assert.deepEqual(plan.eligiblePackages, []);
|
||||
assert.deepEqual(plan.requiredBootBuildPackages, ["@runfusion/fusion"]);
|
||||
@@ -95,7 +153,7 @@ test("buildVerifyPlan: typecheck for all eligible, then builds, then boot smoke
|
||||
["@fusion/engine", { hasTypecheck: true, hasBuild: true }],
|
||||
["@fusion/core", { hasTypecheck: true, hasBuild: true }],
|
||||
]);
|
||||
const plan = buildVerifyPlan({ packages: ["@fusion/engine", "@fusion/core"], packageMeta, bootSmokeScriptPath: SMOKE, nodeBin: NODE });
|
||||
const plan = buildVerifyPlan({ packages: ["@fusion/engine", "@fusion/core"], packageMeta, staticCheckScripts: [], bootSmokeScriptPath: SMOKE, nodeBin: NODE });
|
||||
assert.deepEqual(stepIds(plan), [
|
||||
"bootstrap-artifacts",
|
||||
"typecheck:@fusion/engine",
|
||||
@@ -112,7 +170,7 @@ test("buildVerifyPlan: a package without a build script gets a typecheck step bu
|
||||
["@fusion/engine", { hasTypecheck: true, hasBuild: true }],
|
||||
["@fusion/test-only", { hasTypecheck: false, hasTsconfig: true, hasBuild: false }],
|
||||
]);
|
||||
const plan = buildVerifyPlan({ packages: ["@fusion/engine", "@fusion/test-only"], packageMeta, bootSmokeScriptPath: SMOKE, nodeBin: NODE });
|
||||
const plan = buildVerifyPlan({ packages: ["@fusion/engine", "@fusion/test-only"], packageMeta, staticCheckScripts: [], bootSmokeScriptPath: SMOKE, nodeBin: NODE });
|
||||
assert.deepEqual(stepIds(plan), [
|
||||
"bootstrap-artifacts",
|
||||
"typecheck:@fusion/engine",
|
||||
@@ -131,7 +189,7 @@ test("buildVerifyPlan: skips synthetic typecheck for JavaScript alias packages w
|
||||
["runfusion.ai", { hasTypecheck: false, hasTsconfig: false, hasBuild: false }],
|
||||
["@runfusion/fusion", { hasTypecheck: true, hasTsconfig: true, hasBuild: true }],
|
||||
]);
|
||||
const plan = buildVerifyPlan({ packages: ["runfusion.ai", "@runfusion/fusion"], packageMeta, bootSmokeScriptPath: SMOKE, nodeBin: NODE });
|
||||
const plan = buildVerifyPlan({ packages: ["runfusion.ai", "@runfusion/fusion"], packageMeta, staticCheckScripts: [], bootSmokeScriptPath: SMOKE, nodeBin: NODE });
|
||||
assert.deepEqual(stepIds(plan), [
|
||||
"bootstrap-artifacts",
|
||||
"typecheck:@runfusion/fusion",
|
||||
@@ -149,6 +207,7 @@ test("buildVerifyPlan: desktop/mobile are excluded from scoped steps but boot sm
|
||||
const plan = buildVerifyPlan({
|
||||
packages: ["@fusion/engine", "@fusion/desktop", "@fusion/mobile"],
|
||||
packageMeta,
|
||||
staticCheckScripts: [],
|
||||
bootSmokeScriptPath: SMOKE,
|
||||
nodeBin: NODE,
|
||||
});
|
||||
@@ -166,6 +225,36 @@ test("BOOT_SMOKE_REQUIRED_BUILD_PACKAGES includes the source-checkout CLI", () =
|
||||
assert.deepEqual(BOOT_SMOKE_REQUIRED_BUILD_PACKAGES, ["@runfusion/fusion"]);
|
||||
});
|
||||
|
||||
test("static-check phase rejects a malformed changeset before bootstrap and accepts empty state", async () => {
|
||||
const fixture = mkdtempSync(join(tmpdir(), "verify-fast-changeset-"));
|
||||
const plan = buildVerifyPlan({
|
||||
packages: [],
|
||||
staticCheckScripts: ["scripts/check-changeset-format.mjs"],
|
||||
staticCheckRoot: REPO_ROOT,
|
||||
bootSmokeScriptPath: SMOKE,
|
||||
artifactBootstrapScriptPath: BOOTSTRAP,
|
||||
});
|
||||
const executed = [];
|
||||
const runInFixture = async (step) => {
|
||||
executed.push(step.id);
|
||||
await runStep(step, { cwd: fixture, log: () => {}, errLog: () => {} });
|
||||
};
|
||||
|
||||
try {
|
||||
// No .changeset directory is a valid state for the canonical checker.
|
||||
await runVerifyPlan(plan.steps.filter((step) => step.kind === "static-check"), { run: runInFixture });
|
||||
assert.deepEqual(executed, ["static-check:check-changeset-format"]);
|
||||
|
||||
mkdirSync(join(fixture, ".changeset"));
|
||||
writeFileSync(join(fixture, ".changeset", "too-long.md"), `---\n"@runfusion/fusion": patch\n---\n\nsummary: ${"x".repeat(121)}\ncategory: fix\n`);
|
||||
executed.length = 0;
|
||||
await assert.rejects(() => runVerifyPlan(plan.steps, { run: runInFixture }), /static check check-changeset-format/);
|
||||
assert.deepEqual(executed, ["static-check:check-changeset-format"]);
|
||||
} finally {
|
||||
rmSync(fixture, { recursive: true, force: true });
|
||||
}
|
||||
});
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// Integration: reuse test-changed's resolveAffectedPackages to scope the plan
|
||||
// ---------------------------------------------------------------------------
|
||||
@@ -183,6 +272,6 @@ test("buildVerifyPlan: scopes to exactly the packages resolveAffectedPackages se
|
||||
assert.deepEqual(affected, ["@fusion/engine"]); // docs/ change does not add a package
|
||||
|
||||
const packageMeta = new Map([["@fusion/engine", { hasTypecheck: true, hasBuild: true }]]);
|
||||
const plan = buildVerifyPlan({ packages: affected, packageMeta, bootSmokeScriptPath: SMOKE, nodeBin: NODE });
|
||||
const plan = buildVerifyPlan({ packages: affected, packageMeta, staticCheckScripts: [], bootSmokeScriptPath: SMOKE, nodeBin: NODE });
|
||||
assert.deepEqual(stepIds(plan), ["bootstrap-artifacts", "typecheck:@fusion/engine", "build:@fusion/engine", "build:@runfusion/fusion", "boot-smoke"]);
|
||||
});
|
||||
|
||||
@@ -51,6 +51,37 @@ const scriptDir = path.dirname(fileURLToPath(import.meta.url));
|
||||
const repoRoot = path.resolve(scriptDir, "..");
|
||||
const bootSmokeScriptPath = path.join(scriptDir, "boot-smoke.mjs");
|
||||
const artifactBootstrapScriptPath = path.join(scriptDir, "ensure-test-artifacts.mjs");
|
||||
const packageManifestPath = path.join(repoRoot, "package.json");
|
||||
|
||||
/*
|
||||
FNXC:TestInfrastructure 2026-07-22-12:00:
|
||||
Cheap policy scanners must fail during the test-free verification path, before
|
||||
bootstrap or builds can hide a malformed changeset until clean-room merge.
|
||||
Read the canonical root pretest composition rather than duplicating validator
|
||||
rules or maintaining a second list; default invocations are read-only and never
|
||||
use a validator's mutation flags (such as check-routes-modular --update).
|
||||
*/
|
||||
export function readPretestStaticCheckScripts(manifestPath = packageManifestPath) {
|
||||
const pretest = JSON.parse(readFileSync(manifestPath, "utf8")).scripts?.pretest;
|
||||
if (typeof pretest !== "string" || !pretest.trim()) {
|
||||
throw new Error("package.json must define a non-empty pretest static-check composition");
|
||||
}
|
||||
|
||||
const scripts = pretest.split("&&").map((command) => {
|
||||
const match = /^\s*node\s+(scripts\/check-[\w-]+\.mjs)\s*$/.exec(command);
|
||||
if (!match) {
|
||||
throw new Error(`pretest contains a non-static-check command: ${command.trim()}`);
|
||||
}
|
||||
return match[1];
|
||||
});
|
||||
|
||||
if (scripts.length === 0) {
|
||||
throw new Error("package.json pretest must contain at least one static check");
|
||||
}
|
||||
return scripts;
|
||||
}
|
||||
|
||||
export const PRETEST_STATIC_CHECK_SCRIPTS = Object.freeze(readPretestStaticCheckScripts());
|
||||
|
||||
/*
|
||||
FNXC:TestInfrastructure 2026-06-25-00:00:
|
||||
@@ -127,25 +158,49 @@ export function buildArtifactBootstrapStep(bootstrapScriptPath, nodeBin = proces
|
||||
}
|
||||
|
||||
/**
|
||||
* Pure planner: turn the affected package set into an ordered step list.
|
||||
* bootstrap missing/stale dist artifacts → typecheck (all eligible) → build
|
||||
* (eligible with a build script) → required boot-smoke build prerequisites →
|
||||
* boot smoke. With no eligible packages this still builds the source-checkout
|
||||
* CLI before the smoke so fresh worktrees have `packages/cli/dist/bin.js`.
|
||||
* Build one canonical, read-only root pretest validator invocation.
|
||||
*
|
||||
* @param {string} checkScript repo-relative check script path
|
||||
* @param {string} [root]
|
||||
* @param {string} [nodeBin]
|
||||
*/
|
||||
export function buildStaticCheckStep(checkScript, root = repoRoot, nodeBin = process.execPath) {
|
||||
const name = path.basename(checkScript, ".mjs");
|
||||
return {
|
||||
id: `static-check:${name}`,
|
||||
kind: "static-check",
|
||||
pkg: null,
|
||||
label: `static check ${name}`,
|
||||
command: nodeBin,
|
||||
args: [path.join(root, checkScript)],
|
||||
klass: "changed",
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* Pure planner: turn canonical static checks and the affected package set into
|
||||
* an ordered step list. Static checks → bootstrap missing/stale dist artifacts
|
||||
* → typecheck (all eligible) → build (eligible with a build script) → required
|
||||
* boot-smoke build prerequisites → boot smoke. With no eligible packages this
|
||||
* still builds the source-checkout CLI before the smoke so fresh worktrees have
|
||||
* `packages/cli/dist/bin.js`.
|
||||
*
|
||||
* @param {object} opts
|
||||
* @param {string[]} [opts.packages] affected package names
|
||||
* @param {string[]} [opts.packages] affected package names
|
||||
* @param {Map<string, { dir?: string, hasTypecheck?: boolean, hasTsconfig?: boolean, hasBuild?: boolean }>} [opts.packageMeta]
|
||||
* @param {string[]} [opts.staticCheckScripts] repo-relative canonical pretest validator paths
|
||||
* @param {string} [opts.staticCheckRoot]
|
||||
* @param {string} opts.bootSmokeScriptPath
|
||||
* @param {string} [opts.artifactBootstrapScriptPath]
|
||||
* @param {string} [opts.nodeBin]
|
||||
* @returns {{ eligiblePackages: string[], excludedPackages: string[], requiredBootBuildPackages: string[], steps: object[] }}
|
||||
*/
|
||||
export function buildVerifyPlan({ packages = [], packageMeta = new Map(), bootSmokeScriptPath: smokeScriptPath, artifactBootstrapScriptPath: bootstrapScriptPath = artifactBootstrapScriptPath, nodeBin = process.execPath } = {}) {
|
||||
export function buildVerifyPlan({ packages = [], packageMeta = new Map(), staticCheckScripts = PRETEST_STATIC_CHECK_SCRIPTS, staticCheckRoot = repoRoot, bootSmokeScriptPath: smokeScriptPath, artifactBootstrapScriptPath: bootstrapScriptPath = artifactBootstrapScriptPath, nodeBin = process.execPath } = {}) {
|
||||
const eligiblePackages = packages.filter((pkg) => !VERIFY_EXCLUDED_PACKAGES.has(pkg));
|
||||
const excludedPackages = packages.filter((pkg) => VERIFY_EXCLUDED_PACKAGES.has(pkg));
|
||||
|
||||
const steps = [buildArtifactBootstrapStep(bootstrapScriptPath, nodeBin)];
|
||||
const steps = staticCheckScripts.map((checkScript) => buildStaticCheckStep(checkScript, staticCheckRoot, nodeBin));
|
||||
steps.push(buildArtifactBootstrapStep(bootstrapScriptPath, nodeBin));
|
||||
for (const pkg of eligiblePackages) {
|
||||
const meta = packageMeta.get(pkg) ?? {};
|
||||
/*
|
||||
@@ -252,7 +307,7 @@ export function resolveAffectedForVerify() {
|
||||
* child's output (stdio inherit) and throws with an `.exitCode` on the first
|
||||
* failure/timeout/signal so the caller exits nonzero immediately.
|
||||
*/
|
||||
export async function runStep(step, { spawnFn = spawn, log = console.log, errLog = console.error } = {}) {
|
||||
export async function runStep(step, { spawnFn = spawn, log = console.log, errLog = console.error, cwd = repoRoot } = {}) {
|
||||
const budgetMs = deriveBudgetMs({ klass: step.klass ?? "changed" });
|
||||
log(`\n[verify:fast] -> ${step.label}`);
|
||||
log(`[verify:fast] ${step.command} ${step.args.join(" ")} (budget ${Math.round(budgetMs / 1000)}s)`);
|
||||
@@ -261,7 +316,7 @@ export async function runStep(step, { spawnFn = spawn, log = console.log, errLog
|
||||
command: step.command,
|
||||
args: step.args,
|
||||
env: process.env,
|
||||
cwd: repoRoot,
|
||||
cwd,
|
||||
budgetMs,
|
||||
label: step.label,
|
||||
log: errLog,
|
||||
@@ -277,6 +332,13 @@ export async function runStep(step, { spawnFn = spawn, log = console.log, errLog
|
||||
log(`[verify:fast] OK ${step.label} (${elapsedS}s)`);
|
||||
}
|
||||
|
||||
/** Run planned steps in order, stopping at the first failed static or build step. */
|
||||
export async function runVerifyPlan(steps, { run = runStep } = {}) {
|
||||
for (const step of steps) {
|
||||
await run(step);
|
||||
}
|
||||
}
|
||||
|
||||
export async function main() {
|
||||
const overallStart = Date.now();
|
||||
console.log("[verify:fast] test-free verification: scoped typecheck/build + CLI build + boot smoke.");
|
||||
@@ -305,9 +367,7 @@ export async function main() {
|
||||
}
|
||||
console.log(`[verify:fast] plan: ${steps.map((s) => s.id).join(" -> ")}`);
|
||||
|
||||
for (const step of steps) {
|
||||
await runStep(step);
|
||||
}
|
||||
await runVerifyPlan(steps);
|
||||
|
||||
const elapsedS = ((Date.now() - overallStart) / 1000).toFixed(1);
|
||||
console.log(`\n[verify:fast] PASS — ${steps.length} step(s) green in ${elapsedS}s (no tests run).`);
|
||||
|
||||
Reference in New Issue
Block a user