fix(release): isolate trusted candidate dependencies

This commit is contained in:
Peter Steinberger
2026-07-15 07:07:54 +01:00
parent f30d9b250a
commit 34b8a3fda0
2 changed files with 20 additions and 8 deletions

View File

@@ -9,7 +9,6 @@ import {
readFileSync,
renameSync,
rmSync,
symlinkSync,
writeFileSync,
} from "node:fs";
import { tmpdir } from "node:os";
@@ -273,6 +272,7 @@ export function run(command, args, options = {}) {
const result = spawnSync(command, args, {
cwd: options.cwd,
encoding: "utf8",
env: options.env ? { ...process.env, ...options.env } : process.env,
maxBuffer: COMMAND_CAPTURE_MAX_BUFFER_BYTES,
stdio: options.capture ? ["ignore", "pipe", "pipe"] : "inherit",
});
@@ -516,13 +516,11 @@ function runFromTrustedTooling(argv, { targetRoot, workflowRef }) {
cwd: targetRoot,
});
worktreeAdded = true;
const targetNodeModules = join(targetRoot, "node_modules");
if (!existsSync(targetNodeModules)) {
throw new Error("release candidate validation requires target checkout node_modules");
}
// Trusted release tooling executes from an isolated worktree, while dependency
// bytes stay pinned to the prepared target checkout used by local preflight.
symlinkSync(targetNodeModules, join(toolingRoot, "node_modules"), "dir");
// Build an isolated trusted dependency tree. Lifecycle scripts stay disabled;
// the frozen lockfile and prefer-offline store keep setup deterministic.
run("pnpm", ["install", "--frozen-lockfile", "--ignore-scripts", "--prefer-offline"], {
cwd: toolingRoot,
});
const result = spawnSync(
process.execPath,
[join(toolingRoot, "scripts/release-candidate-checklist.mjs"), ...argv],

View File

@@ -98,6 +98,16 @@ describe("release candidate checklist", () => {
expect(output).toHaveLength(2 * 1024 * 1024);
});
it("passes scoped environment overrides to release child commands", () => {
const output = run(
process.execPath,
["-e", "process.stdout.write(process.env.OPENCLAW_RELEASE_TEST_VALUE ?? '')"],
{ capture: true, env: { OPENCLAW_RELEASE_TEST_VALUE: "passed" } },
);
expect(output).toBe("passed");
});
it("keeps the frozen release target separate from clean trusted workflow tooling", () => {
expect(
validateCandidateCheckout({
@@ -161,6 +171,10 @@ describe("release candidate checklist", () => {
).toThrow("clean tracked tooling checkout");
const source = readFileSync("scripts/release-candidate-checklist.mjs", "utf8");
expect(source).toContain('const TOOLING_ROOT = fileURLToPath(new URL("../", import.meta.url))');
expect(source).toContain('mkdtempSync(join(tmpdir(), "openclaw-release-tooling-"))');
expect(source).toContain(
'["install", "--frozen-lockfile", "--ignore-scripts", "--prefer-offline"]',
);
expect(source).toContain("`+refs/heads/${workflowRef}:${remoteRef}`");
expect(source).toContain('"worktree", "add", "--detach", toolingRoot, trustedToolingSha');
expect(source).toContain(