Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions docs/agents/release-and-notices.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ Licenses, distribution notices, and package rehearsal.

| Rule | Enforced by |
|---|---|
| Reinstall fixtures replace a previously executed optional binary by publishing a fresh executable inode rather than truncating it | `replaceBinary` stages complete executable contents and renames them into the fixture path. `binary replacement retains a running executable's inode` holds a real Linux executable open, proves in-place writing gets ETXTBSY, and requires an IPC acknowledgment from the original process after executing the replacement, failing if it exits first. Filesystem executable lifetime cannot be encoded by a path type |
| A launcher lifecycle fixture publishes a complete positive child PID before the test probes or signals it | The stand-in writes a sibling file and atomically renames it into place; `stopping the launcher stops the binary` validates the record and requires ESRCH after launcher exit. Atomic publication removes the empty-file-to-PID-0 process-group race; cleanup signals only a validated PID. A numeric conversion alone cannot distinguish a complete record from an empty file |
| The installer and Node launcher reject the same missing, wrong-version, non-executable or failed-check platform binary, including a Unix version process that ignores termination | `nativeBinary` owns validation and uses SIGKILL on Unix at its timeout. `native.test.mjs` executes recovery without an environment override and runs a real SIGTERM-ignoring child with the production signal option. External package contents and process signals cannot be enforced by a type |
| Dependencies keep to the license policy | `cargo deny check` and the fuzz projects' license check (`deny.yml`); `scripts/svt_av1_licenses.py` checks the native SVT dependency against its recipe identity and independently pinned notice bytes (`svt-av1-native.yml`). Its rewritten-manifest regressions reject changing both the text and its recorded hash |
Expand Down
43 changes: 40 additions & 3 deletions packages/cli/test/native.test.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,8 @@
// of this package's version); the fixture cases also COREAV_FIXTURE_DIRS.

import assert from "node:assert/strict";
import { spawnSync } from "node:child_process";
import { spawn, spawnSync } from "node:child_process";
import { once } from "node:events";
import { chmodSync, cpSync, existsSync, lstatSync, mkdirSync, mkdtempSync, readFileSync, readlinkSync, realpathSync, renameSync, rmSync, writeFileSync } from "node:fs";
import { createRequire } from "node:module";
import { tmpdir } from "node:os";
Expand Down Expand Up @@ -79,6 +80,42 @@ function installed(scratch, binary) {
return copy;
}

function replaceBinary(binary, contents) {
const replacement = `${binary}.replacement`;
writeFileSync(replacement, contents);
chmodSync(replacement, 0o755);
renameSync(replacement, binary);
}

test("binary replacement retains a running executable's inode", { skip: process.platform !== "linux" && "Linux executable write protection", timeout: 10000 }, async (t) => {
const scratch = mkdtempSync(join(tmpdir(), "coreav-running-inode-"));
const binary = join(scratch, "binary");
cpSync(process.execPath, binary);
const child = spawn(binary, ["-e", 'process.on("message", () => process.send("alive")); process.send("ready");'], { stdio: ["ignore", "ignore", "pipe", "ipc"] });
const closed = once(child, "close");
const nextMessage = () => Promise.race([
once(child, "message", { signal: t.signal }),
closed.then(() => assert.fail("the original executable exited before responding")),
]);
try {
const [ready] = await nextMessage();
assert.equal(ready, "ready");
assert.throws(() => writeFileSync(binary, "replacement"), { code: "ETXTBSY" });
replaceBinary(binary, "#!/bin/sh\necho replacement\n");
const done = run(binary, ["--version"]);
assert.equal(done.status, 0, done.stderr.toString());
assert.equal(done.stdout.toString(), "replacement\n");
const response = nextMessage();
child.send("ping");
const [alive] = await response;
assert.equal(alive, "alive");
} finally {
child.kill("SIGKILL");
await closed;
rmSync(scratch, { recursive: true, force: true });
}
});

test("the install script makes the command the native binary", { skip: needsBin || (process.platform === "win32" && "the launcher stays on Windows") }, () => {
const scratch = mkdtempSync(join(tmpdir(), "coreav-install-"));
try {
Expand Down Expand Up @@ -145,9 +182,9 @@ test("reinstall restores the WASI launcher after the optional binary is removed
assert.equal(run(process.execPath, [join(copy, "install.js")]).status, 0);
assert.equal(realpathSync(command), realpathSync(binary));
if (failure === "removed") rmSync(dirname(dirname(binary)), { recursive: true, force: true });
else if (failure === "wrong version") writeFileSync(binary, "#!/bin/sh\necho coreav 9.9.9\n");
else if (failure === "wrong version") replaceBinary(binary, "#!/bin/sh\necho coreav 9.9.9\n");
else if (failure === "cannot execute") chmodSync(binary, 0o644);
else writeFileSync(binary, `#!/bin/sh\necho coreav ${pkg.version}\necho rejected-native-binary >&2\nexit 1\n`);
else replaceBinary(binary, `#!/bin/sh\necho coreav ${pkg.version}\necho rejected-native-binary >&2\nexit 1\n`);
const done = run(process.execPath, [join(copy, "install.js")]);
assert.equal(done.status, 0, done.stderr.toString());
assert.equal(realpathSync(command), realpathSync(join(copy, "lib", "main.js")));
Expand Down
Loading