fix(release): isolate packaged compile cache

This commit is contained in:
Peter Steinberger
2026-04-28 14:53:27 +01:00
parent f55220d6b9
commit e09ddbd8b6
3 changed files with 89 additions and 2 deletions

View File

@@ -1083,6 +1083,15 @@ gateway_log_ready() {
gateway_smoke_ready() {
gateway_listener_ready && gateway_log_ready
}
stop_openclaw_gateway_processes() {
OPENCLAW_DISABLE_BUNDLED_PLUGINS=1 /opt/homebrew/bin/openclaw gateway stop >/dev/null 2>&1 || true
/usr/bin/pkill -9 -f openclaw-gateway || true
/usr/bin/pkill -9 -f 'openclaw gateway run' || true
/usr/bin/pkill -9 -f 'openclaw.mjs gateway' || true
for pid in \$(/usr/sbin/lsof -tiTCP:18789 -sTCP:LISTEN 2>/dev/null || true); do
/bin/kill -9 "\$pid" 2>/dev/null || true
done
}
if [ -n "\$busy" ]; then
printf 'update still has active npm/pnpm/openclaw processes\n%s\n' "\$busy" >&2
exit 1
@@ -1118,6 +1127,18 @@ if [ "\$gateway_ready" != "1" ]; then
done
fi
if [ "\$gateway_ready" != "1" ]; then
stop_openclaw_gateway_processes
/opt/homebrew/bin/openclaw gateway run --bind loopback --port 18789 --force >/tmp/openclaw-parallels-npm-update-macos-recover-gateway.log 2>&1 </dev/null &
for _ in 1 2 3 4 5 6 7 8 9 10; do
if gateway_smoke_ready; then
gateway_ready=1
break
fi
sleep 2
done
fi
if [ "\$gateway_ready" != "1" ]; then
tail -n 120 /tmp/openclaw-parallels-npm-update-macos-recover-gateway.log 2>/dev/null || true
echo "gateway did not become ready after transport recovery" >&2
exit 1
fi

View File

@@ -5,6 +5,7 @@ import { cleanupTempDirs, makeTempDir } from "../test/helpers/temp-dir.js";
import {
buildOpenClawCompileCacheRespawnPlan,
isSourceCheckoutInstallRoot,
resolveOpenClawCompileCacheDirectory,
resolveEntryInstallRoot,
shouldEnableOpenClawCompileCache,
} from "./entry.compile-cache.js";
@@ -54,6 +55,21 @@ describe("entry compile cache", () => {
).toBe(false);
});
it("scopes packaged compile cache by package install metadata", async () => {
const root = makeTempDir(tempDirs, "openclaw-compile-cache-package-key-");
const packageJsonPath = path.join(root, "package.json");
await fs.writeFile(packageJsonPath, '{"version":"2026.4.27-beta.1"}\n', "utf8");
const directory = resolveOpenClawCompileCacheDirectory({
env: { NODE_COMPILE_CACHE: path.join(root, ".node-cache") },
installRoot: root,
});
expect(directory).toContain(path.join(".node-cache", "openclaw"));
expect(directory).toContain("2026.4.27-beta.1");
expect(path.basename(directory)).toMatch(/^\d+-\d+$/);
});
it("builds a one-shot no-cache respawn plan when source checkout inherits NODE_COMPILE_CACHE", async () => {
const root = makeTempDir(tempDirs, "openclaw-compile-cache-respawn-");
await fs.mkdir(path.join(root, "src"), { recursive: true });

View File

@@ -1,6 +1,7 @@
import { spawnSync } from "node:child_process";
import { existsSync } from "node:fs";
import { existsSync, readFileSync, statSync } from "node:fs";
import { enableCompileCache, getCompileCacheDir } from "node:module";
import os from "node:os";
import path from "node:path";
export function resolveEntryInstallRoot(entryFile: string): string {
@@ -34,6 +35,55 @@ export function shouldEnableOpenClawCompileCache(params: {
return !isSourceCheckoutInstallRoot(params.installRoot);
}
function sanitizeCompileCachePathSegment(value: string): string {
const normalized = value.replace(/[^A-Za-z0-9._-]+/g, "_").replace(/^_+|_+$/g, "");
return normalized.length > 0 ? normalized : "unknown";
}
function readPackageVersion(packageJsonPath: string): string {
try {
const parsed = JSON.parse(readFileSync(packageJsonPath, "utf8")) as unknown;
if (
parsed &&
typeof parsed === "object" &&
"version" in parsed &&
typeof parsed.version === "string" &&
parsed.version.trim().length > 0
) {
return parsed.version;
}
} catch {
// Fall through to an install-metadata-only cache key.
}
return "unknown";
}
export function resolveOpenClawCompileCacheDirectory(params: {
env?: NodeJS.ProcessEnv;
installRoot: string;
}): string {
const env = params.env ?? process.env;
const packageJsonPath = path.join(params.installRoot, "package.json");
const version = sanitizeCompileCachePathSegment(readPackageVersion(packageJsonPath));
let installMarker = "no-package-json";
try {
const stat = statSync(packageJsonPath);
installMarker = `${Math.trunc(stat.mtimeMs)}-${stat.size}`;
} catch {
// Package archives should always have package.json, but keep startup best-effort.
}
const baseDirectory =
env.NODE_COMPILE_CACHE && !isNodeCompileCacheDisabled(env)
? env.NODE_COMPILE_CACHE
: path.join(os.tmpdir(), "node-compile-cache");
return path.join(
baseDirectory,
"openclaw",
version,
sanitizeCompileCachePathSegment(installMarker),
);
}
export type OpenClawCompileCacheRespawnPlan = {
command: string;
args: string[];
@@ -107,7 +157,7 @@ export function enableOpenClawCompileCache(params: {
return;
}
try {
enableCompileCache();
enableCompileCache(resolveOpenClawCompileCacheDirectory(params));
} catch {
// Best-effort only; never block startup.
}