From e99d978e24cde0ea837bff913470de4522ecc3a9 Mon Sep 17 00:00:00 2001 From: Peter Steinberger Date: Thu, 23 Jul 2026 00:51:10 -0400 Subject: [PATCH] test: consolidate script workflow fixtures (#112892) --- test/scripts/crabbox-wrapper.test.ts | 2524 ++++++----------- .../package-acceptance-workflow.test.ts | 199 +- 2 files changed, 979 insertions(+), 1744 deletions(-) diff --git a/test/scripts/crabbox-wrapper.test.ts b/test/scripts/crabbox-wrapper.test.ts index 4a37343af107..0305172c20d5 100644 --- a/test/scripts/crabbox-wrapper.test.ts +++ b/test/scripts/crabbox-wrapper.test.ts @@ -34,6 +34,8 @@ const GIT_STATUS_PORCELAIN_KEY = "status\u0000--porcelain=v1"; const GIT_MERGE_BASE_MAIN_HEAD_KEY = "merge-base\u0000origin/main\u0000HEAD"; const GIT_MERGE_BASE_RELEASE_HEAD_KEY = "merge-base\u0000origin/release/2026.7.2\u0000HEAD"; const GIT_CHECK_RELEASE_REF_KEY = "check-ref-format\u0000refs/remotes/origin/release/2026.7.2"; +const defaultProviderHelp = + "provider: hetzner, aws, local-container, blacksmith-testbox, or cloudflare\n"; const defaultGitResponses: Record = { [GIT_CONFIG_SPARSE_KEY]: { stdout: "false\n" }, [GIT_SPARSE_LIST_KEY]: { status: 1 }, @@ -523,6 +525,10 @@ function runWrapper(helpText: string, args: string[], options: WrapperOptions = }); } +function runDefaultWrapper(args: string[], options: WrapperOptions = {}) { + return runWrapper(defaultProviderHelp, args, options); +} + type WrapperOptions = { configJson?: Record; configStatus?: number; @@ -704,11 +710,57 @@ function expectGroupedShellCommand(remoteCommand: string, command: string): void } } +function runMacosShell(shellScript: string, options: WrapperOptions = {}) { + const result = runDefaultWrapper( + ["run", "--provider", "aws", "--target", "macos", "--shell", "--", shellScript], + options, + ); + const output = parseFakeCrabboxOutput(result); + return { + output, + remoteCommand: normalizeShellLineEndings(output.args.at(-1) ?? ""), + result, + }; +} + +function runMacosCommand(command: string[], options: WrapperOptions = {}) { + const result = runDefaultWrapper( + ["run", "--provider", "aws", "--target", "macos", "--", ...command], + options, + ); + const output = parseFakeCrabboxOutput(result); + return { + output, + remoteCommand: normalizeShellLineEndings(output.args.at(-1) ?? ""), + result, + }; +} + const remoteChangedGateEnvPrefix = "OPENCLAW_CHECK_CHANGED_REMOTE_CHILD=1 OPENCLAW_CHANGED_LANES_RAW_SYNC=1 CI=1"; const remoteChangedGateExport = `export ${remoteChangedGateEnvPrefix};`; const remoteChangedGateFetch = 'git fetch -q --depth=2 origin "$openclaw_changed_gate_base:refs/remotes/origin/main"'; +const sparseChangedGateOptions = { + gitResponses: { + [GIT_CONFIG_SPARSE_KEY]: { stdout: "true\n" }, + [GIT_STATUS_PORCELAIN_KEY]: { stdout: "" }, + [GIT_MERGE_BASE_MAIN_HEAD_KEY]: { stdout: "abc123\n" }, + }, +} satisfies WrapperOptions; + +function runSparseShell(shellScript: string) { + const result = runDefaultWrapper( + ["run", "--provider", "aws", "--shell", "--", shellScript], + sparseChangedGateOptions, + ); + const output = parseFakeCrabboxOutput(result); + return { + output, + remoteCommand: normalizeShellLineEndings(output.args.at(-1) ?? ""), + result, + }; +} function expectChangedGateGitBootstrap(remoteCommand: string): void { expect(remoteCommand).toContain("command -v git"); @@ -778,18 +830,14 @@ describe("scripts/crabbox-wrapper", () => { }); it("accepts advertised canonical providers from Crabbox help", () => { - const result = runWrapper( - "provider: hetzner, aws, local-container, blacksmith-testbox, or cloudflare\n", - ["run", "--provider", "local-container", "--", "echo ok"], - ); + const result = runDefaultWrapper(["run", "--provider", "local-container", "--", "echo ok"]); expect(result.status).toBe(0); expect(parseFakeCrabboxOutput(result).args).toContain("local-container"); }); it("hints at lease expiry when a reused-lease run fails fast", () => { - const result = runWrapper( - "provider: hetzner, aws, local-container, blacksmith-testbox, or cloudflare\n", + const result = runDefaultWrapper( ["run", "--provider", "local-container", "--id", "tbx_expired_fixture", "--", "echo ok"], { env: { OPENCLAW_FAKE_CRABBOX_RUN_STATUS: "1" } }, ); @@ -801,22 +849,18 @@ describe("scripts/crabbox-wrapper", () => { }); it("keeps failed runs without a reused lease free of the expiry hint", () => { - const result = runWrapper( - "provider: hetzner, aws, local-container, blacksmith-testbox, or cloudflare\n", - ["run", "--provider", "local-container", "--", "echo ok"], - { env: { OPENCLAW_FAKE_CRABBOX_RUN_STATUS: "1" } }, - ); + const result = runDefaultWrapper(["run", "--provider", "local-container", "--", "echo ok"], { + env: { OPENCLAW_FAKE_CRABBOX_RUN_STATUS: "1" }, + }); expect(result.status).toBe(1); expect(result.stderr).not.toContain("failed fast; reusable leases expire"); }); it("requires a current Crabbox binary for Blacksmith Testbox runs", () => { - const result = runWrapper( - "provider: hetzner, aws, local-container, blacksmith-testbox, or cloudflare\n", - ["run", "--provider", "blacksmith-testbox", "--", "echo ok"], - { env: { OPENCLAW_FAKE_CRABBOX_VERSION: "crabbox 0.21.9" } }, - ); + const result = runDefaultWrapper(["run", "--provider", "blacksmith-testbox", "--", "echo ok"], { + env: { OPENCLAW_FAKE_CRABBOX_VERSION: "crabbox 0.21.9" }, + }); expect(result.status).toBe(2); expect(result.stdout).toBe(""); @@ -825,33 +869,27 @@ describe("scripts/crabbox-wrapper", () => { }); it("applies the Blacksmith version gate to provider aliases", () => { - const result = runWrapper( - "provider: hetzner, aws, local-container, blacksmith-testbox, or cloudflare\n", - ["run", "--provider", "blacksmith", "--", "echo ok"], - { env: { OPENCLAW_FAKE_CRABBOX_VERSION: "crabbox 0.21.9" } }, - ); + const result = runDefaultWrapper(["run", "--provider", "blacksmith", "--", "echo ok"], { + env: { OPENCLAW_FAKE_CRABBOX_VERSION: "crabbox 0.21.9" }, + }); expect(result.status).toBe(2); expect(result.stderr).toContain("provider=blacksmith-testbox requires Crabbox >= 0.22.0"); }); it("rejects prerelease Crabbox builds at the Blacksmith minimum boundary", () => { - const result = runWrapper( - "provider: hetzner, aws, local-container, blacksmith-testbox, or cloudflare\n", - ["run", "--provider", "blacksmith-testbox", "--", "echo ok"], - { env: { OPENCLAW_FAKE_CRABBOX_VERSION: "crabbox 0.22.0-rc.1" } }, - ); + const result = runDefaultWrapper(["run", "--provider", "blacksmith-testbox", "--", "echo ok"], { + env: { OPENCLAW_FAKE_CRABBOX_VERSION: "crabbox 0.22.0-rc.1" }, + }); expect(result.status).toBe(2); expect(result.stderr).toContain("selected binary reported version=crabbox 0.22.0-rc.1"); }); it("rejects unsafe Crabbox version numbers at the Blacksmith minimum gate", () => { - const result = runWrapper( - "provider: hetzner, aws, local-container, blacksmith-testbox, or cloudflare\n", - ["run", "--provider", "blacksmith-testbox", "--", "echo ok"], - { env: { OPENCLAW_FAKE_CRABBOX_VERSION: "crabbox 0.9007199254740993.0" } }, - ); + const result = runDefaultWrapper(["run", "--provider", "blacksmith-testbox", "--", "echo ok"], { + env: { OPENCLAW_FAKE_CRABBOX_VERSION: "crabbox 0.9007199254740993.0" }, + }); expect(result.status).toBe(2); expect(result.stderr).toContain( @@ -860,21 +898,16 @@ describe("scripts/crabbox-wrapper", () => { }); it("accepts post-release Crabbox describe builds at the Blacksmith minimum boundary", () => { - const result = runWrapper( - "provider: hetzner, aws, local-container, blacksmith-testbox, or cloudflare\n", - ["run", "--provider", "blacksmith-testbox", "--", "echo ok"], - { env: { OPENCLAW_FAKE_CRABBOX_VERSION: "crabbox 0.22.0-3-gabc1234" } }, - ); + const result = runDefaultWrapper(["run", "--provider", "blacksmith-testbox", "--", "echo ok"], { + env: { OPENCLAW_FAKE_CRABBOX_VERSION: "crabbox 0.22.0-3-gabc1234" }, + }); expect(result.status).toBe(0); expect(parseFakeCrabboxOutput(result).args).toContain("blacksmith-testbox"); }); it("tells operators how to read delegated Testbox proof status", () => { - const result = runWrapper( - "provider: hetzner, aws, local-container, blacksmith-testbox, or cloudflare\n", - ["run", "--provider", "blacksmith-testbox", "--", "echo ok"], - ); + const result = runDefaultWrapper(["run", "--provider", "blacksmith-testbox", "--", "echo ok"]); expect(result.status).toBe(0); expect(result.stderr).toContain("delegated Testbox proof uses the wrapper exitCode"); @@ -885,8 +918,7 @@ describe("scripts/crabbox-wrapper", () => { const home = mkdtempSync(path.join(tmpdir(), "openclaw-crabbox-home-")); tempDirs.push(home); - const result = runWrapper( - "provider: hetzner, aws, local-container, blacksmith-testbox, or cloudflare\n", + const result = runDefaultWrapper( ["run", "--provider", "blacksmith-testbox", "--id", "tbx_direct", "--", "echo ok"], { env: testHomeEnv(home) }, ); @@ -905,8 +937,7 @@ describe("scripts/crabbox-wrapper", () => { mkdirSync(path.dirname(keyPath), { recursive: true }); writeFileSync(keyPath, "fake test key\n", "utf8"); - const result = runWrapper( - "provider: hetzner, aws, local-container, blacksmith-testbox, or cloudflare\n", + const result = runDefaultWrapper( ["run", "--provider", "blacksmith-testbox", "--id", "tbx_owned", "--", "echo ok"], { env: testHomeEnv(home) }, ); @@ -941,8 +972,7 @@ describe("scripts/crabbox-wrapper", () => { "utf8", ); - const result = runWrapper( - "provider: hetzner, aws, local-container, blacksmith-testbox, or cloudflare\n", + const result = runDefaultWrapper( ["run", "--provider", "blacksmith-testbox", "--id", id, "--", "echo ok"], { env: { ...testHomeEnv(home), XDG_STATE_HOME: stateRoot } }, ); @@ -952,8 +982,7 @@ describe("scripts/crabbox-wrapper", () => { expect(result.stderr).toContain(`lease ${id} is claimed by repo /tmp/other-repo`); expect(result.stderr).toContain(`use --reclaim to claim it for ${repoRoot}`); - const reclaimed = runWrapper( - "provider: hetzner, aws, local-container, blacksmith-testbox, or cloudflare\n", + const reclaimed = runDefaultWrapper( ["run", "--provider", "blacksmith-testbox", "--id", id, "--reclaim", "--", "echo ok"], { env: { ...testHomeEnv(home), XDG_STATE_HOME: stateRoot } }, ); @@ -982,8 +1011,7 @@ describe("scripts/crabbox-wrapper", () => { }; writeFileSync(claimPath, `${JSON.stringify(originalClaim)}\n`, "utf8"); - const result = runWrapper( - "provider: hetzner, aws, local-container, blacksmith-testbox, or cloudflare\n", + const result = runDefaultWrapper( ["run", "--provider", "blacksmith-testbox", "--id", id, "--", "echo ok"], { env: { @@ -1017,8 +1045,7 @@ describe("scripts/crabbox-wrapper", () => { writeFileSync(claimPath, `${JSON.stringify(originalClaim)}\n`, "utf8"); writeFileSync(decoyPath, `${JSON.stringify(decoyClaim)}\n`, "utf8"); - const result = runWrapper( - "provider: hetzner, aws, local-container, blacksmith-testbox, or cloudflare\n", + const result = runDefaultWrapper( ["run", "--provider", "blacksmith-testbox", "--keep", "--timing-json", "--", "echo ok"], { env: { @@ -1062,8 +1089,7 @@ describe("scripts/crabbox-wrapper", () => { writeFileSync(siblingPath, `${JSON.stringify(siblingClaim)}\n`, "utf8"); writeFileSync(foreignPath, `${JSON.stringify(foreignClaim)}\n`, "utf8"); - const result = runWrapper( - "provider: hetzner, aws, local-container, blacksmith-testbox, or cloudflare\n", + const result = runDefaultWrapper( ["run", "--provider", "blacksmith-testbox", "--keep", "--", "echo ok"], { env: { @@ -1098,8 +1124,7 @@ describe("scripts/crabbox-wrapper", () => { mkdirSync(path.dirname(claimPath), { recursive: true }); writeFileSync(claimPath, `${JSON.stringify(originalClaim)}\n`, "utf8"); - const result = runWrapper( - "provider: hetzner, aws, local-container, blacksmith-testbox, or cloudflare\n", + const result = runDefaultWrapper( ["run", "--provider", "blacksmith-testbox", "--keep-on-failure", "--", "false"], { env: { @@ -1136,8 +1161,7 @@ describe("scripts/crabbox-wrapper", () => { }; writeFileSync(claimPath, `${JSON.stringify({ ...foreignClaim, repoRoot })}\n`, "utf8"); - const result = runWrapper( - "provider: hetzner, aws, local-container, blacksmith-testbox, or cloudflare\n", + const result = runDefaultWrapper( ["run", "--provider", "blacksmith-testbox", "--id", id, "--", "echo ok"], { env: { @@ -1161,8 +1185,7 @@ describe("scripts/crabbox-wrapper", () => { const home = mkdtempSync(path.join(tmpdir(), "openclaw-crabbox-home-")); tempDirs.push(home); - const result = runWrapper( - "provider: hetzner, aws, local-container, blacksmith-testbox, or cloudflare\n", + const result = runDefaultWrapper( ["run", "--provider", "blacksmith-testbox", "--id", "blue-hermit", "--", "echo ok"], { env: testHomeEnv(home) }, ); @@ -1182,17 +1205,14 @@ describe("scripts/crabbox-wrapper", () => { }); it("exports CI for complete Blacksmith Testbox shell snippets", () => { - const result = runWrapper( - "provider: hetzner, aws, local-container, blacksmith-testbox, or cloudflare\n", - [ - "run", - "--provider", - "blacksmith-testbox", - "--shell", - "--", - "cd packages && pnpm install && pnpm build", - ], - ); + const result = runDefaultWrapper([ + "run", + "--provider", + "blacksmith-testbox", + "--shell", + "--", + "cd packages && pnpm install && pnpm build", + ]); expect(result.status).toBe(0); expect(parseFakeCrabboxOutput(result).args).toEqual([ @@ -1206,10 +1226,7 @@ describe("scripts/crabbox-wrapper", () => { }); it("only forces the short local-container Docker work root on Linux", () => { - const result = runWrapper( - "provider: hetzner, aws, local-container, blacksmith-testbox, or cloudflare\n", - ["run", "--provider", "local-container", "--", "echo ok"], - ); + const result = runDefaultWrapper(["run", "--provider", "local-container", "--", "echo ok"]); expect(result.status).toBe(0); const expectedMessage = @@ -1222,10 +1239,15 @@ describe("scripts/crabbox-wrapper", () => { }); it("defaults AWS macOS runs to on-demand capacity", () => { - const result = runWrapper( - "provider: hetzner, aws, local-container, blacksmith-testbox, or cloudflare\n", - ["run", "--provider", "aws", "--target", "macos", "--", "echo ok"], - ); + const result = runDefaultWrapper([ + "run", + "--provider", + "aws", + "--target", + "macos", + "--", + "echo ok", + ]); expect(result.status).toBe(0); expect(parseFakeCrabboxOutput(result).args).toEqual([ @@ -1329,16 +1351,12 @@ describe("scripts/crabbox-wrapper", () => { }); it("keeps the AWS provider env for Windows runs when Azure is unavailable", () => { - const result = runWrapper( - "provider: hetzner, aws, local-container, blacksmith-testbox, or cloudflare\n", - ["run", "--target", "windows", "--", "echo ok"], - { - env: { - CRABBOX_PROVIDER: "aws", - OPENCLAW_CRABBOX_ALLOW_DIRECT_AWS: "1", - }, + const result = runDefaultWrapper(["run", "--target", "windows", "--", "echo ok"], { + env: { + CRABBOX_PROVIDER: "aws", + OPENCLAW_CRABBOX_ALLOW_DIRECT_AWS: "1", }, - ); + }); expect(result.status).toBe(0); expect(parseFakeCrabboxOutput(result).args).toEqual([ @@ -1584,11 +1602,9 @@ describe("scripts/crabbox-wrapper", () => { }); it("fails closed for AWS proof when broker auth is missing", () => { - const result = runWrapper( - "provider: hetzner, aws, local-container, blacksmith-testbox, or cloudflare\n", - ["run", "--provider", "aws", "--", "echo ok"], - { configJson: { coordinator: "", brokerAuth: "missing" } }, - ); + const result = runDefaultWrapper(["run", "--provider", "aws", "--", "echo ok"], { + configJson: { coordinator: "", brokerAuth: "missing" }, + }); expect(result.status).toBe(2); expect(result.stdout).toBe(""); @@ -1599,14 +1615,10 @@ describe("scripts/crabbox-wrapper", () => { }); it("fails closed for AWS proof when broker auth is stale", () => { - const result = runWrapper( - "provider: hetzner, aws, local-container, blacksmith-testbox, or cloudflare\n", - ["run", "--provider", "aws", "--", "echo ok"], - { - configJson: { coordinator: "https://crabbox.openclaw.ai", brokerAuth: "configured" }, - env: { OPENCLAW_FAKE_CRABBOX_WHOAMI_STATUS: "1" }, - }, - ); + const result = runDefaultWrapper(["run", "--provider", "aws", "--", "echo ok"], { + configJson: { coordinator: "https://crabbox.openclaw.ai", brokerAuth: "configured" }, + env: { OPENCLAW_FAKE_CRABBOX_WHOAMI_STATUS: "1" }, + }); expect(result.status).toBe(2); expect(result.stdout).toBe(""); @@ -1614,14 +1626,10 @@ describe("scripts/crabbox-wrapper", () => { }); it("allows explicit direct AWS debugging without broker auth", () => { - const result = runWrapper( - "provider: hetzner, aws, local-container, blacksmith-testbox, or cloudflare\n", - ["run", "--provider", "aws", "--", "echo ok"], - { - configJson: { coordinator: "", brokerAuth: "missing" }, - env: { OPENCLAW_CRABBOX_ALLOW_DIRECT_AWS: "1" }, - }, - ); + const result = runDefaultWrapper(["run", "--provider", "aws", "--", "echo ok"], { + configJson: { coordinator: "", brokerAuth: "missing" }, + env: { OPENCLAW_CRABBOX_ALLOW_DIRECT_AWS: "1" }, + }); expect(result.status).toBe(0); expect(parseFakeCrabboxOutput(result).args).toEqual([ @@ -1634,10 +1642,7 @@ describe("scripts/crabbox-wrapper", () => { }); it("defaults AWS macOS warmups to on-demand capacity", () => { - const result = runWrapper( - "provider: hetzner, aws, local-container, blacksmith-testbox, or cloudflare\n", - ["warmup", "--provider", "aws", "--target", "macos"], - ); + const result = runDefaultWrapper(["warmup", "--provider", "aws", "--target", "macos"]); expect(result.status).toBe(0); expect(parseFakeCrabboxOutput(result).args).toEqual([ @@ -1701,10 +1706,16 @@ describe("scripts/crabbox-wrapper", () => { }); it("bootstraps only Node for raw AWS macOS node commands", () => { - const result = runWrapper( - "provider: hetzner, aws, local-container, blacksmith-testbox, or cloudflare\n", - ["run", "--provider", "aws", "--target", "macos", "--", "node", "--version"], - ); + const result = runDefaultWrapper([ + "run", + "--provider", + "aws", + "--target", + "macos", + "--", + "node", + "--version", + ]); const output = parseFakeCrabboxOutput(result); const remoteCommand = normalizeShellLineEndings(output.args.at(-1) ?? ""); @@ -1719,23 +1730,20 @@ describe("scripts/crabbox-wrapper", () => { }); it("preflights Swift 6.2 for raw AWS macOS Swift app builds", () => { - const result = runWrapper( - "provider: hetzner, aws, local-container, blacksmith-testbox, or cloudflare\n", - [ - "run", - "--provider", - "aws", - "--target", - "macos", - "--", - "swift", - "build", - "--package-path", - "apps/macos", - "--product", - "OpenClaw", - ], - ); + const result = runDefaultWrapper([ + "run", + "--provider", + "aws", + "--target", + "macos", + "--", + "swift", + "build", + "--package-path", + "apps/macos", + "--product", + "OpenClaw", + ]); const output = parseFakeCrabboxOutput(result); const remoteCommand = normalizeShellLineEndings(output.args.at(-1) ?? ""); @@ -1756,10 +1764,16 @@ describe("scripts/crabbox-wrapper", () => { }); it("preflights Swift and JS tooling for raw AWS macOS package scripts", () => { - const result = runWrapper( - "provider: hetzner, aws, local-container, blacksmith-testbox, or cloudflare\n", - ["run", "--provider", "aws", "--target", "macos", "--", "pnpm", "mac:package"], - ); + const result = runDefaultWrapper([ + "run", + "--provider", + "aws", + "--target", + "macos", + "--", + "pnpm", + "mac:package", + ]); const output = parseFakeCrabboxOutput(result); const remoteCommand = normalizeShellLineEndings(output.args.at(-1) ?? ""); @@ -1774,10 +1788,18 @@ describe("scripts/crabbox-wrapper", () => { }); it("preserves sanitized env pnpm package commands when Swift preflight is needed", () => { - const result = runWrapper( - "provider: hetzner, aws, local-container, blacksmith-testbox, or cloudflare\n", - ["run", "--provider", "aws", "--target", "macos", "--", "env", "-i", "pnpm", "mac:package"], - ); + const result = runDefaultWrapper([ + "run", + "--provider", + "aws", + "--target", + "macos", + "--", + "env", + "-i", + "pnpm", + "mac:package", + ]); const output = parseFakeCrabboxOutput(result); const remoteCommand = normalizeShellLineEndings(output.args.at(-1) ?? ""); @@ -1789,21 +1811,18 @@ describe("scripts/crabbox-wrapper", () => { }); it("preserves sanitized env package script commands when JS tooling is needed", () => { - const result = runWrapper( - "provider: hetzner, aws, local-container, blacksmith-testbox, or cloudflare\n", - [ - "run", - "--provider", - "aws", - "--target", - "macos", - "--", - "env", - "-i", - "bash", - "scripts/package-mac-app.sh", - ], - ); + const result = runDefaultWrapper([ + "run", + "--provider", + "aws", + "--target", + "macos", + "--", + "env", + "-i", + "bash", + "scripts/package-mac-app.sh", + ]); const output = parseFakeCrabboxOutput(result); const remoteCommand = normalizeShellLineEndings(output.args.at(-1) ?? ""); @@ -1819,23 +1838,20 @@ describe("scripts/crabbox-wrapper", () => { }); it("does not bootstrap JS tooling for env package scripts behind command", () => { - const result = runWrapper( - "provider: hetzner, aws, local-container, blacksmith-testbox, or cloudflare\n", - [ - "run", - "--provider", - "aws", - "--target", - "macos", - "--", - "command", - "env", - "-i", - "PATH=/usr/bin:/bin", - "bash", - "scripts/package-mac-app.sh", - ], - ); + const result = runDefaultWrapper([ + "run", + "--provider", + "aws", + "--target", + "macos", + "--", + "command", + "env", + "-i", + "PATH=/usr/bin:/bin", + "bash", + "scripts/package-mac-app.sh", + ]); const output = parseFakeCrabboxOutput(result); const remoteCommand = normalizeShellLineEndings(output.args.at(-1) ?? ""); @@ -1848,10 +1864,7 @@ describe("scripts/crabbox-wrapper", () => { ["--", "bash", "-lc", "env -i PATH=/usr/bin:/bin bash scripts/package-mac-app.sh"], ["--shell", "--", "bash -lc 'env -i PATH=/usr/bin:/bin bash scripts/package-mac-app.sh'"], ]) { - const result = runWrapper( - "provider: hetzner, aws, local-container, blacksmith-testbox, or cloudflare\n", - ["run", "--provider", "aws", "--target", "macos", ...args], - ); + const result = runDefaultWrapper(["run", "--provider", "aws", "--target", "macos", ...args]); const output = parseFakeCrabboxOutput(result); const remoteCommand = normalizeShellLineEndings(output.args.at(-1) ?? ""); @@ -1862,19 +1875,16 @@ describe("scripts/crabbox-wrapper", () => { }); it("does not bootstrap Corepack for nested env pnpm commands that cannot be shimmed", () => { - const result = runWrapper( - "provider: hetzner, aws, local-container, blacksmith-testbox, or cloudflare\n", - [ - "run", - "--provider", - "aws", - "--target", - "macos", - "--shell", - "--", - "bash -lc 'env -i PATH=/usr/bin:/bin pnpm --version'", - ], - ); + const result = runDefaultWrapper([ + "run", + "--provider", + "aws", + "--target", + "macos", + "--shell", + "--", + "bash -lc 'env -i PATH=/usr/bin:/bin pnpm --version'", + ]); const output = parseFakeCrabboxOutput(result); const remoteCommand = normalizeShellLineEndings(output.args.at(-1) ?? ""); @@ -1884,19 +1894,16 @@ describe("scripts/crabbox-wrapper", () => { }); it("preserves sanitized env shell package scripts when JS tooling is needed", () => { - const result = runWrapper( - "provider: hetzner, aws, local-container, blacksmith-testbox, or cloudflare\n", - [ - "run", - "--provider", - "aws", - "--target", - "macos", - "--shell", - "--", - "env -i bash scripts/package-mac-app.sh", - ], - ); + const result = runDefaultWrapper([ + "run", + "--provider", + "aws", + "--target", + "macos", + "--shell", + "--", + "env -i bash scripts/package-mac-app.sh", + ]); const output = parseFakeCrabboxOutput(result); const remoteCommand = normalizeShellLineEndings(output.args.at(-1) ?? ""); @@ -1922,10 +1929,16 @@ describe("scripts/crabbox-wrapper", () => { "openclaw_crabbox_env -i bash scripts/package-mac-app.sh >out.log 2>&1", ], ] as const) { - const result = runWrapper( - "provider: hetzner, aws, local-container, blacksmith-testbox, or cloudflare\n", - ["run", "--provider", "aws", "--target", "macos", "--shell", "--", shellCommand], - ); + const result = runDefaultWrapper([ + "run", + "--provider", + "aws", + "--target", + "macos", + "--shell", + "--", + shellCommand, + ]); const output = parseFakeCrabboxOutput(result); const remoteCommand = normalizeShellLineEndings(output.args.at(-1) ?? ""); @@ -1940,19 +1953,16 @@ describe("scripts/crabbox-wrapper", () => { }); it("preserves trailing shell segments when sanitizing env package scripts", () => { - const result = runWrapper( - "provider: hetzner, aws, local-container, blacksmith-testbox, or cloudflare\n", - [ - "run", - "--provider", - "aws", - "--target", - "macos", - "--shell", - "--", - "env -i bash scripts/package-mac-app.sh && echo done", - ], - ); + const result = runDefaultWrapper([ + "run", + "--provider", + "aws", + "--target", + "macos", + "--shell", + "--", + "env -i bash scripts/package-mac-app.sh && echo done", + ]); const output = parseFakeCrabboxOutput(result); const remoteCommand = normalizeShellLineEndings(output.args.at(-1) ?? ""); @@ -1978,10 +1988,16 @@ describe("scripts/crabbox-wrapper", () => { "time openclaw_crabbox_env -i bash scripts/package-mac-app.sh", ], ] as const) { - const result = runWrapper( - "provider: hetzner, aws, local-container, blacksmith-testbox, or cloudflare\n", - ["run", "--provider", "aws", "--target", "macos", "--shell", "--", shellCommand], - ); + const result = runDefaultWrapper([ + "run", + "--provider", + "aws", + "--target", + "macos", + "--shell", + "--", + shellCommand, + ]); const output = parseFakeCrabboxOutput(result); const remoteCommand = normalizeShellLineEndings(output.args.at(-1) ?? ""); @@ -2005,10 +2021,16 @@ describe("scripts/crabbox-wrapper", () => { "{ openclaw_crabbox_env -i bash scripts/package-mac-app.sh; }", ], ] as const) { - const result = runWrapper( - "provider: hetzner, aws, local-container, blacksmith-testbox, or cloudflare\n", - ["run", "--provider", "aws", "--target", "macos", "--shell", "--", shellCommand], - ); + const result = runDefaultWrapper([ + "run", + "--provider", + "aws", + "--target", + "macos", + "--shell", + "--", + shellCommand, + ]); const output = parseFakeCrabboxOutput(result); const remoteCommand = normalizeShellLineEndings(output.args.at(-1) ?? ""); @@ -2034,10 +2056,16 @@ describe("scripts/crabbox-wrapper", () => { "env -i bash scripts/package-mac-app.sh", "EOF", ].join("\n"); - const result = runWrapper( - "provider: hetzner, aws, local-container, blacksmith-testbox, or cloudflare\n", - ["run", "--provider", "aws", "--target", "macos", "--shell", "--", shellCommand], - ); + const result = runDefaultWrapper([ + "run", + "--provider", + "aws", + "--target", + "macos", + "--shell", + "--", + shellCommand, + ]); const output = parseFakeCrabboxOutput(result); const remoteCommand = normalizeShellLineEndings(output.args.at(-1) ?? ""); @@ -2051,19 +2079,16 @@ describe("scripts/crabbox-wrapper", () => { }); it("preserves control-flow shell segments when sanitizing env package scripts", () => { - const result = runWrapper( - "provider: hetzner, aws, local-container, blacksmith-testbox, or cloudflare\n", - [ - "run", - "--provider", - "aws", - "--target", - "macos", - "--shell", - "--", - "if true; then env -i bash scripts/package-mac-app.sh; fi", - ], - ); + const result = runDefaultWrapper([ + "run", + "--provider", + "aws", + "--target", + "macos", + "--shell", + "--", + "if true; then env -i bash scripts/package-mac-app.sh; fi", + ]); const output = parseFakeCrabboxOutput(result); const remoteCommand = normalizeShellLineEndings(output.args.at(-1) ?? ""); @@ -2097,10 +2122,16 @@ describe("scripts/crabbox-wrapper", () => { "PATH=/usr/bin:/bin openclaw_crabbox_env -i bash scripts/package-mac-app.sh", ], ] as const) { - const result = runWrapper( - "provider: hetzner, aws, local-container, blacksmith-testbox, or cloudflare\n", - ["run", "--provider", "aws", "--target", "macos", "--shell", "--", shellCommand], - ); + const result = runDefaultWrapper([ + "run", + "--provider", + "aws", + "--target", + "macos", + "--shell", + "--", + shellCommand, + ]); const output = parseFakeCrabboxOutput(result); const remoteCommand = normalizeShellLineEndings(output.args.at(-1) ?? ""); @@ -2114,92 +2145,61 @@ describe("scripts/crabbox-wrapper", () => { } }); - it("preflights Swift and JS tooling for raw AWS macOS shell-launched package scripts", () => { - const result = runWrapper( - "provider: hetzner, aws, local-container, blacksmith-testbox, or cloudflare\n", - ["run", "--provider", "aws", "--target", "macos", "--", "bash", "scripts/package-mac-app.sh"], - ); - + it.each([ + { + name: "preflights Swift and JS tooling for raw AWS macOS shell-launched package scripts", + script: "scripts/package-mac-app.sh", + }, + { + name: "preflights Swift and JS tooling for raw AWS macOS dist package scripts", + script: "scripts/package-mac-dist.sh", + }, + { + name: "preflights Swift and JS tooling for raw AWS macOS restart scripts", + script: "scripts/restart-mac.sh", + }, + { + js: false, + name: "keeps raw AWS macOS build-and-run scripts Swift-only", + script: "scripts/build-and-run-mac.sh", + }, + ])("$name", ({ js = true, script }) => { + const result = runDefaultWrapper([ + "run", + "--provider", + "aws", + "--target", + "macos", + "--", + "bash", + script, + ]); const output = parseFakeCrabboxOutput(result); const remoteCommand = normalizeShellLineEndings(output.args.at(-1) ?? ""); - expect(result.status).toBe(0); - expect(output.args).toContain("--shell"); - expect(remoteCommand).toContain("openclaw_crabbox_bootstrap_macos_js"); - expect(remoteCommand).toContain("pnpm --version >&2"); - expect(remoteCommand).toContain("openclaw_crabbox_require_macos_swift_62"); - expectGroupedShellCommand(remoteCommand, "bash scripts/package-mac-app.sh"); - }); - it("preflights Swift and JS tooling for raw AWS macOS dist package scripts", () => { - const result = runWrapper( - "provider: hetzner, aws, local-container, blacksmith-testbox, or cloudflare\n", - [ - "run", - "--provider", - "aws", - "--target", - "macos", - "--", - "bash", - "scripts/package-mac-dist.sh", - ], - ); - - const output = parseFakeCrabboxOutput(result); - const remoteCommand = normalizeShellLineEndings(output.args.at(-1) ?? ""); - expect(result.status).toBe(0); - expect(output.args).toContain("--shell"); - expect(remoteCommand).toContain("openclaw_crabbox_bootstrap_macos_js"); - expect(remoteCommand).toContain("pnpm --version >&2"); - expect(remoteCommand).toContain("openclaw_crabbox_require_macos_swift_62"); - expectGroupedShellCommand(remoteCommand, "bash scripts/package-mac-dist.sh"); - }); - - it("preflights Swift and JS tooling for raw AWS macOS restart scripts", () => { - const result = runWrapper( - "provider: hetzner, aws, local-container, blacksmith-testbox, or cloudflare\n", - ["run", "--provider", "aws", "--target", "macos", "--", "bash", "scripts/restart-mac.sh"], - ); - - const output = parseFakeCrabboxOutput(result); - const remoteCommand = normalizeShellLineEndings(output.args.at(-1) ?? ""); - expect(result.status).toBe(0); - expect(output.args).toContain("--shell"); - expect(remoteCommand).toContain("openclaw_crabbox_bootstrap_macos_js"); - expect(remoteCommand).toContain("pnpm --version >&2"); - expect(remoteCommand).toContain("openclaw_crabbox_require_macos_swift_62"); - expectGroupedShellCommand(remoteCommand, "bash scripts/restart-mac.sh"); - }); - - it("keeps raw AWS macOS build-and-run scripts Swift-only", () => { - const result = runWrapper( - "provider: hetzner, aws, local-container, blacksmith-testbox, or cloudflare\n", - [ - "run", - "--provider", - "aws", - "--target", - "macos", - "--", - "bash", - "scripts/build-and-run-mac.sh", - ], - ); - - const output = parseFakeCrabboxOutput(result); - const remoteCommand = normalizeShellLineEndings(output.args.at(-1) ?? ""); expect(result.status).toBe(0); expect(output.args).toContain("--shell"); expect(remoteCommand).toContain("openclaw_crabbox_require_macos_swift_62"); - expect(remoteCommand).not.toContain("openclaw_crabbox_bootstrap_macos_js"); - expectGroupedShellCommand(remoteCommand, "bash scripts/build-and-run-mac.sh"); + if (js) { + expect(remoteCommand).toContain("openclaw_crabbox_bootstrap_macos_js"); + expect(remoteCommand).toContain("pnpm --version >&2"); + } else { + expect(remoteCommand).not.toContain("openclaw_crabbox_bootstrap_macos_js"); + } + expectGroupedShellCommand(remoteCommand, `bash ${script}`); }); it("does not preflight Swift for raw AWS macOS commands that only mention package scripts", () => { - const result = runWrapper( - "provider: hetzner, aws, local-container, blacksmith-testbox, or cloudflare\n", - ["run", "--provider", "aws", "--target", "macos", "--", "echo", "scripts/package-mac-app.sh"], - ); + const result = runDefaultWrapper([ + "run", + "--provider", + "aws", + "--target", + "macos", + "--", + "echo", + "scripts/package-mac-app.sh", + ]); const output = parseFakeCrabboxOutput(result); expect(result.status).toBe(0); @@ -2219,8 +2219,7 @@ describe("scripts/crabbox-wrapper", () => { }); it("normalizes inherited Linux UTF-8 locale names for raw AWS macOS bootstrap", () => { - const result = runWrapper( - "provider: hetzner, aws, local-container, blacksmith-testbox, or cloudflare\n", + const result = runDefaultWrapper( ["run", "--provider", "aws", "--target", "macos", "--", "node", "--version"], { env: { @@ -2248,10 +2247,16 @@ describe("scripts/crabbox-wrapper", () => { }); it("bootstraps Bun for raw AWS macOS bun commands", () => { - const result = runWrapper( - "provider: hetzner, aws, local-container, blacksmith-testbox, or cloudflare\n", - ["run", "--provider", "aws", "--target", "macos", "--", "bun", "--version"], - ); + const result = runDefaultWrapper([ + "run", + "--provider", + "aws", + "--target", + "macos", + "--", + "bun", + "--version", + ]); const output = parseFakeCrabboxOutput(result); const remoteCommand = normalizeShellLineEndings(output.args.at(-1) ?? ""); @@ -2270,10 +2275,18 @@ describe("scripts/crabbox-wrapper", () => { }); it("bootstraps Bun for raw AWS macOS env-prefixed bun commands", () => { - const result = runWrapper( - "provider: hetzner, aws, local-container, blacksmith-testbox, or cloudflare\n", - ["run", "--provider", "aws", "--target", "macos", "--", "env", "-i", "bun", "--version"], - ); + const result = runDefaultWrapper([ + "run", + "--provider", + "aws", + "--target", + "macos", + "--", + "env", + "-i", + "bun", + "--version", + ]); const output = parseFakeCrabboxOutput(result); const remoteCommand = normalizeShellLineEndings(output.args.at(-1) ?? ""); @@ -2283,10 +2296,16 @@ describe("scripts/crabbox-wrapper", () => { }); it("bootstraps Corepack for raw AWS macOS pnpm commands", () => { - const result = runWrapper( - "provider: hetzner, aws, local-container, blacksmith-testbox, or cloudflare\n", - ["run", "--provider", "aws", "--target", "macos", "--", "pnpm", "--version"], - ); + const result = runDefaultWrapper([ + "run", + "--provider", + "aws", + "--target", + "macos", + "--", + "pnpm", + "--version", + ]); const output = parseFakeCrabboxOutput(result); const remoteCommand = normalizeShellLineEndings(output.args.at(-1) ?? ""); @@ -2334,164 +2353,81 @@ describe("scripts/crabbox-wrapper", () => { expectGroupedShellCommand(remoteCommand, "pnpm --version"); }); - it("bootstraps Corepack for raw AWS macOS env-prefixed pnpm commands", () => { - const result = runWrapper( - "provider: hetzner, aws, local-container, blacksmith-testbox, or cloudflare\n", - ["run", "--provider", "aws", "--target", "macos", "--", "/usr/bin/env", "pnpm", "--version"], - ); - - const output = parseFakeCrabboxOutput(result); - const remoteCommand = normalizeShellLineEndings(output.args.at(-1) ?? ""); - expect(result.status).toBe(0); - expect(output.args).toContain("--shell"); - expect(remoteCommand).toContain("openclaw_crabbox_bootstrap_macos_js"); - expect(remoteCommand).toContain('corepack enable --install-directory "$PNPM_HOME"'); - expect(remoteCommand).toContain("pnpm --version >&2"); - expectGroupedShellCommand(remoteCommand, "openclaw_crabbox_env pnpm --version"); - }); - - it("bootstraps Corepack for raw AWS macOS env option pnpm commands", () => { - const result = runWrapper( - "provider: hetzner, aws, local-container, blacksmith-testbox, or cloudflare\n", - [ - "run", - "--provider", - "aws", - "--target", - "macos", - "--", - "env", - "-i", - "PATH=/usr/bin:/bin", - "pnpm", - "--version", + it.each([ + { + command: ["/usr/bin/env", "pnpm", "--version"], + expectShell: true, + expectedCommand: "openclaw_crabbox_env pnpm --version", + includes: ['corepack enable --install-directory "$PNPM_HOME"'], + name: "bootstraps Corepack for raw AWS macOS env-prefixed pnpm commands", + }, + { + command: ["env", "-i", "PATH=/usr/bin:/bin", "pnpm", "--version"], + excludes: ["export -f env openclaw_crabbox_env", 'env() { openclaw_crabbox_env "$@"; };'], + expectedCommand: "openclaw_crabbox_env -i PATH=/usr/bin:/bin pnpm --version", + includes: [ + "openclaw_crabbox_env", + "PATH=${OPENCLAW_CRABBOX_BOOTSTRAP_PATH:-$PATH}:${1#PATH=}", ], - ); - - const output = parseFakeCrabboxOutput(result); - const remoteCommand = normalizeShellLineEndings(output.args.at(-1) ?? ""); - expect(result.status).toBe(0); - expect(remoteCommand).toContain("openclaw_crabbox_bootstrap_macos_js"); - expect(remoteCommand).toContain("openclaw_crabbox_env"); - expect(remoteCommand).not.toContain("export -f env openclaw_crabbox_env"); - expect(remoteCommand).not.toContain('env() { openclaw_crabbox_env "$@"; };'); - expect(remoteCommand).toContain("PATH=${OPENCLAW_CRABBOX_BOOTSTRAP_PATH:-$PATH}:${1#PATH=}"); - expect(remoteCommand).toContain("pnpm --version >&2"); - expectGroupedShellCommand( - remoteCommand, - "openclaw_crabbox_env -i PATH=/usr/bin:/bin pnpm --version", - ); - }); - - it("bootstraps Corepack for raw AWS macOS env options before ignore-environment", () => { - const result = runWrapper( - "provider: hetzner, aws, local-container, blacksmith-testbox, or cloudflare\n", - [ - "run", - "--provider", - "aws", - "--target", - "macos", - "--", - "env", - "-u", - "FOO", - "-i", - "PATH=/usr/bin:/bin", - "pnpm", - "--version", + name: "bootstraps Corepack for raw AWS macOS env option pnpm commands", + }, + { + command: ["env", "-u", "FOO", "-i", "PATH=/usr/bin:/bin", "pnpm", "--version"], + expectedCommand: "openclaw_crabbox_env -u FOO -i PATH=/usr/bin:/bin pnpm --version", + includes: ["-u|--unset|-C|--chdir)", "-i|--ignore-environment)"], + name: "bootstraps Corepack for raw AWS macOS env options before ignore-environment", + }, + { + command: ["/usr/bin/env", "-i", "PATH=/usr/bin:/bin", "pnpm", "--version"], + expectShell: true, + expectedCommand: "openclaw_crabbox_env -i PATH=/usr/bin:/bin pnpm --version", + name: "bootstraps Corepack for raw AWS macOS absolute env ignore-environment commands", + }, + { + command: ["/usr/bin/env", "-i", "pnpm", "--version"], + expectedCommand: "openclaw_crabbox_env -i pnpm --version", + includes: [ + 'if [ "$openclaw_env_ignore" = "1" ] && [ "$openclaw_env_path_seen" = "0" ]; then openclaw_env_args+=("PATH=${OPENCLAW_CRABBOX_BOOTSTRAP_PATH:-$PATH}"); fi;', ], - ); - + name: "injects the bootstrapped PATH for raw AWS macOS absolute env -i commands", + }, + ])("$name", ({ command, excludes = [], expectShell, expectedCommand, includes = [] }) => { + const result = runDefaultWrapper([ + "run", + "--provider", + "aws", + "--target", + "macos", + "--", + ...command, + ]); const output = parseFakeCrabboxOutput(result); const remoteCommand = normalizeShellLineEndings(output.args.at(-1) ?? ""); + expect(result.status).toBe(0); - expect(remoteCommand).toContain("openclaw_crabbox_bootstrap_macos_js"); - expect(remoteCommand).toContain("-u|--unset|-C|--chdir)"); - expect(remoteCommand).toContain("-i|--ignore-environment)"); - expect(remoteCommand).toContain("pnpm --version >&2"); - expectGroupedShellCommand( - remoteCommand, - "openclaw_crabbox_env -u FOO -i PATH=/usr/bin:/bin pnpm --version", - ); - }); - - it("bootstraps Corepack for raw AWS macOS absolute env ignore-environment commands", () => { - const result = runWrapper( - "provider: hetzner, aws, local-container, blacksmith-testbox, or cloudflare\n", - [ - "run", - "--provider", - "aws", - "--target", - "macos", - "--", - "/usr/bin/env", - "-i", - "PATH=/usr/bin:/bin", - "pnpm", - "--version", - ], - ); - - const output = parseFakeCrabboxOutput(result); - const remoteCommand = normalizeShellLineEndings(output.args.at(-1) ?? ""); - expect(result.status).toBe(0); - expect(output.args).toContain("--shell"); - expect(remoteCommand).toContain("openclaw_crabbox_bootstrap_macos_js"); - expect(remoteCommand).toContain("pnpm --version >&2"); - expectGroupedShellCommand( - remoteCommand, - "openclaw_crabbox_env -i PATH=/usr/bin:/bin pnpm --version", - ); - }); - - it("injects the bootstrapped PATH for raw AWS macOS absolute env -i commands", () => { - const result = runWrapper( - "provider: hetzner, aws, local-container, blacksmith-testbox, or cloudflare\n", - [ - "run", - "--provider", - "aws", - "--target", - "macos", - "--", - "/usr/bin/env", - "-i", - "pnpm", - "--version", - ], - ); - - const output = parseFakeCrabboxOutput(result); - const remoteCommand = normalizeShellLineEndings(output.args.at(-1) ?? ""); - expect(result.status).toBe(0); - expect(remoteCommand).toContain("openclaw_crabbox_bootstrap_macos_js"); - expect(remoteCommand).toContain( - 'if [ "$openclaw_env_ignore" = "1" ] && [ "$openclaw_env_path_seen" = "0" ]; then openclaw_env_args+=("PATH=${OPENCLAW_CRABBOX_BOOTSTRAP_PATH:-$PATH}"); fi;', - ); - expectGroupedShellCommand(remoteCommand, "openclaw_crabbox_env -i pnpm --version"); + if (expectShell) { + expect(output.args).toContain("--shell"); + } + for (const snippet of [ + "openclaw_crabbox_bootstrap_macos_js", + "pnpm --version >&2", + ...includes, + ]) { + expect(remoteCommand).toContain(snippet); + } + for (const snippet of excludes) { + expect(remoteCommand).not.toContain(snippet); + } + expectGroupedShellCommand(remoteCommand, expectedCommand); }); it("does not rewrite custom env executables for raw AWS macOS ignore-environment commands", () => { - const result = runWrapper( - "provider: hetzner, aws, local-container, blacksmith-testbox, or cloudflare\n", - [ - "run", - "--provider", - "aws", - "--target", - "macos", - "--", - "./tools/env", - "-i", - "pnpm", - "--version", - ], - ); - - const output = parseFakeCrabboxOutput(result); - const remoteCommand = normalizeShellLineEndings(output.args.at(-1) ?? ""); + const { output, remoteCommand, result } = runMacosCommand([ + "./tools/env", + "-i", + "pnpm", + "--version", + ]); expect(result.status).toBe(0); expect(remoteCommand).not.toContain("openclaw_crabbox_bootstrap_macos_js"); expect(output.args).not.toContain("--shell"); @@ -2499,26 +2435,14 @@ describe("scripts/crabbox-wrapper", () => { it("does not bootstrap env ignore-environment commands that bypass shell functions", () => { for (const prefix of ["command", "exec"]) { - const result = runWrapper( - "provider: hetzner, aws, local-container, blacksmith-testbox, or cloudflare\n", - [ - "run", - "--provider", - "aws", - "--target", - "macos", - "--", - prefix, - "env", - "-i", - "PATH=/usr/bin:/bin", - "pnpm", - "--version", - ], - ); - - const output = parseFakeCrabboxOutput(result); - const remoteCommand = normalizeShellLineEndings(output.args.at(-1) ?? ""); + const { output, remoteCommand, result } = runMacosCommand([ + prefix, + "env", + "-i", + "PATH=/usr/bin:/bin", + "pnpm", + "--version", + ]); expect(result.status).toBe(0); expect(remoteCommand).not.toContain("openclaw_crabbox_bootstrap_macos_js"); expect(output.args).not.toContain("--shell"); @@ -2526,25 +2450,13 @@ describe("scripts/crabbox-wrapper", () => { }); it("bootstraps env commands behind command when they keep the inherited PATH", () => { - const result = runWrapper( - "provider: hetzner, aws, local-container, blacksmith-testbox, or cloudflare\n", - [ - "run", - "--provider", - "aws", - "--target", - "macos", - "--", - "command", - "env", - "CI=1", - "pnpm", - "--version", - ], - ); - - const output = parseFakeCrabboxOutput(result); - const remoteCommand = normalizeShellLineEndings(output.args.at(-1) ?? ""); + const { remoteCommand, result } = runMacosCommand([ + "command", + "env", + "CI=1", + "pnpm", + "--version", + ]); expect(result.status).toBe(0); expect(remoteCommand).toContain("openclaw_crabbox_bootstrap_macos_js"); expect(remoteCommand).toContain("pnpm --version >&2"); @@ -2553,13 +2465,7 @@ describe("scripts/crabbox-wrapper", () => { it("does not shadow unrelated env calls in AWS macOS shell commands", () => { const shellScript = "node --version; env -i PATH=/usr/bin:/bin printenv PATH"; - const result = runWrapper( - "provider: hetzner, aws, local-container, blacksmith-testbox, or cloudflare\n", - ["run", "--provider", "aws", "--target", "macos", "--shell", "--", shellScript], - ); - - const output = parseFakeCrabboxOutput(result); - const remoteCommand = normalizeShellLineEndings(output.args.at(-1) ?? ""); + const { remoteCommand, result } = runMacosShell(shellScript); expect(result.status).toBe(0); expect(remoteCommand).toContain("openclaw_crabbox_bootstrap_macos_js"); expect(remoteCommand).toContain("openclaw_crabbox_env"); @@ -2568,36 +2474,19 @@ describe("scripts/crabbox-wrapper", () => { }); it("does not bootstrap env split-string commands after ignore-environment", () => { - const result = runWrapper( - "provider: hetzner, aws, local-container, blacksmith-testbox, or cloudflare\n", - ["run", "--provider", "aws", "--target", "macos", "--", "env", "-i", "-S", "pnpm --version"], - ); - - const output = parseFakeCrabboxOutput(result); - const remoteCommand = normalizeShellLineEndings(output.args.at(-1) ?? ""); + const { output, remoteCommand, result } = runMacosCommand([ + "env", + "-i", + "-S", + "pnpm --version", + ]); expect(result.status).toBe(0); expect(remoteCommand).not.toContain("openclaw_crabbox_bootstrap_macos_js"); expect(output.args).not.toContain("--shell"); }); it("bootstraps Corepack for raw AWS macOS env split-string pnpm commands", () => { - const result = runWrapper( - "provider: hetzner, aws, local-container, blacksmith-testbox, or cloudflare\n", - [ - "run", - "--provider", - "aws", - "--target", - "macos", - "--", - "/usr/bin/env", - "-S", - "pnpm --version", - ], - ); - - const output = parseFakeCrabboxOutput(result); - const remoteCommand = normalizeShellLineEndings(output.args.at(-1) ?? ""); + const { remoteCommand, result } = runMacosCommand(["/usr/bin/env", "-S", "pnpm --version"]); expect(result.status).toBe(0); expect(remoteCommand).toContain("openclaw_crabbox_bootstrap_macos_js"); expect(remoteCommand).toContain("pnpm --version >&2"); @@ -2608,13 +2497,7 @@ describe("scripts/crabbox-wrapper", () => { }); it("bootstraps Corepack for AWS macOS node changed-gate commands", () => { - const result = runWrapper( - "provider: hetzner, aws, local-container, blacksmith-testbox, or cloudflare\n", - ["run", "--provider", "aws", "--target", "macos", "--", "node", "scripts/check-changed.mjs"], - ); - - const output = parseFakeCrabboxOutput(result); - const remoteCommand = normalizeShellLineEndings(output.args.at(-1) ?? ""); + const { remoteCommand, result } = runMacosCommand(["node", "scripts/check-changed.mjs"]); expect(result.status).toBe(0); expect(remoteCommand).toContain("node --version >&2"); expect(remoteCommand).toContain('corepack enable --install-directory "$PNPM_HOME"'); @@ -2626,27 +2509,24 @@ describe("scripts/crabbox-wrapper", () => { }); it("bootstraps Corepack for AWS macOS node option changed-gate commands", () => { - const result = runWrapper( - "provider: hetzner, aws, local-container, blacksmith-testbox, or cloudflare\n", - [ - "run", - "--provider", - "aws", - "--target", - "macos", - "--", - "node", - "--max-old-space-size", - "4096", - "--env-file-if-exists", - ".env", - "--unhandled-rejections", - "strict", - "--trace-warnings", - "--import=tsx", - "scripts/check-changed.mjs", - ], - ); + const result = runDefaultWrapper([ + "run", + "--provider", + "aws", + "--target", + "macos", + "--", + "node", + "--max-old-space-size", + "4096", + "--env-file-if-exists", + ".env", + "--unhandled-rejections", + "strict", + "--trace-warnings", + "--import=tsx", + "scripts/check-changed.mjs", + ]); const output = parseFakeCrabboxOutput(result); const remoteCommand = normalizeShellLineEndings(output.args.at(-1) ?? ""); @@ -2659,21 +2539,18 @@ describe("scripts/crabbox-wrapper", () => { }); it("does not treat node script arguments as changed-gate commands", () => { - const result = runWrapper( - "provider: hetzner, aws, local-container, blacksmith-testbox, or cloudflare\n", - [ - "run", - "--provider", - "aws", - "--target", - "macos", - "--", - "node", - "--trace-warnings", - "scripts/other.mjs", - "scripts/check-changed.mjs", - ], - ); + const result = runDefaultWrapper([ + "run", + "--provider", + "aws", + "--target", + "macos", + "--", + "node", + "--trace-warnings", + "scripts/other.mjs", + "scripts/check-changed.mjs", + ]); const output = parseFakeCrabboxOutput(result); const remoteCommand = normalizeShellLineEndings(output.args.at(-1) ?? ""); @@ -2687,10 +2564,16 @@ describe("scripts/crabbox-wrapper", () => { }); it("preserves shell commands when bootstrapping raw AWS macOS JavaScript commands", () => { - const result = runWrapper( - "provider: hetzner, aws, local-container, blacksmith-testbox, or cloudflare\n", - ["run", "--provider", "aws", "--target", "macos", "--shell", "--", "pnpm check:changed"], - ); + const result = runDefaultWrapper([ + "run", + "--provider", + "aws", + "--target", + "macos", + "--shell", + "--", + "pnpm check:changed", + ]); const output = parseFakeCrabboxOutput(result); const remoteCommand = normalizeShellLineEndings(output.args.at(-1) ?? ""); @@ -2706,10 +2589,16 @@ describe("scripts/crabbox-wrapper", () => { 'repo_tmp=$(node -e "console.log(require(\\"node:os\\").tmpdir())")', "pnpm --version", ].join("\n"); - const result = runWrapper( - "provider: hetzner, aws, local-container, blacksmith-testbox, or cloudflare\n", - ["run", "--provider", "aws", "--target", "macos", "--shell", "--", shellScript], - ); + const result = runDefaultWrapper([ + "run", + "--provider", + "aws", + "--target", + "macos", + "--shell", + "--", + shellScript, + ]); const output = parseFakeCrabboxOutput(result); const remoteCommand = normalizeShellLineEndings(output.args.at(-1) ?? ""); @@ -2721,10 +2610,16 @@ describe("scripts/crabbox-wrapper", () => { it("bootstraps raw AWS macOS shell scripts with env-prefixed JavaScript commands", () => { const shellScript = "/usr/bin/env CI=1 pnpm --version"; - const result = runWrapper( - "provider: hetzner, aws, local-container, blacksmith-testbox, or cloudflare\n", - ["run", "--provider", "aws", "--target", "macos", "--shell", "--", shellScript], - ); + const result = runDefaultWrapper([ + "run", + "--provider", + "aws", + "--target", + "macos", + "--shell", + "--", + shellScript, + ]); const output = parseFakeCrabboxOutput(result); const remoteCommand = normalizeShellLineEndings(output.args.at(-1) ?? ""); @@ -2736,8 +2631,7 @@ describe("scripts/crabbox-wrapper", () => { it("bootstraps AWS macOS script-stdin runs before the uploaded script body", () => { const script = ["set -euo pipefail", "node -v", "pnpm --version"].join("\n"); - const result = runWrapper( - "provider: hetzner, aws, local-container, blacksmith-testbox, or cloudflare\n", + const result = runDefaultWrapper( ["run", "--provider", "aws", "--target", "macos", "--script-stdin"], { input: script }, ); @@ -2759,8 +2653,7 @@ describe("scripts/crabbox-wrapper", () => { it("preserves AWS macOS script-stdin shebang payloads behind the bootstrap wrapper", () => { const script = ["#!/usr/bin/env node", "console.log(process.version);"].join("\n"); - const result = runWrapper( - "provider: hetzner, aws, local-container, blacksmith-testbox, or cloudflare\n", + const result = runDefaultWrapper( ["run", "--provider", "aws", "--target", "macos", "--script-stdin", "--", "arg1"], { input: script }, ); @@ -2786,8 +2679,7 @@ describe("scripts/crabbox-wrapper", () => { "pnpm --version", "bun --version", ].join("\n"); - const result = runWrapper( - "provider: hetzner, aws, local-container, blacksmith-testbox, or cloudflare\n", + const result = runDefaultWrapper( ["run", "--provider", "aws", "--target", "macos", "--script-stdin"], { input: script }, ); @@ -2806,8 +2698,7 @@ describe("scripts/crabbox-wrapper", () => { "set -euo pipefail", "swift build --package-path apps/macos --product OpenClaw", ].join("\n"); - const result = runWrapper( - "provider: hetzner, aws, local-container, blacksmith-testbox, or cloudflare\n", + const result = runDefaultWrapper( ["run", "--provider", "aws", "--target", "macos", "--script-stdin"], { input: script }, ); @@ -2824,8 +2715,7 @@ describe("scripts/crabbox-wrapper", () => { it("preflights Swift and JS for AWS macOS script-stdin package scripts", () => { const script = ["#!/usr/bin/env bash", "set -euo pipefail", "pnpm mac:package"].join("\n"); - const result = runWrapper( - "provider: hetzner, aws, local-container, blacksmith-testbox, or cloudflare\n", + const result = runDefaultWrapper( ["run", "--provider", "aws", "--target", "macos", "--script-stdin"], { input: script }, ); @@ -2841,8 +2731,7 @@ describe("scripts/crabbox-wrapper", () => { it("bootstraps Corepack for AWS macOS script-stdin env shebangs with option values", () => { const script = ["#!/usr/bin/env -C /tmp -u OPENCLAW_FAKE_VAR pnpm", "--version"].join("\n"); - const result = runWrapper( - "provider: hetzner, aws, local-container, blacksmith-testbox, or cloudflare\n", + const result = runDefaultWrapper( ["run", "--provider", "aws", "--target", "macos", "--script-stdin"], { input: script }, ); @@ -2857,8 +2746,7 @@ describe("scripts/crabbox-wrapper", () => { it("bootstraps Bun for AWS macOS script-stdin bun shebangs", () => { const script = ["#!/usr/bin/env bun", "console.log(Bun.version);"].join("\n"); - const result = runWrapper( - "provider: hetzner, aws, local-container, blacksmith-testbox, or cloudflare\n", + const result = runDefaultWrapper( ["run", "--provider", "aws", "--target", "macos", "--script-stdin"], { input: script }, ); @@ -2874,8 +2762,7 @@ describe("scripts/crabbox-wrapper", () => { }); it("does not treat run option values as AWS macOS script-stdin flags", () => { - const result = runWrapper( - "provider: hetzner, aws, local-container, blacksmith-testbox, or cloudflare\n", + const result = runDefaultWrapper( [ "run", "--provider", @@ -2898,417 +2785,139 @@ describe("scripts/crabbox-wrapper", () => { expect(output.scriptContent).toBe(""); }); - it("bootstraps raw AWS macOS shell scripts with setup inside command substitutions", () => { - const shellScript = "version=$(cd repo && pnpm --version)"; - const result = runWrapper( - "provider: hetzner, aws, local-container, blacksmith-testbox, or cloudflare\n", - ["run", "--provider", "aws", "--target", "macos", "--shell", "--", shellScript], - ); + it.each([ + { + name: "bootstraps raw AWS macOS shell scripts with setup inside command substitutions", + shellScript: "version=$(cd repo && pnpm --version)", + }, + { + name: "bootstraps raw AWS macOS shell scripts with assignment-prefix command substitutions", + shellScript: "TOOL_ROOT=$(pwd) pnpm --version", + }, + { + name: "bootstraps raw AWS macOS shell scripts with case branches inside command substitutions", + shellScript: 'version=$(case "$pm" in pnpm) pnpm --version ;; esac)', + }, + { + name: "bootstraps raw AWS macOS shell scripts with grouped setup inside command substitutions", + shellScript: 'echo "$( (echo setup); pnpm --version )"', + }, + { + name: "bootstraps raw AWS macOS shell scripts after comments and setup commands", + shellScript: ["# setup", "cd repo && pnpm --version"].join("\n"), + }, + { + name: "bootstraps raw AWS macOS shell scripts after escaped newlines", + shellScript: "cd repo && \\\npnpm --version", + }, + { + expectedCommand: `${remoteChangedGateExport} set -e; exec pnpm check:changed`, + name: "bootstraps raw AWS macOS shell scripts with exec-prefixed JavaScript commands", + shellScript: "set -e; exec pnpm check:changed", + }, + { + name: "bootstraps raw AWS macOS shell scripts with command-prefixed JavaScript commands", + shellScript: "command pnpm --version", + }, + { + name: "bootstraps raw AWS macOS shell scripts with time-prefixed JavaScript commands", + shellScript: "time -p node -e 'process.exit(0)'", + }, + { + name: "bootstraps raw AWS macOS shell scripts with absolute time-prefixed JavaScript commands", + shellScript: "/usr/bin/time -l pnpm --version", + }, + { + name: "bootstraps raw AWS macOS shell scripts with JavaScript control conditions", + shellScript: "if node -e 'process.exit(0)'; then echo ok; fi", + }, + { + name: "bootstraps raw AWS macOS shell scripts with env-prefixed JavaScript control conditions", + shellScript: "if CI=1 pnpm --version; then echo ok; fi", + }, + { + name: "bootstraps raw AWS macOS shell scripts with JavaScript pipeline stages", + shellScript: "echo '{}' | node -e 'process.stdin.resume()'", + }, + { + name: "bootstraps raw AWS macOS shell scripts after background setup commands", + shellScript: "setup_task & pnpm --version", + }, + { + name: "bootstraps raw AWS macOS shell scripts with JavaScript else branches", + shellScript: "if test -d node_modules; then echo cached; else pnpm --version; fi", + }, + { + name: "bootstraps raw AWS macOS shell scripts with JavaScript case branches", + shellScript: 'case "$(uname -m)" in arm64|x64) pnpm --version ;; esac', + }, + ])("$name", ({ expectedCommand, shellScript }) => { + const { remoteCommand, result } = runMacosShell(shellScript); - const output = parseFakeCrabboxOutput(result); - const remoteCommand = normalizeShellLineEndings(output.args.at(-1) ?? ""); expect(result.status).toBe(0); expect(remoteCommand).toContain("openclaw_crabbox_bootstrap_macos_js"); - expectGroupedShellCommand(remoteCommand, shellScript); + expectGroupedShellCommand(remoteCommand, expectedCommand ?? shellScript); }); - it("bootstraps raw AWS macOS shell scripts with assignment-prefix command substitutions", () => { - const shellScript = "TOOL_ROOT=$(pwd) pnpm --version"; - const result = runWrapper( - "provider: hetzner, aws, local-container, blacksmith-testbox, or cloudflare\n", - ["run", "--provider", "aws", "--target", "macos", "--shell", "--", shellScript], - ); + it.each([ + { + name: "does not bootstrap raw AWS macOS shell scripts for JavaScript-named case labels", + shellScript: 'case "$packageManager" in pnpm) echo "$packageManager" ;; esac', + }, + { + expectSingleShell: true, + name: "does not bootstrap raw AWS macOS shell scripts that only mention JavaScript tools", + shellScript: 'echo "node and pnpm are documented here"', + }, + { + name: "does not bootstrap raw AWS macOS shell scripts for quoted JavaScript tool mentions", + shellScript: 'echo "docs; pnpm --version"', + }, + { + name: "does not bootstrap raw AWS macOS shell scripts for inline comment mentions", + shellScript: "echo ok # $(pnpm --version)", + }, + { + name: "does not bootstrap raw AWS macOS shell scripts for reserved words in arguments", + shellScript: "echo then pnpm --version && echo use-case", + }, + { + name: "does not bootstrap raw AWS macOS shell scripts for arithmetic expansion names", + shellScript: "node=1; echo $((node + 1))", + }, + { + name: "does not bootstrap raw AWS macOS shell scripts for quoted assignment mentions", + shellScript: 'MSG="use pnpm here" printf "%s\\n" "$MSG"', + }, + { + name: "does not bootstrap raw AWS macOS shell scripts for command lookup checks", + shellScript: "command -v pnpm", + }, + { + name: "does not bootstrap raw AWS macOS shell scripts for timed command lookup checks", + shellScript: "/usr/bin/time -l command -v pnpm", + }, + ])("$name", ({ expectSingleShell, shellScript }) => { + const { output, remoteCommand, result } = runMacosShell(shellScript); - const output = parseFakeCrabboxOutput(result); - const remoteCommand = normalizeShellLineEndings(output.args.at(-1) ?? ""); - expect(result.status).toBe(0); - expect(remoteCommand).toContain("openclaw_crabbox_bootstrap_macos_js"); - expectGroupedShellCommand(remoteCommand, shellScript); - }); - - it("bootstraps raw AWS macOS shell scripts with case branches inside command substitutions", () => { - const shellScript = 'version=$(case "$pm" in pnpm) pnpm --version ;; esac)'; - const result = runWrapper( - "provider: hetzner, aws, local-container, blacksmith-testbox, or cloudflare\n", - ["run", "--provider", "aws", "--target", "macos", "--shell", "--", shellScript], - ); - - const output = parseFakeCrabboxOutput(result); - const remoteCommand = normalizeShellLineEndings(output.args.at(-1) ?? ""); - expect(result.status).toBe(0); - expect(remoteCommand).toContain("openclaw_crabbox_bootstrap_macos_js"); - expectGroupedShellCommand(remoteCommand, shellScript); - }); - - it("bootstraps raw AWS macOS shell scripts with grouped setup inside command substitutions", () => { - const shellScript = 'echo "$( (echo setup); pnpm --version )"'; - const result = runWrapper( - "provider: hetzner, aws, local-container, blacksmith-testbox, or cloudflare\n", - ["run", "--provider", "aws", "--target", "macos", "--shell", "--", shellScript], - ); - - const output = parseFakeCrabboxOutput(result); - const remoteCommand = normalizeShellLineEndings(output.args.at(-1) ?? ""); - expect(result.status).toBe(0); - expect(remoteCommand).toContain("openclaw_crabbox_bootstrap_macos_js"); - expectGroupedShellCommand(remoteCommand, shellScript); - }); - - it("bootstraps raw AWS macOS shell scripts after comments and setup commands", () => { - const shellScript = ["# setup", "cd repo && pnpm --version"].join("\n"); - const result = runWrapper( - "provider: hetzner, aws, local-container, blacksmith-testbox, or cloudflare\n", - ["run", "--provider", "aws", "--target", "macos", "--shell", "--", shellScript], - ); - - const output = parseFakeCrabboxOutput(result); - const remoteCommand = normalizeShellLineEndings(output.args.at(-1) ?? ""); - expect(result.status).toBe(0); - expect(remoteCommand).toContain("openclaw_crabbox_bootstrap_macos_js"); - expectGroupedShellCommand(remoteCommand, shellScript); - }); - - it("bootstraps raw AWS macOS shell scripts after escaped newlines", () => { - const shellScript = "cd repo && \\\npnpm --version"; - const result = runWrapper( - "provider: hetzner, aws, local-container, blacksmith-testbox, or cloudflare\n", - ["run", "--provider", "aws", "--target", "macos", "--shell", "--", shellScript], - ); - - const output = parseFakeCrabboxOutput(result); - const remoteCommand = normalizeShellLineEndings(output.args.at(-1) ?? ""); - expect(result.status).toBe(0); - expect(remoteCommand).toContain("openclaw_crabbox_bootstrap_macos_js"); - expectGroupedShellCommand(remoteCommand, shellScript); - }); - - it("bootstraps raw AWS macOS shell scripts with exec-prefixed JavaScript commands", () => { - const shellScript = "set -e; exec pnpm check:changed"; - const result = runWrapper( - "provider: hetzner, aws, local-container, blacksmith-testbox, or cloudflare\n", - ["run", "--provider", "aws", "--target", "macos", "--shell", "--", shellScript], - ); - - const output = parseFakeCrabboxOutput(result); - const remoteCommand = normalizeShellLineEndings(output.args.at(-1) ?? ""); - expect(result.status).toBe(0); - expect(remoteCommand).toContain("openclaw_crabbox_bootstrap_macos_js"); - expectGroupedShellCommand(remoteCommand, `${remoteChangedGateExport} ${shellScript}`); - }); - - it("bootstraps raw AWS macOS shell scripts with command-prefixed JavaScript commands", () => { - const shellScript = "command pnpm --version"; - const result = runWrapper( - "provider: hetzner, aws, local-container, blacksmith-testbox, or cloudflare\n", - ["run", "--provider", "aws", "--target", "macos", "--shell", "--", shellScript], - ); - - const output = parseFakeCrabboxOutput(result); - const remoteCommand = normalizeShellLineEndings(output.args.at(-1) ?? ""); - expect(result.status).toBe(0); - expect(remoteCommand).toContain("openclaw_crabbox_bootstrap_macos_js"); - expectGroupedShellCommand(remoteCommand, shellScript); - }); - - it("bootstraps raw AWS macOS shell scripts with time-prefixed JavaScript commands", () => { - const shellScript = "time -p node -e 'process.exit(0)'"; - const result = runWrapper( - "provider: hetzner, aws, local-container, blacksmith-testbox, or cloudflare\n", - ["run", "--provider", "aws", "--target", "macos", "--shell", "--", shellScript], - ); - - const output = parseFakeCrabboxOutput(result); - const remoteCommand = normalizeShellLineEndings(output.args.at(-1) ?? ""); - expect(result.status).toBe(0); - expect(remoteCommand).toContain("openclaw_crabbox_bootstrap_macos_js"); - expectGroupedShellCommand(remoteCommand, shellScript); - }); - - it("bootstraps raw AWS macOS shell scripts with absolute time-prefixed JavaScript commands", () => { - const shellScript = "/usr/bin/time -l pnpm --version"; - const result = runWrapper( - "provider: hetzner, aws, local-container, blacksmith-testbox, or cloudflare\n", - ["run", "--provider", "aws", "--target", "macos", "--shell", "--", shellScript], - ); - - const output = parseFakeCrabboxOutput(result); - const remoteCommand = normalizeShellLineEndings(output.args.at(-1) ?? ""); - expect(result.status).toBe(0); - expect(remoteCommand).toContain("openclaw_crabbox_bootstrap_macos_js"); - expectGroupedShellCommand(remoteCommand, shellScript); - }); - - it("bootstraps raw AWS macOS shell scripts with JavaScript control conditions", () => { - const shellScript = "if node -e 'process.exit(0)'; then echo ok; fi"; - const result = runWrapper( - "provider: hetzner, aws, local-container, blacksmith-testbox, or cloudflare\n", - ["run", "--provider", "aws", "--target", "macos", "--shell", "--", shellScript], - ); - - const output = parseFakeCrabboxOutput(result); - const remoteCommand = normalizeShellLineEndings(output.args.at(-1) ?? ""); - expect(result.status).toBe(0); - expect(remoteCommand).toContain("openclaw_crabbox_bootstrap_macos_js"); - expectGroupedShellCommand(remoteCommand, shellScript); - }); - - it("bootstraps raw AWS macOS shell scripts with env-prefixed JavaScript control conditions", () => { - const shellScript = "if CI=1 pnpm --version; then echo ok; fi"; - const result = runWrapper( - "provider: hetzner, aws, local-container, blacksmith-testbox, or cloudflare\n", - ["run", "--provider", "aws", "--target", "macos", "--shell", "--", shellScript], - ); - - const output = parseFakeCrabboxOutput(result); - const remoteCommand = normalizeShellLineEndings(output.args.at(-1) ?? ""); - expect(result.status).toBe(0); - expect(remoteCommand).toContain("openclaw_crabbox_bootstrap_macos_js"); - expectGroupedShellCommand(remoteCommand, shellScript); - }); - - it("bootstraps raw AWS macOS shell scripts with JavaScript pipeline stages", () => { - const shellScript = "echo '{}' | node -e 'process.stdin.resume()'"; - const result = runWrapper( - "provider: hetzner, aws, local-container, blacksmith-testbox, or cloudflare\n", - ["run", "--provider", "aws", "--target", "macos", "--shell", "--", shellScript], - ); - - const output = parseFakeCrabboxOutput(result); - const remoteCommand = normalizeShellLineEndings(output.args.at(-1) ?? ""); - expect(result.status).toBe(0); - expect(remoteCommand).toContain("openclaw_crabbox_bootstrap_macos_js"); - expectGroupedShellCommand(remoteCommand, shellScript); - }); - - it("bootstraps raw AWS macOS shell scripts after background setup commands", () => { - const shellScript = "setup_task & pnpm --version"; - const result = runWrapper( - "provider: hetzner, aws, local-container, blacksmith-testbox, or cloudflare\n", - ["run", "--provider", "aws", "--target", "macos", "--shell", "--", shellScript], - ); - - const output = parseFakeCrabboxOutput(result); - const remoteCommand = normalizeShellLineEndings(output.args.at(-1) ?? ""); - expect(result.status).toBe(0); - expect(remoteCommand).toContain("openclaw_crabbox_bootstrap_macos_js"); - expectGroupedShellCommand(remoteCommand, shellScript); - }); - - it("bootstraps raw AWS macOS shell scripts with JavaScript else branches", () => { - const shellScript = "if test -d node_modules; then echo cached; else pnpm --version; fi"; - const result = runWrapper( - "provider: hetzner, aws, local-container, blacksmith-testbox, or cloudflare\n", - ["run", "--provider", "aws", "--target", "macos", "--shell", "--", shellScript], - ); - - const output = parseFakeCrabboxOutput(result); - const remoteCommand = normalizeShellLineEndings(output.args.at(-1) ?? ""); - expect(result.status).toBe(0); - expect(remoteCommand).toContain("openclaw_crabbox_bootstrap_macos_js"); - expectGroupedShellCommand(remoteCommand, shellScript); - }); - - it("bootstraps raw AWS macOS shell scripts with JavaScript case branches", () => { - const shellScript = 'case "$(uname -m)" in arm64|x64) pnpm --version ;; esac'; - const result = runWrapper( - "provider: hetzner, aws, local-container, blacksmith-testbox, or cloudflare\n", - ["run", "--provider", "aws", "--target", "macos", "--shell", "--", shellScript], - ); - - const output = parseFakeCrabboxOutput(result); - const remoteCommand = normalizeShellLineEndings(output.args.at(-1) ?? ""); - expect(result.status).toBe(0); - expect(remoteCommand).toContain("openclaw_crabbox_bootstrap_macos_js"); - expectGroupedShellCommand(remoteCommand, shellScript); - }); - - it("does not bootstrap raw AWS macOS shell scripts for JavaScript-named case labels", () => { - const shellScript = 'case "$packageManager" in pnpm) echo "$packageManager" ;; esac'; - const result = runWrapper( - "provider: hetzner, aws, local-container, blacksmith-testbox, or cloudflare\n", - ["run", "--provider", "aws", "--target", "macos", "--shell", "--", shellScript], - ); - - const output = parseFakeCrabboxOutput(result); - const remoteCommand = normalizeShellLineEndings(output.args.at(-1) ?? ""); - expect(result.status).toBe(0); - expect(remoteCommand).not.toContain("openclaw_crabbox_bootstrap_macos_js"); - }); - - it("does not bootstrap raw AWS macOS shell scripts that only mention JavaScript tools", () => { - const result = runWrapper( - "provider: hetzner, aws, local-container, blacksmith-testbox, or cloudflare\n", - [ - "run", - "--provider", - "aws", - "--target", - "macos", - "--shell", - "--", - 'echo "node and pnpm are documented here"', - ], - ); - - const output = parseFakeCrabboxOutput(result); - const remoteCommand = normalizeShellLineEndings(output.args.at(-1) ?? ""); - expect(result.status).toBe(0); - expect(output.args.filter((arg) => arg === "--shell")).toHaveLength(1); - expect(remoteCommand).not.toContain("openclaw_crabbox_bootstrap_macos_js"); - }); - - it("does not bootstrap raw AWS macOS shell scripts for quoted JavaScript tool mentions", () => { - const result = runWrapper( - "provider: hetzner, aws, local-container, blacksmith-testbox, or cloudflare\n", - [ - "run", - "--provider", - "aws", - "--target", - "macos", - "--shell", - "--", - 'echo "docs; pnpm --version"', - ], - ); - - const output = parseFakeCrabboxOutput(result); - const remoteCommand = normalizeShellLineEndings(output.args.at(-1) ?? ""); - expect(result.status).toBe(0); - expect(remoteCommand).not.toContain("openclaw_crabbox_bootstrap_macos_js"); - }); - - it("does not bootstrap raw AWS macOS shell scripts for inline comment mentions", () => { - const result = runWrapper( - "provider: hetzner, aws, local-container, blacksmith-testbox, or cloudflare\n", - [ - "run", - "--provider", - "aws", - "--target", - "macos", - "--shell", - "--", - "echo ok # $(pnpm --version)", - ], - ); - - const output = parseFakeCrabboxOutput(result); - const remoteCommand = normalizeShellLineEndings(output.args.at(-1) ?? ""); - expect(result.status).toBe(0); - expect(remoteCommand).not.toContain("openclaw_crabbox_bootstrap_macos_js"); - }); - - it("does not bootstrap raw AWS macOS shell scripts for reserved words in arguments", () => { - const result = runWrapper( - "provider: hetzner, aws, local-container, blacksmith-testbox, or cloudflare\n", - [ - "run", - "--provider", - "aws", - "--target", - "macos", - "--shell", - "--", - "echo then pnpm --version && echo use-case", - ], - ); - - const output = parseFakeCrabboxOutput(result); - const remoteCommand = normalizeShellLineEndings(output.args.at(-1) ?? ""); - expect(result.status).toBe(0); - expect(remoteCommand).not.toContain("openclaw_crabbox_bootstrap_macos_js"); - }); - - it("does not bootstrap raw AWS macOS shell scripts for arithmetic expansion names", () => { - const result = runWrapper( - "provider: hetzner, aws, local-container, blacksmith-testbox, or cloudflare\n", - [ - "run", - "--provider", - "aws", - "--target", - "macos", - "--shell", - "--", - "node=1; echo $((node + 1))", - ], - ); - - const output = parseFakeCrabboxOutput(result); - const remoteCommand = normalizeShellLineEndings(output.args.at(-1) ?? ""); - expect(result.status).toBe(0); - expect(remoteCommand).not.toContain("openclaw_crabbox_bootstrap_macos_js"); - }); - - it("does not bootstrap raw AWS macOS shell scripts for quoted assignment mentions", () => { - const result = runWrapper( - "provider: hetzner, aws, local-container, blacksmith-testbox, or cloudflare\n", - [ - "run", - "--provider", - "aws", - "--target", - "macos", - "--shell", - "--", - 'MSG="use pnpm here" printf "%s\\n" "$MSG"', - ], - ); - - const output = parseFakeCrabboxOutput(result); - const remoteCommand = normalizeShellLineEndings(output.args.at(-1) ?? ""); - expect(result.status).toBe(0); - expect(remoteCommand).not.toContain("openclaw_crabbox_bootstrap_macos_js"); - }); - - it("does not bootstrap raw AWS macOS shell scripts for command lookup checks", () => { - const result = runWrapper( - "provider: hetzner, aws, local-container, blacksmith-testbox, or cloudflare\n", - ["run", "--provider", "aws", "--target", "macos", "--shell", "--", "command -v pnpm"], - ); - - const output = parseFakeCrabboxOutput(result); - const remoteCommand = normalizeShellLineEndings(output.args.at(-1) ?? ""); - expect(result.status).toBe(0); - expect(remoteCommand).not.toContain("openclaw_crabbox_bootstrap_macos_js"); - }); - - it("does not bootstrap raw AWS macOS shell scripts for timed command lookup checks", () => { - const result = runWrapper( - "provider: hetzner, aws, local-container, blacksmith-testbox, or cloudflare\n", - [ - "run", - "--provider", - "aws", - "--target", - "macos", - "--shell", - "--", - "/usr/bin/time -l command -v pnpm", - ], - ); - - const output = parseFakeCrabboxOutput(result); - const remoteCommand = normalizeShellLineEndings(output.args.at(-1) ?? ""); expect(result.status).toBe(0); + if (expectSingleShell) { + expect(output.args.filter((arg) => arg === "--shell")).toHaveLength(1); + } expect(remoteCommand).not.toContain("openclaw_crabbox_bootstrap_macos_js"); }); it("groups shell commands so fallbacks cannot mask AWS macOS bootstrap failures", () => { - const result = runWrapper( - "provider: hetzner, aws, local-container, blacksmith-testbox, or cloudflare\n", - [ - "run", - "--provider", - "aws", - "--target", - "macos", - "--shell", - "--", - "pnpm check:changed || true", - ], - ); + const result = runDefaultWrapper([ + "run", + "--provider", + "aws", + "--target", + "macos", + "--shell", + "--", + "pnpm check:changed || true", + ]); const output = parseFakeCrabboxOutput(result); const remoteCommand = normalizeShellLineEndings(output.args.at(-1) ?? ""); @@ -3321,10 +2930,16 @@ describe("scripts/crabbox-wrapper", () => { }); it("does not bootstrap non-macOS AWS JavaScript commands", () => { - const result = runWrapper( - "provider: hetzner, aws, local-container, blacksmith-testbox, or cloudflare\n", - ["run", "--provider", "aws", "--target", "linux", "--", "pnpm", "--version"], - ); + const result = runDefaultWrapper([ + "run", + "--provider", + "aws", + "--target", + "linux", + "--", + "pnpm", + "--version", + ]); const output = parseFakeCrabboxOutput(result); expect(result.status).toBe(0); @@ -3510,17 +3125,13 @@ describe("scripts/crabbox-wrapper", () => { } it("falls back to normal sync decisions when git is missing from PATH", () => { - const result = runWrapper( - "provider: hetzner, aws, local-container, blacksmith-testbox, or cloudflare\n", - ["run", "--provider", "aws", "--", "echo ok"], - { - gitResponses: { - [GIT_COMMON_DIR_KEY]: { status: 1 }, - [GIT_CONFIG_SPARSE_KEY]: { status: 1 }, - [GIT_SPARSE_LIST_KEY]: { status: 1 }, - }, + const result = runDefaultWrapper(["run", "--provider", "aws", "--", "echo ok"], { + gitResponses: { + [GIT_COMMON_DIR_KEY]: { status: 1 }, + [GIT_CONFIG_SPARSE_KEY]: { status: 1 }, + [GIT_SPARSE_LIST_KEY]: { status: 1 }, }, - ); + }); expect(result.status).toBe(0); expect(parseFakeCrabboxOutput(result).args).toContain("aws"); @@ -3550,10 +3161,7 @@ describe("scripts/crabbox-wrapper", () => { }); it("keeps unsupported provider selections rejected", () => { - const result = runWrapper( - "provider: hetzner, aws, local-container, blacksmith-testbox, or cloudflare\n", - ["run", "--provider", "bogus", "--", "echo ok"], - ); + const result = runDefaultWrapper(["run", "--provider", "bogus", "--", "echo ok"]); expect(result.status).toBe(2); expect(result.stderr).toContain("selected binary does not advertise provider bogus"); @@ -3607,8 +3215,7 @@ describe("scripts/crabbox-wrapper", () => { }); it("uses a temporary full checkout for clean sparse Blacksmith syncs", () => { - const result = runWrapper( - "provider: hetzner, aws, local-container, blacksmith-testbox, or cloudflare\n", + const result = runDefaultWrapper( [ "run", "--provider", @@ -3635,8 +3242,7 @@ describe("scripts/crabbox-wrapper", () => { }); it("uses a temporary full checkout for clean sparse AWS syncs", () => { - const result = runWrapper( - "provider: hetzner, aws, local-container, blacksmith-testbox, or cloudflare\n", + const result = runDefaultWrapper( ["run", "--provider", "aws", "--", "corepack", "pnpm", "check:changed"], { gitResponses: { @@ -3656,8 +3262,7 @@ describe("scripts/crabbox-wrapper", () => { }); it("uses a temporary full checkout when clean sparse AWS syncs reuse a lease", () => { - const result = runWrapper( - "provider: hetzner, aws, local-container, blacksmith-testbox, or cloudflare\n", + const result = runDefaultWrapper( [ "run", "--provider", @@ -3685,16 +3290,9 @@ describe("scripts/crabbox-wrapper", () => { }); it("bootstraps Git metadata for sparse changed gates on remote raw syncs", () => { - const result = runWrapper( - "provider: hetzner, aws, local-container, blacksmith-testbox, or cloudflare\n", + const result = runDefaultWrapper( ["run", "--provider", "aws", "--", "corepack", "pnpm", "check:changed"], - { - gitResponses: { - [GIT_CONFIG_SPARSE_KEY]: { stdout: "true\n" }, - [GIT_STATUS_PORCELAIN_KEY]: { stdout: "" }, - [GIT_MERGE_BASE_MAIN_HEAD_KEY]: { stdout: "abc123\n" }, - }, - }, + sparseChangedGateOptions, ); const output = parseFakeCrabboxOutput(result); @@ -3709,8 +3307,7 @@ describe("scripts/crabbox-wrapper", () => { }); it("uses an explicit release base for changed-gate sync and remote Git metadata", () => { - const result = runWrapper( - "provider: hetzner, aws, local-container, blacksmith-testbox, or cloudflare\n", + const result = runDefaultWrapper( [ "run", "--provider", @@ -3753,8 +3350,7 @@ describe("scripts/crabbox-wrapper", () => { }); it("rejects changed-gate revision expressions that cannot be recreated remotely", () => { - const result = runWrapper( - "provider: hetzner, aws, local-container, blacksmith-testbox, or cloudflare\n", + const result = runDefaultWrapper( [ "run", "--provider", @@ -3781,8 +3377,7 @@ describe("scripts/crabbox-wrapper", () => { }); it("rejects compound changed gates with incompatible bases", () => { - const result = runWrapper( - "provider: hetzner, aws, local-container, blacksmith-testbox, or cloudflare\n", + const result = runDefaultWrapper( [ "run", "--provider", @@ -3813,8 +3408,7 @@ describe("scripts/crabbox-wrapper", () => { const syntheticHeadMarker = path.join(markerDir, "synthetic-head"); const rootCommitMarker = path.join(markerDir, "root-commit"); const selfContainedBundleMarker = path.join(markerDir, "self-contained-bundle"); - const result = runWrapper( - "provider: hetzner, aws, local-container, blacksmith-testbox, or cloudflare\n", + const result = runDefaultWrapper( ["run", "--provider", "aws", "--", "corepack", "pnpm", "check:changed"], { env: { @@ -3846,8 +3440,7 @@ describe("scripts/crabbox-wrapper", () => { it("transports changed-gate bundles larger than the child-process buffer", () => { const bundleBytes = 2 * 1024 * 1024; - const result = runWrapper( - "provider: hetzner, aws, local-container, blacksmith-testbox, or cloudflare\n", + const result = runDefaultWrapper( ["run", "--provider", "aws", "--", "corepack", "pnpm", "check:changed"], { env: { @@ -3875,8 +3468,7 @@ describe("scripts/crabbox-wrapper", () => { const bundle = "synthetic-bundle"; writeFileSync(victimPath, victimContents, "utf8"); - const result = runWrapper( - "provider: hetzner, aws, local-container, blacksmith-testbox, or cloudflare\n", + const result = runDefaultWrapper( ["run", "--provider", "aws", "--", "corepack", "pnpm", "check:changed"], { env: { @@ -3898,8 +3490,7 @@ describe("scripts/crabbox-wrapper", () => { ); it("bootstraps Git metadata for non-sparse changed gates on remote raw syncs", () => { - const result = runWrapper( - "provider: hetzner, aws, local-container, blacksmith-testbox, or cloudflare\n", + const result = runDefaultWrapper( ["run", "--provider", "aws", "--", "corepack", "pnpm", "check:changed"], { gitResponses: { @@ -3924,8 +3515,7 @@ describe("scripts/crabbox-wrapper", () => { }); it("bootstraps Git metadata for env-prefixed sparse changed gates", () => { - const result = runWrapper( - "provider: hetzner, aws, local-container, blacksmith-testbox, or cloudflare\n", + const result = runDefaultWrapper( [ "run", "--provider", @@ -3939,13 +3529,7 @@ describe("scripts/crabbox-wrapper", () => { "pnpm", "check:changed", ], - { - gitResponses: { - [GIT_CONFIG_SPARSE_KEY]: { stdout: "true\n" }, - [GIT_STATUS_PORCELAIN_KEY]: { stdout: "" }, - [GIT_MERGE_BASE_MAIN_HEAD_KEY]: { stdout: "abc123\n" }, - }, - }, + sparseChangedGateOptions, ); const output = parseFakeCrabboxOutput(result); @@ -3959,16 +3543,9 @@ describe("scripts/crabbox-wrapper", () => { }); it("preserves macOS JS bootstrapping for sparse changed gates on remote raw syncs", () => { - const result = runWrapper( - "provider: hetzner, aws, local-container, blacksmith-testbox, or cloudflare\n", + const result = runDefaultWrapper( ["run", "--provider", "aws", "--target", "macos", "--", "pnpm", "check:changed"], - { - gitResponses: { - [GIT_CONFIG_SPARSE_KEY]: { stdout: "true\n" }, - [GIT_STATUS_PORCELAIN_KEY]: { stdout: "" }, - [GIT_MERGE_BASE_MAIN_HEAD_KEY]: { stdout: "abc123\n" }, - }, - }, + sparseChangedGateOptions, ); const output = parseFakeCrabboxOutput(result); @@ -3985,16 +3562,9 @@ describe("scripts/crabbox-wrapper", () => { it("preserves macOS JS and Git bootstraps for sparse shell changed gates with setup", () => { const shellScript = ["set -euo pipefail", "pnpm check:changed"].join("\n"); - const result = runWrapper( - "provider: hetzner, aws, local-container, blacksmith-testbox, or cloudflare\n", + const result = runDefaultWrapper( ["run", "--provider", "aws", "--target", "macos", "--shell", "--", shellScript], - { - gitResponses: { - [GIT_CONFIG_SPARSE_KEY]: { stdout: "true\n" }, - [GIT_STATUS_PORCELAIN_KEY]: { stdout: "" }, - [GIT_MERGE_BASE_MAIN_HEAD_KEY]: { stdout: "abc123\n" }, - }, - }, + sparseChangedGateOptions, ); const output = parseFakeCrabboxOutput(result); @@ -4008,16 +3578,9 @@ describe("scripts/crabbox-wrapper", () => { it("preserves macOS JS and Git bootstraps for shell-wrapped sparse changed gates", () => { const shellScript = "bash -lc 'pnpm check:changed'"; - const result = runWrapper( - "provider: hetzner, aws, local-container, blacksmith-testbox, or cloudflare\n", + const result = runDefaultWrapper( ["run", "--provider", "aws", "--target", "macos", "--shell", "--", shellScript], - { - gitResponses: { - [GIT_CONFIG_SPARSE_KEY]: { stdout: "true\n" }, - [GIT_STATUS_PORCELAIN_KEY]: { stdout: "" }, - [GIT_MERGE_BASE_MAIN_HEAD_KEY]: { stdout: "abc123\n" }, - }, - }, + sparseChangedGateOptions, ); const output = parseFakeCrabboxOutput(result); @@ -4030,16 +3593,9 @@ describe("scripts/crabbox-wrapper", () => { it("does not mistake quoted remote-child markers for shell changed-gate environment", () => { const shellScript = 'echo "OPENCLAW_CHECK_CHANGED_REMOTE_CHILD=1"; pnpm check:changed'; - const result = runWrapper( - "provider: hetzner, aws, local-container, blacksmith-testbox, or cloudflare\n", + const result = runDefaultWrapper( ["run", "--provider", "aws", "--target", "macos", "--shell", "--", shellScript], - { - gitResponses: { - [GIT_CONFIG_SPARSE_KEY]: { stdout: "true\n" }, - [GIT_STATUS_PORCELAIN_KEY]: { stdout: "" }, - [GIT_MERGE_BASE_MAIN_HEAD_KEY]: { stdout: "abc123\n" }, - }, - }, + sparseChangedGateOptions, ); const output = parseFakeCrabboxOutput(result); @@ -4049,181 +3605,56 @@ describe("scripts/crabbox-wrapper", () => { expectGroupedShellCommand(remoteCommand, `${remoteChangedGateExport} ${shellScript}`); }); - it("preserves sparse changed-gate Git bootstrap for assignment-prefix command substitutions", () => { - const shellScript = "TOOL_ROOT=$(pwd) pnpm check:changed"; - const result = runWrapper( - "provider: hetzner, aws, local-container, blacksmith-testbox, or cloudflare\n", - ["run", "--provider", "aws", "--shell", "--", shellScript], - { - gitResponses: { - [GIT_CONFIG_SPARSE_KEY]: { stdout: "true\n" }, - [GIT_STATUS_PORCELAIN_KEY]: { stdout: "" }, - [GIT_MERGE_BASE_MAIN_HEAD_KEY]: { stdout: "abc123\n" }, - }, - }, - ); + it.each([ + { + name: "preserves sparse changed-gate Git bootstrap for assignment-prefix command substitutions", + shellScript: "TOOL_ROOT=$(pwd) pnpm check:changed", + }, + { + name: "preserves sparse changed-gate Git bootstrap for command-prefixed shell commands", + shellScript: "command pnpm check:changed", + }, + { + expectFetch: true, + name: "preserves sparse changed-gate Git bootstrap for bash -lc shell commands", + shellScript: + "env CI=1 NODE_OPTIONS=--max-old-space-size=4096 bash -lc 'set -euo pipefail; pnpm check:changed'", + }, + { + name: "preserves sparse changed-gate Git bootstrap for shell option values before -c", + shellScript: "bash -o pipefail -c 'pnpm check:changed'", + }, + { + name: "preserves sparse changed-gate Git bootstrap for attached shell option values before -c", + shellScript: "bash --rcfile=./ci.bashrc -c 'pnpm check:changed'", + }, + { + name: "preserves sparse changed-gate Git bootstrap for grouped shell options before -c", + shellScript: "bash -eo pipefail -c 'pnpm check:changed'", + }, + { + name: "preserves sparse changed-gate Git bootstrap for absolute time-prefixed shell commands", + shellScript: "/usr/bin/time -l pnpm check:changed", + }, + { + expectFetch: true, + name: "preserves sparse changed-gate Git bootstrap for timeout-wrapped shell commands", + shellScript: + "/usr/bin/time -v timeout 1200s node --max-old-space-size=4096 scripts/check-changed.mjs --base origin/main --head HEAD", + }, + ])("$name", ({ expectFetch, shellScript }) => { + const { remoteCommand, result } = runSparseShell(shellScript); - const output = parseFakeCrabboxOutput(result); - const remoteCommand = normalizeShellLineEndings(output.args.at(-1) ?? ""); expect(result.status).toBe(0); expect(remoteCommand).toContain("git init -q"); - expect(remoteCommand).toContain(`&& ${remoteChangedGateExport} ${shellScript}`); - }); - - it("preserves sparse changed-gate Git bootstrap for command-prefixed shell commands", () => { - const shellScript = "command pnpm check:changed"; - const result = runWrapper( - "provider: hetzner, aws, local-container, blacksmith-testbox, or cloudflare\n", - ["run", "--provider", "aws", "--shell", "--", shellScript], - { - gitResponses: { - [GIT_CONFIG_SPARSE_KEY]: { stdout: "true\n" }, - [GIT_STATUS_PORCELAIN_KEY]: { stdout: "" }, - [GIT_MERGE_BASE_MAIN_HEAD_KEY]: { stdout: "abc123\n" }, - }, - }, - ); - - const output = parseFakeCrabboxOutput(result); - const remoteCommand = normalizeShellLineEndings(output.args.at(-1) ?? ""); - expect(result.status).toBe(0); - expect(remoteCommand).toContain("git init -q"); - expect(remoteCommand).toContain(`&& ${remoteChangedGateExport} ${shellScript}`); - }); - - it("preserves sparse changed-gate Git bootstrap for bash -lc shell commands", () => { - const shellScript = - "env CI=1 NODE_OPTIONS=--max-old-space-size=4096 bash -lc 'set -euo pipefail; pnpm check:changed'"; - const result = runWrapper( - "provider: hetzner, aws, local-container, blacksmith-testbox, or cloudflare\n", - ["run", "--provider", "aws", "--shell", "--", shellScript], - { - gitResponses: { - [GIT_CONFIG_SPARSE_KEY]: { stdout: "true\n" }, - [GIT_STATUS_PORCELAIN_KEY]: { stdout: "" }, - [GIT_MERGE_BASE_MAIN_HEAD_KEY]: { stdout: "abc123\n" }, - }, - }, - ); - - const output = parseFakeCrabboxOutput(result); - const remoteCommand = normalizeShellLineEndings(output.args.at(-1) ?? ""); - expect(result.status).toBe(0); - expect(remoteCommand).toContain("git init -q"); - expect(remoteCommand).toContain(remoteChangedGateFetch); - expect(remoteCommand).toContain(`&& export ${remoteChangedGateEnvPrefix}; ${shellScript}`); - }); - - it("preserves sparse changed-gate Git bootstrap for shell option values before -c", () => { - const shellScript = "bash -o pipefail -c 'pnpm check:changed'"; - const result = runWrapper( - "provider: hetzner, aws, local-container, blacksmith-testbox, or cloudflare\n", - ["run", "--provider", "aws", "--shell", "--", shellScript], - { - gitResponses: { - [GIT_CONFIG_SPARSE_KEY]: { stdout: "true\n" }, - [GIT_STATUS_PORCELAIN_KEY]: { stdout: "" }, - [GIT_MERGE_BASE_MAIN_HEAD_KEY]: { stdout: "abc123\n" }, - }, - }, - ); - - const output = parseFakeCrabboxOutput(result); - const remoteCommand = normalizeShellLineEndings(output.args.at(-1) ?? ""); - expect(result.status).toBe(0); - expect(remoteCommand).toContain("git init -q"); - expect(remoteCommand).toContain(`&& ${remoteChangedGateExport} ${shellScript}`); - }); - - it("preserves sparse changed-gate Git bootstrap for attached shell option values before -c", () => { - const shellScript = "bash --rcfile=./ci.bashrc -c 'pnpm check:changed'"; - const result = runWrapper( - "provider: hetzner, aws, local-container, blacksmith-testbox, or cloudflare\n", - ["run", "--provider", "aws", "--shell", "--", shellScript], - { - gitResponses: { - [GIT_CONFIG_SPARSE_KEY]: { stdout: "true\n" }, - [GIT_STATUS_PORCELAIN_KEY]: { stdout: "" }, - [GIT_MERGE_BASE_MAIN_HEAD_KEY]: { stdout: "abc123\n" }, - }, - }, - ); - - const output = parseFakeCrabboxOutput(result); - const remoteCommand = normalizeShellLineEndings(output.args.at(-1) ?? ""); - expect(result.status).toBe(0); - expect(remoteCommand).toContain("git init -q"); - expect(remoteCommand).toContain(`&& ${remoteChangedGateExport} ${shellScript}`); - }); - - it("preserves sparse changed-gate Git bootstrap for grouped shell options before -c", () => { - const shellScript = "bash -eo pipefail -c 'pnpm check:changed'"; - const result = runWrapper( - "provider: hetzner, aws, local-container, blacksmith-testbox, or cloudflare\n", - ["run", "--provider", "aws", "--shell", "--", shellScript], - { - gitResponses: { - [GIT_CONFIG_SPARSE_KEY]: { stdout: "true\n" }, - [GIT_STATUS_PORCELAIN_KEY]: { stdout: "" }, - [GIT_MERGE_BASE_MAIN_HEAD_KEY]: { stdout: "abc123\n" }, - }, - }, - ); - - const output = parseFakeCrabboxOutput(result); - const remoteCommand = normalizeShellLineEndings(output.args.at(-1) ?? ""); - expect(result.status).toBe(0); - expect(remoteCommand).toContain("git init -q"); - expect(remoteCommand).toContain(`&& ${remoteChangedGateExport} ${shellScript}`); - }); - - it("preserves sparse changed-gate Git bootstrap for absolute time-prefixed shell commands", () => { - const shellScript = "/usr/bin/time -l pnpm check:changed"; - const result = runWrapper( - "provider: hetzner, aws, local-container, blacksmith-testbox, or cloudflare\n", - ["run", "--provider", "aws", "--shell", "--", shellScript], - { - gitResponses: { - [GIT_CONFIG_SPARSE_KEY]: { stdout: "true\n" }, - [GIT_STATUS_PORCELAIN_KEY]: { stdout: "" }, - [GIT_MERGE_BASE_MAIN_HEAD_KEY]: { stdout: "abc123\n" }, - }, - }, - ); - - const output = parseFakeCrabboxOutput(result); - const remoteCommand = normalizeShellLineEndings(output.args.at(-1) ?? ""); - expect(result.status).toBe(0); - expect(remoteCommand).toContain("git init -q"); - expect(remoteCommand).toContain(`&& ${remoteChangedGateExport} ${shellScript}`); - }); - - it("preserves sparse changed-gate Git bootstrap for timeout-wrapped shell commands", () => { - const shellScript = - "/usr/bin/time -v timeout 1200s node --max-old-space-size=4096 scripts/check-changed.mjs --base origin/main --head HEAD"; - const result = runWrapper( - "provider: hetzner, aws, local-container, blacksmith-testbox, or cloudflare\n", - ["run", "--provider", "aws", "--shell", "--", shellScript], - { - gitResponses: { - [GIT_CONFIG_SPARSE_KEY]: { stdout: "true\n" }, - [GIT_STATUS_PORCELAIN_KEY]: { stdout: "" }, - [GIT_MERGE_BASE_MAIN_HEAD_KEY]: { stdout: "abc123\n" }, - }, - }, - ); - - const output = parseFakeCrabboxOutput(result); - const remoteCommand = normalizeShellLineEndings(output.args.at(-1) ?? ""); - expect(result.status).toBe(0); - expect(remoteCommand).toContain("git init -q"); - expect(remoteCommand).toContain(remoteChangedGateFetch); + if (expectFetch) { + expect(remoteCommand).toContain(remoteChangedGateFetch); + } expect(remoteCommand).toContain(`&& ${remoteChangedGateExport} ${shellScript}`); }); it("preserves sparse changed-gate Git bootstrap for direct timeout-wrapped node commands", () => { - const result = runWrapper( - "provider: hetzner, aws, local-container, blacksmith-testbox, or cloudflare\n", + const result = runDefaultWrapper( [ "run", "--provider", @@ -4238,13 +3669,7 @@ describe("scripts/crabbox-wrapper", () => { "--head", "HEAD", ], - { - gitResponses: { - [GIT_CONFIG_SPARSE_KEY]: { stdout: "true\n" }, - [GIT_STATUS_PORCELAIN_KEY]: { stdout: "" }, - [GIT_MERGE_BASE_MAIN_HEAD_KEY]: { stdout: "abc123\n" }, - }, - }, + sparseChangedGateOptions, ); const output = parseFakeCrabboxOutput(result); @@ -4258,16 +3683,9 @@ describe("scripts/crabbox-wrapper", () => { }); it("preserves sparse changed-gate Git bootstrap for direct timeout-wrapped shell commands", () => { - const result = runWrapper( - "provider: hetzner, aws, local-container, blacksmith-testbox, or cloudflare\n", + const result = runDefaultWrapper( ["run", "--provider", "aws", "--", "timeout", "1200s", "bash", "-lc", "pnpm check:changed"], - { - gitResponses: { - [GIT_CONFIG_SPARSE_KEY]: { stdout: "true\n" }, - [GIT_STATUS_PORCELAIN_KEY]: { stdout: "" }, - [GIT_MERGE_BASE_MAIN_HEAD_KEY]: { stdout: "abc123\n" }, - }, - }, + sparseChangedGateOptions, ); const output = parseFakeCrabboxOutput(result); @@ -4281,16 +3699,9 @@ describe("scripts/crabbox-wrapper", () => { }); it("preserves sparse changed-gate Git bootstrap for direct env -i commands", () => { - const result = runWrapper( - "provider: hetzner, aws, local-container, blacksmith-testbox, or cloudflare\n", + const result = runDefaultWrapper( ["run", "--provider", "aws", "--", "env", "-i", "pnpm", "check:changed"], - { - gitResponses: { - [GIT_CONFIG_SPARSE_KEY]: { stdout: "true\n" }, - [GIT_STATUS_PORCELAIN_KEY]: { stdout: "" }, - [GIT_MERGE_BASE_MAIN_HEAD_KEY]: { stdout: "abc123\n" }, - }, - }, + sparseChangedGateOptions, ); const output = parseFakeCrabboxOutput(result); @@ -4304,16 +3715,9 @@ describe("scripts/crabbox-wrapper", () => { }); it("preserves sparse changed-gate Git bootstrap for direct absolute env -i commands", () => { - const result = runWrapper( - "provider: hetzner, aws, local-container, blacksmith-testbox, or cloudflare\n", + const result = runDefaultWrapper( ["run", "--provider", "aws", "--", "/usr/bin/env", "-i", "pnpm", "check:changed"], - { - gitResponses: { - [GIT_CONFIG_SPARSE_KEY]: { stdout: "true\n" }, - [GIT_STATUS_PORCELAIN_KEY]: { stdout: "" }, - [GIT_MERGE_BASE_MAIN_HEAD_KEY]: { stdout: "abc123\n" }, - }, - }, + sparseChangedGateOptions, ); const output = parseFakeCrabboxOutput(result); @@ -4326,199 +3730,75 @@ describe("scripts/crabbox-wrapper", () => { ); }); - it("does not mark custom env executables outside the sanitized env", () => { - const result = runWrapper( - "provider: hetzner, aws, local-container, blacksmith-testbox, or cloudflare\n", - ["run", "--provider", "aws", "--", "./tools/env", "-i", "pnpm", "check:changed"], - { - gitResponses: { - [GIT_CONFIG_SPARSE_KEY]: { stdout: "true\n" }, - [GIT_STATUS_PORCELAIN_KEY]: { stdout: "" }, - [GIT_MERGE_BASE_MAIN_HEAD_KEY]: { stdout: "abc123\n" }, - }, - }, + it.each([ + { + command: ["./tools/env", "-i", "pnpm", "check:changed"], + name: "does not mark custom env executables outside the sanitized env", + }, + { + command: ["FOO=1", "env", "-i", "pnpm", "check:changed"], + name: "does not mark assignment-prefixed env -i changed gates outside the sanitized env", + }, + { + command: ["timeout", "1200s", "env", "-i", "CI=1", "pnpm", "check:changed"], + name: "does not mark timeout-prefixed env -i changed gates outside the sanitized env", + }, + { + command: ["env", "env", "-i", "pnpm", "check:changed"], + name: "does not mark nested env -i changed gates outside the sanitized env", + }, + { + command: ["--shell", "--", "bash -lc 'env -i CI=1 pnpm check:changed'"], + name: "does not mark shell env -i changed gates outside the sanitized env", + shell: true, + }, + ])("$name", ({ command, shell }) => { + const result = runDefaultWrapper( + ["run", "--provider", "aws", ...(shell ? command : ["--", ...command])], + sparseChangedGateOptions, ); - const output = parseFakeCrabboxOutput(result); + const renderedCommand = shell + ? normalizeShellLineEndings(output.args.at(-1) ?? "") + : output.args.join("\0"); + expect(result.status).toBe(0); - expect(output.args.join("\0")).not.toContain("OPENCLAW_CHECK_CHANGED_REMOTE_CHILD=1"); - expect(output.args.join("\0")).not.toContain("git init -q"); + expect(renderedCommand).not.toContain("OPENCLAW_CHECK_CHANGED_REMOTE_CHILD=1"); + expect(renderedCommand).not.toContain("git init -q"); }); - it("does not mark assignment-prefixed env -i changed gates outside the sanitized env", () => { - const result = runWrapper( - "provider: hetzner, aws, local-container, blacksmith-testbox, or cloudflare\n", - ["run", "--provider", "aws", "--", "FOO=1", "env", "-i", "pnpm", "check:changed"], - { - gitResponses: { - [GIT_CONFIG_SPARSE_KEY]: { stdout: "true\n" }, - [GIT_STATUS_PORCELAIN_KEY]: { stdout: "" }, - [GIT_MERGE_BASE_MAIN_HEAD_KEY]: { stdout: "abc123\n" }, - }, - }, - ); + it.each([ + { + name: "does not treat quoted sparse shell text as a changed gate", + shellScript: 'cat < { + const { remoteCommand, result } = runSparseShell(shellScript); - const output = parseFakeCrabboxOutput(result); - expect(result.status).toBe(0); - expect(output.args.join("\0")).not.toContain("OPENCLAW_CHECK_CHANGED_REMOTE_CHILD=1"); - expect(output.args.join("\0")).not.toContain("git init -q"); - }); - - it("does not mark timeout-prefixed env -i changed gates outside the sanitized env", () => { - const result = runWrapper( - "provider: hetzner, aws, local-container, blacksmith-testbox, or cloudflare\n", - [ - "run", - "--provider", - "aws", - "--", - "timeout", - "1200s", - "env", - "-i", - "CI=1", - "pnpm", - "check:changed", - ], - { - gitResponses: { - [GIT_CONFIG_SPARSE_KEY]: { stdout: "true\n" }, - [GIT_STATUS_PORCELAIN_KEY]: { stdout: "" }, - [GIT_MERGE_BASE_MAIN_HEAD_KEY]: { stdout: "abc123\n" }, - }, - }, - ); - - const output = parseFakeCrabboxOutput(result); - expect(result.status).toBe(0); - expect(output.args.join("\0")).not.toContain("OPENCLAW_CHECK_CHANGED_REMOTE_CHILD=1"); - expect(output.args.join("\0")).not.toContain("git init -q"); - }); - - it("does not mark nested env -i changed gates outside the sanitized env", () => { - const result = runWrapper( - "provider: hetzner, aws, local-container, blacksmith-testbox, or cloudflare\n", - ["run", "--provider", "aws", "--", "env", "env", "-i", "pnpm", "check:changed"], - { - gitResponses: { - [GIT_CONFIG_SPARSE_KEY]: { stdout: "true\n" }, - [GIT_STATUS_PORCELAIN_KEY]: { stdout: "" }, - [GIT_MERGE_BASE_MAIN_HEAD_KEY]: { stdout: "abc123\n" }, - }, - }, - ); - - const output = parseFakeCrabboxOutput(result); - expect(result.status).toBe(0); - expect(output.args.join("\0")).not.toContain("OPENCLAW_CHECK_CHANGED_REMOTE_CHILD=1"); - expect(output.args.join("\0")).not.toContain("git init -q"); - }); - - it("does not mark shell env -i changed gates outside the sanitized env", () => { - const shellScript = "bash -lc 'env -i CI=1 pnpm check:changed'"; - const result = runWrapper( - "provider: hetzner, aws, local-container, blacksmith-testbox, or cloudflare\n", - ["run", "--provider", "aws", "--shell", "--", shellScript], - { - gitResponses: { - [GIT_CONFIG_SPARSE_KEY]: { stdout: "true\n" }, - [GIT_STATUS_PORCELAIN_KEY]: { stdout: "" }, - [GIT_MERGE_BASE_MAIN_HEAD_KEY]: { stdout: "abc123\n" }, - }, - }, - ); - - const output = parseFakeCrabboxOutput(result); - const remoteCommand = normalizeShellLineEndings(output.args.at(-1) ?? ""); - expect(result.status).toBe(0); - expect(remoteCommand).not.toContain("OPENCLAW_CHECK_CHANGED_REMOTE_CHILD=1"); - expect(remoteCommand).not.toContain("git init -q"); - }); - - it("does not treat quoted sparse shell text as a changed gate", () => { - const result = runWrapper( - "provider: hetzner, aws, local-container, blacksmith-testbox, or cloudflare\n", - [ - "run", - "--provider", - "aws", - "--shell", - "--", - 'cat < { - const result = runWrapper( - "provider: hetzner, aws, local-container, blacksmith-testbox, or cloudflare\n", - [ - "run", - "--provider", - "aws", - "--shell", - "--", - "cat <<\\EOF\npnpm check:changed\nEOF\necho done", - ], - { - gitResponses: { - [GIT_CONFIG_SPARSE_KEY]: { stdout: "true\n" }, - [GIT_STATUS_PORCELAIN_KEY]: { stdout: "" }, - [GIT_MERGE_BASE_MAIN_HEAD_KEY]: { stdout: "abc123\n" }, - }, - }, - ); - - const output = parseFakeCrabboxOutput(result); - const remoteCommand = normalizeShellLineEndings(output.args.at(-1) ?? ""); - expect(result.status).toBe(0); - expect(remoteCommand).not.toContain("git init -q"); - }); - - it("does not treat nested heredoc bodies in substitutions as changed gates", () => { - const result = runWrapper( - "provider: hetzner, aws, local-container, blacksmith-testbox, or cloudflare\n", - [ - "run", - "--provider", - "aws", - "--shell", - "--", - 'echo "$(cat < { const shellScript = "cat < { it("bootstraps raw AWS macOS shell scripts for unquoted heredoc command substitutions", () => { const shellScript = "cat < { it("keeps quoted heredoc command substitutions literal", () => { const shellScript = "cat <<'EOF'\n$(pnpm --version)\nEOF"; - const result = runWrapper( - "provider: hetzner, aws, local-container, blacksmith-testbox, or cloudflare\n", - ["run", "--provider", "aws", "--target", "macos", "--shell", "--", shellScript], - ); + const result = runDefaultWrapper([ + "run", + "--provider", + "aws", + "--target", + "macos", + "--shell", + "--", + shellScript, + ]); const output = parseFakeCrabboxOutput(result); const remoteCommand = normalizeShellLineEndings(output.args.at(-1) ?? ""); @@ -4555,16 +3847,9 @@ describe("scripts/crabbox-wrapper", () => { }); it("preserves existing shell changed-gate commands after remote Git bootstrap", () => { - const result = runWrapper( - "provider: hetzner, aws, local-container, blacksmith-testbox, or cloudflare\n", + const result = runDefaultWrapper( ["run", "--provider", "aws", "--shell", "--", "env CI=1 pnpm check:changed"], - { - gitResponses: { - [GIT_CONFIG_SPARSE_KEY]: { stdout: "true\n" }, - [GIT_STATUS_PORCELAIN_KEY]: { stdout: "" }, - [GIT_MERGE_BASE_MAIN_HEAD_KEY]: { stdout: "abc123\n" }, - }, - }, + sparseChangedGateOptions, ); const output = parseFakeCrabboxOutput(result); @@ -4578,8 +3863,7 @@ describe("scripts/crabbox-wrapper", () => { }); it("does not inject the POSIX changed-gate bootstrap for Windows targets", () => { - const result = runWrapper( - "provider: hetzner, aws, local-container, blacksmith-testbox, or cloudflare\n", + const result = runDefaultWrapper( [ "run", "--provider", @@ -4591,13 +3875,7 @@ describe("scripts/crabbox-wrapper", () => { "pnpm", "check:changed", ], - { - gitResponses: { - [GIT_CONFIG_SPARSE_KEY]: { stdout: "true\n" }, - [GIT_STATUS_PORCELAIN_KEY]: { stdout: "" }, - [GIT_MERGE_BASE_MAIN_HEAD_KEY]: { stdout: "abc123\n" }, - }, - }, + sparseChangedGateOptions, ); const output = parseFakeCrabboxOutput(result); @@ -4617,16 +3895,12 @@ describe("scripts/crabbox-wrapper", () => { }); it("uses a temporary full checkout when local-container syncs clean sparse worktrees", () => { - const result = runWrapper( - "provider: hetzner, aws, local-container, blacksmith-testbox, or cloudflare\n", - ["run", "--provider", "local-container", "--", "echo ok"], - { - gitResponses: { - [GIT_CONFIG_SPARSE_KEY]: { stdout: "true\n" }, - [GIT_STATUS_PORCELAIN_KEY]: { stdout: "" }, - }, + const result = runDefaultWrapper(["run", "--provider", "local-container", "--", "echo ok"], { + gitResponses: { + [GIT_CONFIG_SPARSE_KEY]: { stdout: "true\n" }, + [GIT_STATUS_PORCELAIN_KEY]: { stdout: "" }, }, - ); + }); expect(result.status).toBe(0); expect(result.stderr).toContain("syncing from temporary full checkout"); @@ -4637,17 +3911,13 @@ describe("scripts/crabbox-wrapper", () => { const syncRoot = path.join(repoRoot, ".crabbox-test-sync-root"); rmSync(syncRoot, { recursive: true, force: true }); try { - const result = runWrapper( - "provider: hetzner, aws, local-container, blacksmith-testbox, or cloudflare\n", - ["run", "--provider", "aws", "--", "echo ok"], - { - env: { OPENCLAW_CRABBOX_SYNC_TMPDIR: syncRoot }, - gitResponses: { - [GIT_CONFIG_SPARSE_KEY]: { stdout: "true\n" }, - [GIT_STATUS_PORCELAIN_KEY]: { stdout: "" }, - }, + const result = runDefaultWrapper(["run", "--provider", "aws", "--", "echo ok"], { + env: { OPENCLAW_CRABBOX_SYNC_TMPDIR: syncRoot }, + gitResponses: { + [GIT_CONFIG_SPARSE_KEY]: { stdout: "true\n" }, + [GIT_STATUS_PORCELAIN_KEY]: { stdout: "" }, }, - ); + }); const output = parseFakeCrabboxOutput(result); expect(result.status).toBe(0); @@ -4662,20 +3932,16 @@ describe("scripts/crabbox-wrapper", () => { const syncRoot = path.join(repoRoot, ".crabbox-test-low-disk-sync-root"); rmSync(syncRoot, { recursive: true, force: true }); try { - const result = runWrapper( - "provider: hetzner, aws, local-container, blacksmith-testbox, or cloudflare\n", - ["run", "--provider", "aws", "--", "echo ok"], - { - env: { - OPENCLAW_CRABBOX_SYNC_MIN_FREE_BYTES: "999999999999999", - OPENCLAW_CRABBOX_SYNC_TMPDIR: syncRoot, - }, - gitResponses: { - [GIT_CONFIG_SPARSE_KEY]: { stdout: "true\n" }, - [GIT_STATUS_PORCELAIN_KEY]: { stdout: "" }, - }, + const result = runDefaultWrapper(["run", "--provider", "aws", "--", "echo ok"], { + env: { + OPENCLAW_CRABBOX_SYNC_MIN_FREE_BYTES: "999999999999999", + OPENCLAW_CRABBOX_SYNC_TMPDIR: syncRoot, }, - ); + gitResponses: { + [GIT_CONFIG_SPARSE_KEY]: { stdout: "true\n" }, + [GIT_STATUS_PORCELAIN_KEY]: { stdout: "" }, + }, + }); expect(result.status).toBe(1); expect(result.stderr).toContain( @@ -4693,20 +3959,16 @@ describe("scripts/crabbox-wrapper", () => { const syncRoot = path.join(repoRoot, ".crabbox-test-invalid-disk-sync-root"); rmSync(syncRoot, { recursive: true, force: true }); try { - const result = runWrapper( - "provider: hetzner, aws, local-container, blacksmith-testbox, or cloudflare\n", - ["run", "--provider", "aws", "--", "echo ok"], - { - env: { - OPENCLAW_CRABBOX_SYNC_MIN_FREE_BYTES: "1024mb", - OPENCLAW_CRABBOX_SYNC_TMPDIR: syncRoot, - }, - gitResponses: { - [GIT_CONFIG_SPARSE_KEY]: { stdout: "true\n" }, - [GIT_STATUS_PORCELAIN_KEY]: { stdout: "" }, - }, + const result = runDefaultWrapper(["run", "--provider", "aws", "--", "echo ok"], { + env: { + OPENCLAW_CRABBOX_SYNC_MIN_FREE_BYTES: "1024mb", + OPENCLAW_CRABBOX_SYNC_TMPDIR: syncRoot, }, - ); + gitResponses: { + [GIT_CONFIG_SPARSE_KEY]: { stdout: "true\n" }, + [GIT_STATUS_PORCELAIN_KEY]: { stdout: "" }, + }, + }); expect(result.status).toBe(1); expect(result.stderr).toContain( @@ -4722,20 +3984,16 @@ describe("scripts/crabbox-wrapper", () => { const syncRoot = path.join(repoRoot, ".crabbox-test-unsafe-disk-sync-root"); rmSync(syncRoot, { recursive: true, force: true }); try { - const result = runWrapper( - "provider: hetzner, aws, local-container, blacksmith-testbox, or cloudflare\n", - ["run", "--provider", "aws", "--", "echo ok"], - { - env: { - OPENCLAW_CRABBOX_SYNC_MIN_FREE_BYTES: String(Number.MAX_SAFE_INTEGER + 1), - OPENCLAW_CRABBOX_SYNC_TMPDIR: syncRoot, - }, - gitResponses: { - [GIT_CONFIG_SPARSE_KEY]: { stdout: "true\n" }, - [GIT_STATUS_PORCELAIN_KEY]: { stdout: "" }, - }, + const result = runDefaultWrapper(["run", "--provider", "aws", "--", "echo ok"], { + env: { + OPENCLAW_CRABBOX_SYNC_MIN_FREE_BYTES: String(Number.MAX_SAFE_INTEGER + 1), + OPENCLAW_CRABBOX_SYNC_TMPDIR: syncRoot, }, - ); + gitResponses: { + [GIT_CONFIG_SPARSE_KEY]: { stdout: "true\n" }, + [GIT_STATUS_PORCELAIN_KEY]: { stdout: "" }, + }, + }); expect(result.status).toBe(1); expect(result.stderr).toContain( @@ -4751,20 +4009,16 @@ describe("scripts/crabbox-wrapper", () => { const syncRoot = path.join(repoRoot, ".crabbox-test-invalid-keepalive-sync-root"); rmSync(syncRoot, { recursive: true, force: true }); try { - const result = runWrapper( - "provider: hetzner, aws, local-container, blacksmith-testbox, or cloudflare\n", - ["run", "--provider", "aws", "--", "echo ok"], - { - env: { - OPENCLAW_CRABBOX_SYNC_KEEPALIVE_MS: "10ms", - OPENCLAW_CRABBOX_SYNC_TMPDIR: syncRoot, - }, - gitResponses: { - [GIT_CONFIG_SPARSE_KEY]: { stdout: "true\n" }, - [GIT_STATUS_PORCELAIN_KEY]: { stdout: "" }, - }, + const result = runDefaultWrapper(["run", "--provider", "aws", "--", "echo ok"], { + env: { + OPENCLAW_CRABBOX_SYNC_KEEPALIVE_MS: "10ms", + OPENCLAW_CRABBOX_SYNC_TMPDIR: syncRoot, }, - ); + gitResponses: { + [GIT_CONFIG_SPARSE_KEY]: { stdout: "true\n" }, + [GIT_STATUS_PORCELAIN_KEY]: { stdout: "" }, + }, + }); expect(result.status).toBe(1); expect(result.stderr).toContain( @@ -4799,20 +4053,16 @@ describe("scripts/crabbox-wrapper", () => { (process.platform === "win32" ? it.skip : it)( "terminates when sparse-sync temporary full checkouts disappear while Crabbox is running", () => { - const result = runWrapper( - "provider: hetzner, aws, local-container, blacksmith-testbox, or cloudflare\n", - ["run", "--provider", "aws", "--", "echo ok"], - { - env: { - OPENCLAW_CRABBOX_SYNC_KEEPALIVE_MS: "10", - OPENCLAW_FAKE_CRABBOX_DELETE_CWD_ONCE: "1", - }, - gitResponses: { - [GIT_CONFIG_SPARSE_KEY]: { stdout: "true\n" }, - [GIT_STATUS_PORCELAIN_KEY]: { stdout: "" }, - }, + const result = runDefaultWrapper(["run", "--provider", "aws", "--", "echo ok"], { + env: { + OPENCLAW_CRABBOX_SYNC_KEEPALIVE_MS: "10", + OPENCLAW_FAKE_CRABBOX_DELETE_CWD_ONCE: "1", }, - ); + gitResponses: { + [GIT_CONFIG_SPARSE_KEY]: { stdout: "true\n" }, + [GIT_STATUS_PORCELAIN_KEY]: { stdout: "" }, + }, + }); expect(result.status).not.toBe(0); expect(result.stderr).toContain( @@ -4825,20 +4075,16 @@ describe("scripts/crabbox-wrapper", () => { (process.platform === "win32" ? it.skip : it)( "fails successful sparse-sync children when their temporary full checkout vanishes before exit", () => { - const result = runWrapper( - "provider: hetzner, aws, local-container, blacksmith-testbox, or cloudflare\n", - ["run", "--provider", "aws", "--", "echo ok"], - { - env: { - OPENCLAW_CRABBOX_SYNC_KEEPALIVE_MS: "60000", - OPENCLAW_FAKE_CRABBOX_DELETE_CWD_AND_EXIT: "1", - }, - gitResponses: { - [GIT_CONFIG_SPARSE_KEY]: { stdout: "true\n" }, - [GIT_STATUS_PORCELAIN_KEY]: { stdout: "" }, - }, + const result = runDefaultWrapper(["run", "--provider", "aws", "--", "echo ok"], { + env: { + OPENCLAW_CRABBOX_SYNC_KEEPALIVE_MS: "60000", + OPENCLAW_FAKE_CRABBOX_DELETE_CWD_AND_EXIT: "1", }, - ); + gitResponses: { + [GIT_CONFIG_SPARSE_KEY]: { stdout: "true\n" }, + [GIT_STATUS_PORCELAIN_KEY]: { stdout: "" }, + }, + }); expect(result.status).toBe(1); expect(result.stderr).toContain( @@ -4848,8 +4094,7 @@ describe("scripts/crabbox-wrapper", () => { ); it("uses a temporary full checkout when existing AWS leases sync clean sparse worktrees", () => { - const result = runWrapper( - "provider: hetzner, aws, local-container, blacksmith-testbox, or cloudflare\n", + const result = runDefaultWrapper( ["run", "--provider", "aws", "--id", "cbx_existing", "--", "echo ok"], { gitResponses: { @@ -4866,8 +4111,7 @@ describe("scripts/crabbox-wrapper", () => { }); it("uses a temporary full checkout when clean sparse branches differ from the Blacksmith ref", () => { - const result = runWrapper( - "provider: hetzner, aws, local-container, blacksmith-testbox, or cloudflare\n", + const result = runDefaultWrapper( ["run", "--provider", "blacksmith-testbox", "--blacksmith-ref", "main", "--", "echo ok"], { gitResponses: { @@ -4884,8 +4128,7 @@ describe("scripts/crabbox-wrapper", () => { }); it("keeps sparse dirty worktrees on the original checkout", () => { - const result = runWrapper( - "provider: hetzner, aws, local-container, blacksmith-testbox, or cloudflare\n", + const result = runDefaultWrapper( ["run", "--provider", "blacksmith-testbox", "--blacksmith-ref", "main", "--", "echo ok"], { gitResponses: { @@ -4901,8 +4144,7 @@ describe("scripts/crabbox-wrapper", () => { }); it("keeps local artifact paths rooted at the original checkout", () => { - const result = runWrapper( - "provider: hetzner, aws, local-container, blacksmith-testbox, or cloudflare\n", + const result = runDefaultWrapper( [ "run", "--provider", @@ -4939,8 +4181,7 @@ describe("scripts/crabbox-wrapper", () => { const preservedDir = path.join(repoRoot, ".crabbox", "runs", "run_fake"); rmSync(preservedDir, { recursive: true, force: true }); - const result = runWrapper( - "provider: hetzner, aws, local-container, blacksmith-testbox, or cloudflare\n", + const result = runDefaultWrapper( [ "run", "--provider", @@ -4970,8 +4211,7 @@ describe("scripts/crabbox-wrapper", () => { }); it("uses the temporary full checkout for sparse sync-only runs", () => { - const result = runWrapper( - "provider: hetzner, aws, local-container, blacksmith-testbox, or cloudflare\n", + const result = runDefaultWrapper( [ "run", "--provider", diff --git a/test/scripts/package-acceptance-workflow.test.ts b/test/scripts/package-acceptance-workflow.test.ts index e421041ea26c..be76fcb7cd0a 100644 --- a/test/scripts/package-acceptance-workflow.test.ts +++ b/test/scripts/package-acceptance-workflow.test.ts @@ -870,8 +870,6 @@ describe("package acceptance workflow", () => { const crabboxConfig = parse(readFileSync(CRABBOX_CONFIG, "utf8")) as { actions?: { job?: string }; }; - const ignoredWorkflow = readWorkflow(CRABBOX_HYDRATE_WORKFLOW); - void ignoredWorkflow; const workflowText = readFileSync(CRABBOX_HYDRATE_WORKFLOW, "utf8"); const hydrate = workflowJob(CRABBOX_HYDRATE_WORKFLOW, "hydrate"); const hydrateWindowsDaemon = workflowJob(CRABBOX_HYDRATE_WORKFLOW, "hydrate-windows-daemon"); @@ -2945,51 +2943,51 @@ describe("package artifact reuse", () => { }); it.each([ - { telegramEnabled: true, telegramResult: "success" }, - { telegramEnabled: false, telegramResult: "skipped" }, - ])( - "accepts Telegram result $telegramResult when enabled=$telegramEnabled", - ({ telegramEnabled, telegramResult }) => { - const result = runPackageAcceptanceSummary({ telegramEnabled, telegramResult }); - - expect(result.status).toBe(0); - expect(result.stderr).toBe(""); + { + expectedOutput: undefined, + expectedStatus: 0, + name: "accepts Telegram result success when enabled=true", + params: { telegramEnabled: true, telegramResult: "success" }, }, - ); + { + expectedOutput: undefined, + expectedStatus: 0, + name: "accepts Telegram result skipped when enabled=false", + params: { telegramEnabled: false, telegramResult: "skipped" }, + }, + { + expectedOutput: "::error::package_telegram ended with skipped", + expectedStatus: 1, + name: "rejects a skipped Telegram lane when package acceptance enabled it", + params: { telegramEnabled: true, telegramResult: "skipped" }, + }, + { + expectedOutput: "::error::No Docker acceptance transport ran", + expectedStatus: 1, + name: "rejects package acceptance when no Docker transport ran", + params: { + dockerArtifactResult: "skipped", + dockerRegistryResult: "skipped", + telegramEnabled: false, + telegramResult: "skipped", + }, + }, + { + expectedOutput: + "::warning::package_telegram ended with skipped; package acceptance is advisory for this caller.", + expectedStatus: 0, + name: "preserves advisory handling for an unexpectedly skipped Telegram lane", + params: { advisory: true, telegramEnabled: true, telegramResult: "skipped" }, + }, + ] as const)("$name", ({ expectedOutput, expectedStatus, params }) => { + const result = runPackageAcceptanceSummary(params); - it("rejects a skipped Telegram lane when package acceptance enabled it", () => { - const result = runPackageAcceptanceSummary({ - telegramEnabled: true, - telegramResult: "skipped", - }); - - expect(result.status).toBe(1); - expect(result.stdout).toContain("::error::package_telegram ended with skipped"); - }); - - it("rejects package acceptance when no Docker transport ran", () => { - const result = runPackageAcceptanceSummary({ - dockerArtifactResult: "skipped", - dockerRegistryResult: "skipped", - telegramEnabled: false, - telegramResult: "skipped", - }); - - expect(result.status).toBe(1); - expect(result.stdout).toContain("::error::No Docker acceptance transport ran"); - }); - - it("preserves advisory handling for an unexpectedly skipped Telegram lane", () => { - const result = runPackageAcceptanceSummary({ - advisory: true, - telegramEnabled: true, - telegramResult: "skipped", - }); - - expect(result.status).toBe(0); - expect(result.stdout).toContain( - "::warning::package_telegram ended with skipped; package acceptance is advisory for this caller.", - ); + expect(result.status).toBe(expectedStatus); + if (expectedOutput) { + expect(result.stdout).toContain(expectedOutput); + } else { + expect(result.stderr).toBe(""); + } }); it("allows beta callers to make only Telegram package acceptance advisory", () => { @@ -3467,65 +3465,62 @@ describe("package artifact reuse", () => { ); }); - it("accepts a successful dispatched Telegram child", () => { - const result = runReleaseChecksSummary({ - currentAttempt: "2", - currentResult: "success", - }); - - expect(result.status).toBe(0); - expect(result.stderr).toBe(""); - }); - - it.each(["cancelled", "failure", "skipped"] as const)( - "rejects a %s selected Telegram child", - (currentResult) => { - const result = runReleaseChecksSummary({ - currentAttempt: "2", - currentResult, - telegramSelected: true, - }); - - expect(result.status).toBe(1); - expect(result.stdout).toContain( - `::error::qa_live_telegram_release_checks ended with ${currentResult}`, - ); + it.each([ + { + emptyStderr: true, + expected: [], + name: "accepts a successful dispatched Telegram child", + params: { currentAttempt: "2", currentResult: "success" }, + status: 0, }, - ); - - it("accepts a skipped unselected Telegram dispatch", () => { - const result = runReleaseChecksSummary({ - currentAttempt: "2", - currentResult: "skipped", - telegramSelected: false, - }); - - expect(result.status).toBe(0); - }); - - it("keeps target resolution blocking before release children", () => { - const result = runReleaseChecksSummary({ - currentAttempt: "2", - currentResult: "skipped", - resolveResult: "failure", - telegramSelected: false, - }); - - expect(result.status).toBe(1); - expect(result.stdout).toContain("::error::resolve_target ended with failure"); - }); - - it("keeps a cancelled Telegram child non-blocking for Tideclaw alpha", () => { - const result = runReleaseChecksSummary({ - currentAttempt: "2", - currentResult: "cancelled", - workflowRef: "refs/heads/tideclaw/alpha/2026-07-10-1200Z", - }); - - expect(result.status).toBe(0); + ...(["cancelled", "failure", "skipped"] as const).map((currentResult) => ({ + emptyStderr: false, + expected: [`::error::qa_live_telegram_release_checks ended with ${currentResult}`], + name: `rejects a ${currentResult} selected Telegram child`, + params: { currentAttempt: "2", currentResult, telegramSelected: true }, + status: 1, + })), + { + emptyStderr: false, + expected: [], + name: "accepts a skipped unselected Telegram dispatch", + params: { currentAttempt: "2", currentResult: "skipped", telegramSelected: false }, + status: 0, + }, + { + emptyStderr: false, + expected: ["::error::resolve_target ended with failure"], + name: "keeps target resolution blocking before release children", + params: { + currentAttempt: "2", + currentResult: "skipped", + resolveResult: "failure", + telegramSelected: false, + }, + status: 1, + }, + { + emptyStderr: false, + expected: ["qa_live_telegram_release_checks ended with cancelled", "Tideclaw alpha"], + name: "keeps a cancelled Telegram child non-blocking for Tideclaw alpha", + params: { + currentAttempt: "2", + currentResult: "cancelled", + workflowRef: "refs/heads/tideclaw/alpha/2026-07-10-1200Z", + }, + status: 0, + }, + ] as const)("$name", ({ emptyStderr, expected, params, status }) => { + const result = runReleaseChecksSummary(params); const output = `${result.stdout}\n${result.stderr}`; - expect(output).toContain("qa_live_telegram_release_checks ended with cancelled"); - expect(output).toContain("Tideclaw alpha"); + + expect(result.status).toBe(status); + if (emptyStderr) { + expect(result.stderr).toBe(""); + } + for (const snippet of expected ?? []) { + expect(output).toContain(snippet); + } }); it("summarizes start delay separately from execution time in full validation", () => {