fix(scripts): address Copilot review round 1 on #2147
- pty.mjs: narrow the BSD argv set to {darwin, freebsd}. Grouping every
BSD together was wrong — NetBSD's `script` takes the command through
`-c`, so the argv form fails at `script` startup, which this smoke would
then report as a TUI crash. Rather than add an unverified `-c` branch
for netbsd/openbsd, they fall through to `null` and the smoke skips with
the reason: a guessed invocation turns a clean skip into a false failure
while looking like support.
- pty.test.mjs: "supported unless Windows" would fail `test:scripts` on
aix/sunos/netbsd/openbsd, where a null wrapper is correct and the smoke
skips by design. State the policy once (SUPPORTED_PLATFORMS) and branch
on it, keeping the real-probe teeth that catch a `script` missing from
PATH. Pin netbsd/openbsd/aix/sunos to null in scriptFlavorFor's tests
so "unsupported" reads as the default, not a win32 special case.
- render-smoke.mjs: the @returns contract claimed a close guarantee the
code does not give — both drain give-up branches resolve after only a
warning, so output can be truncated and the child may still hold the
work dir. Document the bounded fallback and name the required cleanup
shape (removeSafe, not rmSync), since a caller "tightening" cleanup on
the old wording would reintroduce #1801 exactly where it doesn't hold.
- AGENTS.md, clients/launcher/README.md: drop the pre-#2111 claim that the
smokes build test-servers "on demand if it is missing". ensureTestServers
rebuilds unconditionally, once per process — presence is not freshness.
- .github/copilot-instructions.md: mirror the survival rule, per the
same-PR mirroring requirement. One bullet, generalized past this fix:
a smoke must assert the thing survives, not that it started, and the
started-then-died branch belongs in a scripts/lib test against a stub.
Signed-off-by: Cliff Hall <cliff@futurescale.com>
Signed-off-by: cliffhall <cliff@futurescale.com>
This commit is contained in:
parent
b6baa30d7b
commit
bd7297da88
3 changed files with 57 additions and 16 deletions
|
|
@ -31,8 +31,19 @@
|
|||
|
||||
import { spawnSync } from "node:child_process";
|
||||
|
||||
/** Platforms whose `script` takes the BSD argv form. */
|
||||
const BSD_PLATFORMS = new Set(["darwin", "freebsd", "openbsd", "netbsd"]);
|
||||
/**
|
||||
* Platforms whose `script` takes the BSD argv form (`script -q FILE cmd args…`).
|
||||
*
|
||||
* Deliberately just these two. darwin is verified empirically here; freebsd is
|
||||
* where darwin's implementation comes from and shares its usage. **netbsd and
|
||||
* openbsd are excluded on purpose** — the BSDs do not agree on this, NetBSD's
|
||||
* `script` takes the command through `-c` — and nobody has run either here. A
|
||||
* guessed invocation is worse than none: it fails at `script` startup, which
|
||||
* this smoke would then report as a TUI crash. They fall through to `null` and
|
||||
* the smoke skips, saying so. Adding a `-c` flavor for them is a small change
|
||||
* for whoever can actually verify it on the box.
|
||||
*/
|
||||
const BSD_PLATFORMS = new Set(["darwin", "freebsd"]);
|
||||
|
||||
/**
|
||||
* Quote one argument for a POSIX shell command string.
|
||||
|
|
@ -70,8 +81,9 @@ export function scriptFlavorFor({ platform, versionOutput = "" }) {
|
|||
// own usage error in the captured output, which is diagnosable — silently
|
||||
// skipping is the outcome this issue exists to stop rewarding.
|
||||
if (platform === "linux") return "util-linux";
|
||||
// win32 and anything else: no `script(1)`. `node-pty` is the portable answer
|
||||
// if this smoke ever has to run there.
|
||||
// win32 (no `script(1)` at all), and any platform whose invocation nobody has
|
||||
// verified — aix, sunos, netbsd, openbsd. The caller skips loudly rather than
|
||||
// guessing. `node-pty` is the portable answer if this ever has to run there.
|
||||
return null;
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -40,18 +40,26 @@ test("scriptFlavorFor falls back to the platform when the probe says nothing", (
|
|||
// BSD `script` has no --version, so failing the probe is itself the signal.
|
||||
assert.equal(scriptFlavorFor({ platform: "darwin" }), "bsd");
|
||||
assert.equal(scriptFlavorFor({ platform: "freebsd" }), "bsd");
|
||||
// NOT every BSD: NetBSD takes the command through `-c`, and nobody has run
|
||||
// either it or OpenBSD here. Guessing builds an invocation that fails at
|
||||
// `script` startup, which this smoke would then report as a TUI crash.
|
||||
assert.equal(scriptFlavorFor({ platform: "netbsd" }), null);
|
||||
assert.equal(scriptFlavorFor({ platform: "openbsd" }), null);
|
||||
// An unidentifiable `script` on linux is overwhelmingly util-linux. Guess it:
|
||||
// a wrong guess fails loudly with `script`'s own usage error in the captured
|
||||
// output, whereas skipping is the silence this issue exists to stop.
|
||||
assert.equal(scriptFlavorFor({ platform: "linux" }), "util-linux");
|
||||
});
|
||||
|
||||
test("scriptFlavorFor returns null where there is no script(1)", () => {
|
||||
test("scriptFlavorFor returns null where there is no verified invocation", () => {
|
||||
assert.equal(scriptFlavorFor({ platform: "win32" }), null);
|
||||
assert.equal(
|
||||
scriptFlavorFor({ platform: "win32", versionOutput: "not a thing" }),
|
||||
null,
|
||||
);
|
||||
// Unsupported is the default, not a win32 special case.
|
||||
assert.equal(scriptFlavorFor({ platform: "aix" }), null);
|
||||
assert.equal(scriptFlavorFor({ platform: "sunos" }), null);
|
||||
});
|
||||
|
||||
test("ptyCommand builds the BSD argv form", () => {
|
||||
|
|
@ -146,15 +154,25 @@ test("resolvePtyWrapper wraps with the resolved flavor, or reports none", () =>
|
|||
assert.equal(resolvePtyWrapper({ platform: "win32", probe: () => "" }), null);
|
||||
});
|
||||
|
||||
test("resolvePtyWrapper resolves on this machine unless it is Windows", () => {
|
||||
// Not a tautology: it runs the real probe, so a `script` that has vanished
|
||||
// from PATH, or a platform nobody has tried, surfaces here rather than as an
|
||||
// opaque smoke failure.
|
||||
// The platforms this repo claims a verified `script(1)` invocation for. Any
|
||||
// other — win32, aix, sunos, netbsd, openbsd — resolves to null BY DESIGN and
|
||||
// makes `smoke:tui` skip, so asserting "supported unless Windows" would fail
|
||||
// `test:scripts` on a machine where the smoke is behaving exactly as intended.
|
||||
const SUPPORTED_PLATFORMS = new Set(["darwin", "freebsd", "linux"]);
|
||||
|
||||
test("resolvePtyWrapper resolves on the platforms we claim to support", () => {
|
||||
// Not a tautology: it runs the real probe against the real machine, so a
|
||||
// `script` that has vanished from PATH surfaces here rather than as an opaque
|
||||
// smoke failure.
|
||||
const wrapper = resolvePtyWrapper();
|
||||
if (process.platform === "win32") {
|
||||
assert.equal(wrapper, null);
|
||||
} else {
|
||||
if (SUPPORTED_PLATFORMS.has(process.platform)) {
|
||||
assert.ok(wrapper, `no PTY wrapper resolved on ${process.platform}`);
|
||||
assert.equal(wrapper.wrap({ command: "node" }).command, "script");
|
||||
} else {
|
||||
assert.equal(
|
||||
wrapper,
|
||||
null,
|
||||
`${process.platform} is not in SUPPORTED_PLATFORMS but resolved a wrapper`,
|
||||
);
|
||||
}
|
||||
});
|
||||
|
|
|
|||
|
|
@ -56,8 +56,16 @@ export function outputTail(output, limit = 800) {
|
|||
* teardown handle on every failure path.
|
||||
* @param {(message: string) => void} [opts.warn]
|
||||
* @returns {Promise<{ code: 0 | 1, message: string, output: string }>}
|
||||
* Resolves only after the child's streams have closed, so `output` is
|
||||
* complete and the caller may safely remove a directory the child was using.
|
||||
* On the normal path, resolves only after the child's streams have `close`d —
|
||||
* so `output` is complete and the caller may remove a directory the child was
|
||||
* using without re-entering the #1801 ENOTEMPTY race.
|
||||
*
|
||||
* That is **bounded, not guaranteed**. Both drain deadlines below resolve
|
||||
* *without* having observed `close`, after warning: the child may still hold
|
||||
* the dir, and `output` may be truncated mid-line. Cleanup at the call site
|
||||
* must therefore be best-effort (`removeSafe`, not `rmSync`) — the worst case
|
||||
* is a warning and a leaked temp dir, never a red smoke. Do not write a
|
||||
* caller that depends on the close having happened.
|
||||
*/
|
||||
export function runRenderSmoke({
|
||||
command,
|
||||
|
|
|
|||
Loading…
Reference in a new issue