chore(scripts): end failing oxlint runs with a stable status line (#112532)

* chore(scripts): end failing oxlint runs with a stable status line

A crashed run-oxlint wrapper printed only a stack trace, and a lint
invocation whose output was truncated (cmd | tail -N) read as success —
which recently let a wrapper crash (stale node_modules after a dep-adding
merge) masquerade as a clean lint. Route the CLI entry through a small
wrapper that converts crashes into exit 1 and ends every failing run
with '[oxlint] FAILED (exit N)' as the final line.

* chore(scripts): declare runOxlintCliEntry in the script declaration contract

check-guards verifies .d.mts contracts against .mjs exports and
check-test-types consumes them; the new entry export needed both the
declaration and explicit log-parameter annotations in the test.
This commit is contained in:
Peter Steinberger
2026-07-21 22:53:12 -07:00
committed by GitHub
parent 3946bcc20c
commit 5dafb13614
3 changed files with 83 additions and 2 deletions
+8
View File
@@ -29,3 +29,11 @@ export function filterSparseMissingOxlintTargets(
* Applies wrapper policy and runs oxlint with the final argument list.
*/
export function main(argv?: string[], runtimeEnv?: NodeJS.ProcessEnv): Promise<void>;
/**
* CLI entry: converts wrapper crashes into exit 1 and ends every failing run
* with a stable `[oxlint] FAILED (exit N)` final line.
*/
export function runOxlintCliEntry(
run?: () => Promise<void>,
log?: (message: unknown) => void,
): Promise<void>;
+20 -2
View File
@@ -281,6 +281,24 @@ export async function main(argv = process.argv.slice(2), runtimeEnv = process.en
}
}
if (import.meta.main) {
await main();
/**
* CLI entry: converts wrapper crashes into a nonzero exit and ends every
* failing run with one stable final line. That line must survive output
* truncation (`… | tail -N`): without it, a crash or lint failure whose
* diagnostics scrolled away reads as success when only the tail is inspected.
*/
export async function runOxlintCliEntry(run = main, log = console.error) {
try {
await run();
} catch (error) {
log(error);
process.exitCode = 1;
}
if (typeof process.exitCode === "number" && process.exitCode !== 0) {
log(`[oxlint] FAILED (exit ${process.exitCode})`);
}
}
if (import.meta.main) {
await runOxlintCliEntry();
}
+55
View File
@@ -20,6 +20,7 @@ import {
} from "../../scripts/run-oxlint-shards.mjs";
import {
filterSparseMissingOxlintTargets,
runOxlintCliEntry,
shouldPrepareExtensionPackageBoundaryArtifacts,
} from "../../scripts/run-oxlint.mjs";
import { createScriptTestHarness } from "./test-helpers.js";
@@ -49,6 +50,60 @@ function isProcessAlive(pid: number): boolean {
}
describe("run-oxlint", () => {
it("ends a failing run with a stable final status line", async () => {
const priorExitCode = process.exitCode;
const lines: unknown[] = [];
try {
process.exitCode = 0;
await runOxlintCliEntry(
async () => {
process.exitCode = 2;
},
(line: unknown) => lines.push(line),
);
expect(lines).toEqual(["[oxlint] FAILED (exit 2)"]);
} finally {
process.exitCode = priorExitCode;
}
});
it("converts a wrapper crash into a nonzero exit with the status line last", async () => {
// The original incident: a crashed wrapper printed only a stack trace, and
// truncated output read as success. The marker must be the final line.
const priorExitCode = process.exitCode;
const lines: unknown[] = [];
try {
process.exitCode = 0;
await runOxlintCliEntry(
async () => {
throw new Error("artifact prep failed");
},
(line: unknown) => lines.push(line),
);
expect(process.exitCode).toBe(1);
expect(lines).toHaveLength(2);
expect(lines[0]).toBeInstanceOf(Error);
expect(lines[1]).toBe("[oxlint] FAILED (exit 1)");
} finally {
process.exitCode = priorExitCode;
}
});
it("stays silent on a clean run", async () => {
const priorExitCode = process.exitCode;
const lines: unknown[] = [];
try {
process.exitCode = 0;
await runOxlintCliEntry(
async () => {},
(line: unknown) => lines.push(line),
);
expect(lines).toEqual([]);
} finally {
process.exitCode = priorExitCode;
}
});
it("prepares extension package boundary artifacts for normal lint runs", () => {
expect(shouldPrepareExtensionPackageBoundaryArtifacts([])).toBe(true);
expect(shouldPrepareExtensionPackageBoundaryArtifacts(["src/index.ts"])).toBe(true);