Files
openclaw/scripts/run-lint.mts
Eden 830196c628 fix(scripts): keep Windows lint runs from failing before any file is checked (#126274)
* fix(scripts): keep Windows lint runs from failing before any file is checked

`pnpm lint:extensions` aborts on Windows whenever the plugin SDK boundary
cache is cold: the boundary prep spawns `node_modules/.bin/tsgo` directly,
and Windows cannot execute the extensionless pnpm shim, so the run dies with
ENOENT before oxlint checks a single file. The stylelint runner reached by
`check-changed` fails the same way for the same reason.

Both now build their child process with `createManagedCommandInvocation`,
the launcher every other repo tool runner already uses, which routes the shim
through cmd.exe on Windows and returns the command unchanged everywhere else.

Off Windows the spawned command, arguments, and options are byte-identical to
before, so only the broken platform changes behavior.

* fix(scripts): stop the lint pipeline from spawning a tool shim directly

`pnpm lint` reaches stylelint through the same raw shim spawn that broke the
boundary prep, so the pipeline dies on Windows after oxlint succeeds. Route it
through the managed launcher like every other tool runner.

The remaining hazard is structural: the resolver hands out a path that only
some callers know to normalize, and the three sites that forgot were spread
across two spawn shapes. Add a static guard so a shim can only flow into a
launcher that understands the platform, and so the next occurrence fails on
Linux CI instead of on a contributor's Windows machine.

* test(scripts): trim Windows shim regression coverage

Punchcard-Session: cobalt-orchard-willow-2q

---------

Co-authored-by: Vincent Koc <vincentkoc@ieee.org>
2026-08-21 09:31:50 -07:00

64 lines
2.1 KiB
TypeScript

// Runs the complete lint pipeline after preparing a linked-worktree toolchain.
import { spawnSync, type SpawnSyncOptions } from "node:child_process";
import { createRequire } from "node:module";
import path from "node:path";
import { pathToFileURL } from "node:url";
import {
ensureRepoToolNodeModulesLink,
resolveRepoToolBinPath,
} from "./lib/local-check-runtime.mts";
function run(command: string, args: string[], options: SpawnSyncOptions) {
const result = spawnSync(command, args, options);
if (result.error) {
throw result.error;
}
return result.status ?? 1;
}
const oxlintPath = resolveRepoToolBinPath("oxlint");
const tsxPath = resolveRepoToolBinPath("tsx");
ensureRepoToolNodeModulesLink(oxlintPath);
const tsxImportSpecifier = pathToFileURL(createRequire(tsxPath).resolve("tsx")).href;
// Invoke the pre-step directly: running pnpm through a linked node_modules can
// reconcile the owning checkout's dependency tree instead of merely running it.
const uiI18nStatus = run(
process.execPath,
["--import", tsxImportSpecifier, path.resolve("scripts", "control-ui-i18n-verify.ts"), "verify"],
{ env: process.env, stdio: "inherit" },
);
if (uiI18nStatus !== 0) {
process.exitCode = uiI18nStatus;
} else {
const oxlintStatus = run(
process.execPath,
[
"--import",
tsxImportSpecifier,
path.resolve("scripts", "run-oxlint-shards.mts"),
...process.argv.slice(2),
],
{ env: process.env, stdio: "inherit" },
);
if (oxlintStatus !== 0) {
process.exitCode = oxlintStatus;
} else {
// Control UI CSS hygiene: plain stylesheets plus css`` templates in Lit
// components. oxlint cannot see inside tagged CSS templates.
// Delegate like the steps above: the stylelint runner already resolves and
// launches the shim, so this pipeline never handles a tool path itself.
process.exitCode = run(
process.execPath,
[
"--import",
tsxImportSpecifier,
path.resolve("scripts", "run-stylelint.mts"),
"ui/src/**/*.css",
"ui/src/**/*.ts",
],
{ env: process.env, stdio: "inherit" },
);
}
}