fix(ci): scope package tarball runtime checks

This commit is contained in:
Vincent Koc 2026-07-05 21:38:51 +02:00
parent 035d9975a2
commit 63257a2b0e
No known key found for this signature in database
5 changed files with 94 additions and 24 deletions

View file

@ -15,7 +15,7 @@ import {
} from "./lib/package-dist-imports.mjs";
function usage() {
return "Usage: node scripts/check-openclaw-package-tarball.mjs <openclaw.tgz>";
return "Usage: node scripts/check-openclaw-package-tarball.mjs [--require-bundled-workspace-deps] <openclaw.tgz>";
}
function fail(message) {
@ -25,21 +25,29 @@ function fail(message) {
function parseArgs(argv) {
const args = argv[0] === "--" ? argv.slice(1) : argv;
const tarball = args[0]?.trim() ?? "";
if (tarball === "--help" || tarball === "-h") {
return { help: true, tarball: "" };
let requireBundledWorkspaceDeps = false;
let tarball = "";
for (const rawArg of args) {
const arg = rawArg?.trim() ?? "";
if (arg === "--help" || arg === "-h") {
return { help: true, requireBundledWorkspaceDeps: false, tarball: "" };
}
if (arg === "--require-bundled-workspace-deps") {
requireBundledWorkspaceDeps = true;
continue;
}
if (arg.startsWith("-")) {
throw new Error(`Unknown OpenClaw package tarball check option: ${arg}`);
}
if (tarball) {
throw new Error(`Unexpected OpenClaw package tarball check argument: ${arg}`);
}
tarball = arg;
}
if (!tarball) {
throw new Error(usage());
}
if (tarball.startsWith("-")) {
throw new Error(`Unknown OpenClaw package tarball check option: ${tarball}`);
}
const extraArg = args[1]?.trim();
if (extraArg) {
throw new Error(`Unexpected OpenClaw package tarball check argument: ${extraArg}`);
}
return { help: false, tarball };
return { help: false, requireBundledWorkspaceDeps, tarball };
}
let cliArgs;
@ -293,7 +301,9 @@ if (entrySet.has("package.json")) {
const packageJson = JSON.parse(readTarEntry("package.json"));
packageVersion = typeof packageJson.version === "string" ? packageJson.version : "";
errors.push(...collectWorkspaceProtocolDependencyErrors(packageJson, "package.json"));
errors.push(...collectRequiredBundledWorkspaceDependencyErrors(packageJson, entrySet));
if (cliArgs.requireBundledWorkspaceDeps) {
errors.push(...collectRequiredBundledWorkspaceDependencyErrors(packageJson, entrySet));
}
} catch {
packageVersion = "";
}
@ -331,10 +341,7 @@ if (!entrySet.has("npm-shrinkwrap.json")) {
errors.push("npm-shrinkwrap.json must not lock root devDependencies");
}
errors.push(
...collectWorkspaceProtocolDependencyErrors(
rootPackage,
"npm-shrinkwrap.json packages root",
),
...collectWorkspaceProtocolDependencyErrors(rootPackage, "npm-shrinkwrap.json packages root"),
);
const devLockedPackages = Object.entries(shrinkwrap.packages ?? {})
.filter(([, packageMetadata]) => packageMetadata?.dev === true)

View file

@ -2959,7 +2959,7 @@ async function installPackageSpec(params) {
},
);
} catch (error) {
const debugTail = appendLatestNpmDebugLogTail(params.lane.homeDir, params.logPath);
const debugTail = appendLatestNpmDebugLogTail(params.lane.homeDir, params.logPath, installEnv);
if (!debugTail) {
throw error;
}
@ -2967,9 +2967,13 @@ async function installPackageSpec(params) {
}
}
function appendLatestNpmDebugLogTail(homeDir, logPath) {
const logsDir = join(homeDir, ".npm", "_logs");
const candidates = findNpmDebugLogs(logsDir);
export function appendLatestNpmDebugLogTail(
homeDir,
logPath,
env = process.env,
platform = process.platform,
) {
const candidates = resolveNpmDebugLogDirs(homeDir, env, platform).flatMap(findNpmDebugLogs);
const latest = candidates.at(-1);
if (!latest) {
return "";
@ -2988,6 +2992,17 @@ function appendLatestNpmDebugLogTail(homeDir, logPath) {
return tail;
}
export function resolveNpmDebugLogDirs(homeDir, env = process.env, platform = process.platform) {
const configuredCache = String(env.npm_config_cache ?? env.NPM_CONFIG_CACHE ?? "").trim();
const localAppData = String(env.LOCALAPPDATA ?? "").trim();
const cacheDirs = [
configuredCache,
platform === "win32" && localAppData ? join(localAppData, "npm-cache") : "",
join(homeDir, ".npm"),
].filter(Boolean);
return [...new Set(cacheDirs)].map((cacheDir) => join(cacheDir, "_logs"));
}
function findNpmDebugLogs(logsDir) {
if (!existsSync(logsDir)) {
return [];

View file

@ -648,7 +648,11 @@ async function main() {
const checkStartedAt = Date.now();
await run(
"node",
[path.join(ROOT_DIR, "scripts/check-openclaw-package-tarball.mjs"), tarball],
[
path.join(ROOT_DIR, "scripts/check-openclaw-package-tarball.mjs"),
"--require-bundled-workspace-deps",
tarball,
],
sourceDir,
{
timeoutMs: resolveTimeoutMs(

View file

@ -90,7 +90,7 @@ describe("check-openclaw-package-tarball", () => {
expect(result.status, result.stderr).toBe(0);
expect(result.stdout).toContain(
"Usage: node scripts/check-openclaw-package-tarball.mjs <openclaw.tgz>",
"Usage: node scripts/check-openclaw-package-tarball.mjs [--require-bundled-workspace-deps] <openclaw.tgz>",
);
expect(result.stderr).toBe("");
});
@ -479,13 +479,32 @@ describe("check-openclaw-package-tarball", () => {
);
});
it("rejects private workspace dependencies that are not bundled", () => {
it("accepts separately published private workspace dependencies by default", () => {
withTarball(
["dist/index.js"],
{ "dist/index.js": "export {};\n" },
(tarball) => {
const result = spawnSync("node", [CHECK_SCRIPT, tarball], { encoding: "utf8" });
expect(result.status, result.stderr).toBe(0);
expect(result.stdout).toContain("OpenClaw package tarball integrity passed.");
},
"2026.6.11",
{ packageJson: { dependencies: { "@openclaw/ai": "2026.6.11" } } },
);
});
it("rejects private workspace dependencies that are not bundled when strict packaging requires it", () => {
withTarball(
["dist/index.js"],
{ "dist/index.js": "export {};\n" },
(tarball) => {
const result = spawnSync(
"node",
[CHECK_SCRIPT, "--require-bundled-workspace-deps", tarball],
{ encoding: "utf8" },
);
expect(result.status).not.toBe(0);
expect(result.stderr).toContain(
"package.json dependencies.@openclaw/ai must be listed in bundleDependencies because it is private to the OpenClaw workspace",

View file

@ -31,6 +31,7 @@ import {
buildWindowsFreshShellVersionCheckScript,
buildInstalledBrowserOverrideImportProbeScript,
buildNpmGlobalInstallArgs,
appendLatestNpmDebugLogTail,
buildGatewayStatusArgsFromHelpText,
buildWindowsPathBootstrapScript,
canConnectToLoopbackPort,
@ -74,6 +75,7 @@ import {
resolveInstalledCliInvocation,
resolveInstalledPackageRootFromCliPath,
resolveNpmPackTarballFileName,
resolveNpmDebugLogDirs,
resolvePackDestinationTarball,
resolveProviderConfig,
resolveDevUpdateVerificationRef,
@ -1333,6 +1335,29 @@ describe("scripts/openclaw-cross-os-release-checks", () => {
}
});
it("reads npm debug logs from the Windows cache root", () => {
const dir = mkdtempSync(join(tmpdir(), "openclaw-cross-os-npm-debug-"));
try {
const homeDir = join(dir, "home");
const localAppData = join(homeDir, "AppData", "Local");
const logsDir = join(localAppData, "npm-cache", "_logs");
const logPath = join(dir, "install.log");
mkdirSync(logsDir, { recursive: true });
writeFileSync(join(logsDir, "2026-07-05T00_00_00_000Z-debug-0.log"), "windows log\n");
writeFileSync(logPath, "install failed\n");
expect(resolveNpmDebugLogDirs(homeDir, { LOCALAPPDATA: localAppData }, "win32")).toContain(
logsDir,
);
expect(
appendLatestNpmDebugLogTail(homeDir, logPath, { LOCALAPPDATA: localAppData }, "win32"),
).toContain("windows log");
expect(readFileSync(logPath, "utf8")).toContain("windows log");
} finally {
rmSync(dir, { recursive: true, force: true });
}
});
it("kills timed-out command process groups", async () => {
if (process.platform === "win32") {
return;