fix(testing): keep crabbox sync checkouts durable

This commit is contained in:
Vincent Koc
2026-06-01 20:30:03 +02:00
parent 30819ed3da
commit 7f7f0775ed
2 changed files with 60 additions and 11 deletions

View File

@@ -13,7 +13,7 @@ import {
statSync,
writeFileSync,
} from "node:fs";
import { tmpdir } from "node:os";
import { homedir, tmpdir } from "node:os";
import { delimiter, dirname, extname, isAbsolute, relative, resolve } from "node:path";
import { fileURLToPath } from "node:url";
import { resolvePathEnvKey } from "./windows-cmd-helpers.mjs";
@@ -1540,7 +1540,9 @@ function isWindowsRemoteTarget(commandArgs) {
}
function isNativeWindowsRemoteTarget(commandArgs) {
return isWindowsRemoteTarget(commandArgs) && optionValue(commandArgs, "--windows-mode") !== "wsl2";
return (
isWindowsRemoteTarget(commandArgs) && optionValue(commandArgs, "--windows-mode") !== "wsl2"
);
}
function isAwsMacosRemoteTarget(commandArgs, providerName) {
@@ -1553,14 +1555,14 @@ function isAwsMacosRemoteTarget(commandArgs, providerName) {
function remoteWindowsHydratedNodeModulesBootstrap() {
return [
'$openclawModulesDir = $env:PNPM_CONFIG_MODULES_DIR',
'if ($openclawModulesDir) {',
"$openclawModulesDir = $env:PNPM_CONFIG_MODULES_DIR",
"if ($openclawModulesDir) {",
'if (-not (Test-Path $openclawModulesDir)) { throw "PNPM_CONFIG_MODULES_DIR does not exist: $openclawModulesDir" }',
'$openclawWorkspaceModules = Join-Path (Get-Location).Path "node_modules"',
'$openclawSelfModules = Join-Path $openclawModulesDir "node_modules"',
'if (-not (Test-Path $openclawSelfModules)) { cmd /c mklink /J "$openclawSelfModules" "$openclawModulesDir" | Out-Host; if ($LASTEXITCODE -ne 0) { throw "failed to link hydrated pnpm node_modules" } }',
'if (-not (Test-Path $openclawWorkspaceModules)) { cmd /c mklink /J "$openclawWorkspaceModules" "$openclawModulesDir" | Out-Host; if ($LASTEXITCODE -ne 0) { throw "failed to link workspace node_modules" } }',
'}',
"}",
].join("; ");
}
@@ -1894,8 +1896,23 @@ function shouldUseFullCheckoutForCleanRemoteSync(commandArgs, _providerName) {
return isSparseCheckout() || isChangedGateCommand(runCommandArgs(commandArgs));
}
function defaultFullCheckoutSyncRoot() {
const home = homedir();
if (home) {
return resolve(home, ".cache", "openclaw", "crabbox-sync");
}
return resolve(tmpdir(), "openclaw-crabbox-sync");
}
function fullCheckoutSyncRoot() {
const configured = process.env.OPENCLAW_CRABBOX_SYNC_TMPDIR?.trim();
const root = configured ? resolve(configured) : defaultFullCheckoutSyncRoot();
mkdirSync(root, { recursive: true });
return root;
}
function prepareFullCheckoutForSync(options = {}) {
const dir = mkdtempSync(resolve(tmpdir(), "openclaw-crabbox-sync-"));
const dir = mkdtempSync(resolve(fullCheckoutSyncRoot(), "openclaw-crabbox-sync-"));
let active = false;
const add = gitOutput(["worktree", "add", "--detach", dir, "HEAD"]);
if (add.status !== 0) {

View File

@@ -1,5 +1,13 @@
import { spawnSync } from "node:child_process";
import { chmodSync, mkdirSync, mkdtempSync, rmSync, statSync, writeFileSync } from "node:fs";
import {
chmodSync,
mkdirSync,
mkdtempSync,
readdirSync,
rmSync,
statSync,
writeFileSync,
} from "node:fs";
import { tmpdir } from "node:os";
import path from "node:path";
import { afterAll, beforeAll, describe, expect, it } from "vitest";
@@ -182,6 +190,7 @@ function makeFakeGit(
" exit 0",
"fi",
'if [ "$1" = "worktree" ] && [ "$2" = "remove" ]; then',
' rm -rf "$4"',
" exit 0",
"fi",
...Object.entries(responses).flatMap(([key, response]) => {
@@ -209,7 +218,7 @@ function makeFakeGit(
"if (args[0] === 'worktree' && args[1] === 'add') { fs.mkdirSync(args[3], { recursive: true }); process.exit(0); }",
"if (args[0] === '-C' && args[2] === 'sparse-checkout' && args[3] === 'disable') { process.exit(0); }",
"if (args[0] === '-C' && args[2] === 'reset' && args[3] === '--mixed') { process.exit(0); }",
"if (args[0] === 'worktree' && args[1] === 'remove') { process.exit(0); }",
"if (args[0] === 'worktree' && args[1] === 'remove') { fs.rmSync(args[3], { recursive: true, force: true }); process.exit(0); }",
"const key = args.join('\\u0000');",
"const response = responses.get(key);",
"if (!response) { process.exit(1); }",
@@ -1517,9 +1526,7 @@ describe.concurrent("scripts/crabbox-wrapper", () => {
expect(output.args).toContain("--shell");
expect(remoteCommand).toContain("$openclawModulesDir = $env:PNPM_CONFIG_MODULES_DIR");
expect(remoteCommand).toContain('mklink /J "$openclawSelfModules" "$openclawModulesDir"');
expect(remoteCommand).toContain(
'mklink /J "$openclawWorkspaceModules" "$openclawModulesDir"',
);
expect(remoteCommand).toContain('mklink /J "$openclawWorkspaceModules" "$openclawModulesDir"');
expect(remoteCommand).toContain("corepack pnpm check:changed");
});
@@ -2390,6 +2397,31 @@ describe.concurrent("scripts/crabbox-wrapper", () => {
expect(parseFakeCrabboxOutput(result).cwd).toContain("openclaw-crabbox-sync-");
});
it("creates sparse-sync temporary full checkouts under the durable cache root", () => {
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 output = parseFakeCrabboxOutput(result);
expect(result.status).toBe(0);
expect(output.cwd).toContain(`${syncRoot}${path.sep}openclaw-crabbox-sync-`);
expect(readdirSync(syncRoot)).toEqual([]);
} finally {
rmSync(syncRoot, { recursive: true, force: true });
}
});
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",