build: keep runtime dep stamps out of dist

This commit is contained in:
Peter Steinberger
2026-04-23 06:50:00 +01:00
parent bb55e23c67
commit acb8fe986d
7 changed files with 136 additions and 73 deletions

View File

@@ -322,6 +322,19 @@ describe("collectForbiddenPackPaths", () => {
]);
});
it("blocks legacy runtime dependency stamps from npm pack output", () => {
expect(
collectForbiddenPackPaths([
"dist/index.js",
"dist/extensions/codex/.openclaw-runtime-deps-backup-node_modules-old/zod/index.js",
"dist/extensions/discord/.openclaw-runtime-deps-stamp.json",
]),
).toEqual([
"dist/extensions/codex/.openclaw-runtime-deps-backup-node_modules-old/zod/index.js",
"dist/extensions/discord/.openclaw-runtime-deps-stamp.json",
]);
});
it("blocks private qa channel, qa lab, and suite paths from npm pack output", () => {
expect(
collectForbiddenPackPaths([

View File

@@ -10,6 +10,11 @@ import { createScriptTestHarness } from "./test-helpers.js";
const { createTempDir } = createScriptTestHarness();
type RuntimeDepsStampParams = {
fingerprint: string;
stampPath: string;
};
describe("stageBundledPluginRuntimeDeps", () => {
function createBundledPluginFixture(params: {
packageJson: Record<string, unknown>;
@@ -27,6 +32,15 @@ describe("stageBundledPluginRuntimeDeps", () => {
return { pluginDir, repoRoot };
}
function writeRuntimeDepsStamp(stampPath: string, fingerprint: string) {
fs.mkdirSync(path.dirname(stampPath), { recursive: true });
fs.writeFileSync(stampPath, `${JSON.stringify({ fingerprint }, null, 2)}\n`, "utf8");
}
function runtimeDepsStampPath(repoRoot: string, pluginId = "fixture-plugin") {
return path.join(repoRoot, ".artifacts", "bundled-runtime-deps-stamps", `${pluginId}.json`);
}
it("pins fallback install specs to exact installed versions", () => {
const { repoRoot } = createBundledPluginFixture({
packageJson: {
@@ -142,13 +156,9 @@ describe("stageBundledPluginRuntimeDeps", () => {
let installCount = 0;
stageBundledPluginRuntimeDeps({
cwd: repoRoot,
installPluginRuntimeDepsImpl: ({ fingerprint }: { fingerprint: string }) => {
installPluginRuntimeDepsImpl: ({ fingerprint, stampPath }: RuntimeDepsStampParams) => {
installCount += 1;
fs.writeFileSync(
path.join(pluginDir, ".openclaw-runtime-deps-stamp.json"),
`${JSON.stringify({ fingerprint }, null, 2)}\n`,
"utf8",
);
writeRuntimeDepsStamp(stampPath, fingerprint);
},
});
stageBundledPluginRuntimeDeps({
@@ -182,16 +192,12 @@ describe("stageBundledPluginRuntimeDeps", () => {
const stageOnce = () =>
stageBundledPluginRuntimeDeps({
cwd: repoRoot,
installPluginRuntimeDepsImpl: ({ fingerprint }: { fingerprint: string }) => {
installPluginRuntimeDepsImpl: ({ fingerprint, stampPath }: RuntimeDepsStampParams) => {
installCount += 1;
const nodeModulesDir = path.join(pluginDir, "node_modules");
fs.mkdirSync(nodeModulesDir, { recursive: true });
fs.writeFileSync(path.join(nodeModulesDir, "marker.txt"), `${installCount}\n`, "utf8");
fs.writeFileSync(
path.join(pluginDir, ".openclaw-runtime-deps-stamp.json"),
`${JSON.stringify({ fingerprint }, null, 2)}\n`,
"utf8",
);
writeRuntimeDepsStamp(stampPath, fingerprint);
},
});
@@ -226,16 +232,12 @@ describe("stageBundledPluginRuntimeDeps", () => {
const stageOnce = () =>
stageBundledPluginRuntimeDeps({
cwd: repoRoot,
installPluginRuntimeDepsImpl: ({ fingerprint }: { fingerprint: string }) => {
installPluginRuntimeDepsImpl: ({ fingerprint, stampPath }: RuntimeDepsStampParams) => {
installCount += 1;
const nodeModulesDir = path.join(pluginDir, "node_modules");
fs.mkdirSync(nodeModulesDir, { recursive: true });
fs.writeFileSync(path.join(nodeModulesDir, "marker.txt"), `${installCount}\n`, "utf8");
fs.writeFileSync(
path.join(pluginDir, ".openclaw-runtime-deps-stamp.json"),
`${JSON.stringify({ fingerprint }, null, 2)}\n`,
"utf8",
);
writeRuntimeDepsStamp(stampPath, fingerprint);
},
});
@@ -310,7 +312,7 @@ describe("stageBundledPluginRuntimeDeps", () => {
});
it("refuses to write a runtime deps stamp through a symlink", () => {
const { pluginDir, repoRoot } = createBundledPluginFixture({
const { repoRoot } = createBundledPluginFixture({
packageJson: {
name: "@openclaw/fixture-plugin",
version: "1.0.0",
@@ -320,8 +322,9 @@ describe("stageBundledPluginRuntimeDeps", () => {
});
const directDir = path.join(repoRoot, "node_modules", "direct");
const outsideStamp = path.join(repoRoot, "outside-stamp.json");
const stampPath = path.join(pluginDir, ".openclaw-runtime-deps-stamp.json");
const stampPath = runtimeDepsStampPath(repoRoot);
fs.mkdirSync(directDir, { recursive: true });
fs.mkdirSync(path.dirname(stampPath), { recursive: true });
fs.writeFileSync(
path.join(directDir, "package.json"),
'{ "name": "direct", "version": "1.0.0" }\n',
@@ -359,7 +362,33 @@ describe("stageBundledPluginRuntimeDeps", () => {
expect(
fs.readFileSync(path.join(pluginDir, "node_modules", "left-pad", "index.js"), "utf8"),
).toBe("module.exports = 1;\n");
expect(fs.existsSync(path.join(pluginDir, ".openclaw-runtime-deps-stamp.json"))).toBe(true);
expect(fs.existsSync(path.join(pluginDir, ".openclaw-runtime-deps-stamp.json"))).toBe(false);
expect(fs.existsSync(runtimeDepsStampPath(repoRoot))).toBe(true);
});
it("removes legacy runtime dependency stamps from dist", () => {
const { pluginDir, repoRoot } = createBundledPluginFixture({
packageJson: {
name: "@openclaw/fixture-plugin",
version: "1.0.0",
dependencies: { "left-pad": "1.3.0" },
openclaw: { bundle: { stageRuntimeDependencies: true } },
},
});
const rootDepDir = path.join(repoRoot, "node_modules", "left-pad");
const legacyStampPath = path.join(pluginDir, ".openclaw-runtime-deps-stamp.json");
fs.mkdirSync(rootDepDir, { recursive: true });
fs.writeFileSync(
path.join(rootDepDir, "package.json"),
'{ "name": "left-pad", "version": "1.3.0" }\n',
"utf8",
);
fs.writeFileSync(legacyStampPath, '{"legacy":true}\n', "utf8");
stageBundledPluginRuntimeDeps({ cwd: repoRoot });
expect(fs.existsSync(legacyStampPath)).toBe(false);
expect(fs.existsSync(runtimeDepsStampPath(repoRoot))).toBe(true);
});
it("skips missing optional runtime deps when copying the installed closure", () => {
@@ -617,16 +646,12 @@ describe("stageBundledPluginRuntimeDeps", () => {
let installCount = 0;
stageBundledPluginRuntimeDeps({
cwd: repoRoot,
installPluginRuntimeDepsImpl: ({ fingerprint }: { fingerprint: string }) => {
installPluginRuntimeDepsImpl: ({ fingerprint, stampPath }: RuntimeDepsStampParams) => {
installCount += 1;
const nodeModulesDir = path.join(pluginDir, "node_modules");
fs.mkdirSync(nodeModulesDir, { recursive: true });
fs.writeFileSync(path.join(nodeModulesDir, "marker.txt"), "installed\n", "utf8");
fs.writeFileSync(
path.join(pluginDir, ".openclaw-runtime-deps-stamp.json"),
`${JSON.stringify({ fingerprint }, null, 2)}\n`,
"utf8",
);
writeRuntimeDepsStamp(stampPath, fingerprint);
},
});
@@ -698,16 +723,12 @@ describe("stageBundledPluginRuntimeDeps", () => {
let installCount = 0;
stageBundledPluginRuntimeDeps({
cwd: repoRoot,
installPluginRuntimeDepsImpl: ({ fingerprint }: { fingerprint: string }) => {
installPluginRuntimeDepsImpl: ({ fingerprint, stampPath }: RuntimeDepsStampParams) => {
installCount += 1;
const nodeModulesDir = path.join(pluginDir, "node_modules");
fs.mkdirSync(nodeModulesDir, { recursive: true });
fs.writeFileSync(path.join(nodeModulesDir, "marker.txt"), "installed\n", "utf8");
fs.writeFileSync(
path.join(pluginDir, ".openclaw-runtime-deps-stamp.json"),
`${JSON.stringify({ fingerprint }, null, 2)}\n`,
"utf8",
);
writeRuntimeDepsStamp(stampPath, fingerprint);
},
});
@@ -743,16 +764,12 @@ describe("stageBundledPluginRuntimeDeps", () => {
let installCount = 0;
stageBundledPluginRuntimeDeps({
cwd: repoRoot,
installPluginRuntimeDepsImpl: ({ fingerprint }: { fingerprint: string }) => {
installPluginRuntimeDepsImpl: ({ fingerprint, stampPath }: RuntimeDepsStampParams) => {
installCount += 1;
const nodeModulesDir = path.join(pluginDir, "node_modules");
fs.mkdirSync(nodeModulesDir, { recursive: true });
fs.writeFileSync(path.join(nodeModulesDir, "marker.txt"), "installed\n", "utf8");
fs.writeFileSync(
path.join(pluginDir, ".openclaw-runtime-deps-stamp.json"),
`${JSON.stringify({ fingerprint }, null, 2)}\n`,
"utf8",
);
writeRuntimeDepsStamp(stampPath, fingerprint);
},
});
@@ -788,7 +805,7 @@ describe("stageBundledPluginRuntimeDeps", () => {
let installCount = 0;
stageBundledPluginRuntimeDeps({
cwd: repoRoot,
installPluginRuntimeDepsImpl: ({ fingerprint }: { fingerprint: string }) => {
installPluginRuntimeDepsImpl: ({ fingerprint, stampPath }: RuntimeDepsStampParams) => {
installCount += 1;
const nodeModulesDir = path.join(pluginDir, "node_modules", "direct");
fs.mkdirSync(nodeModulesDir, { recursive: true });
@@ -802,11 +819,7 @@ describe("stageBundledPluginRuntimeDeps", () => {
"module.exports = 'installed';\n",
"utf8",
);
fs.writeFileSync(
path.join(pluginDir, ".openclaw-runtime-deps-stamp.json"),
`${JSON.stringify({ fingerprint }, null, 2)}\n`,
"utf8",
);
writeRuntimeDepsStamp(stampPath, fingerprint);
},
});
@@ -976,7 +989,7 @@ describe("stageBundledPluginRuntimeDeps", () => {
let installCount = 0;
stageBundledPluginRuntimeDeps({
cwd: repoRoot,
installPluginRuntimeDepsImpl: ({ fingerprint }: { fingerprint: string }) => {
installPluginRuntimeDepsImpl: ({ fingerprint, stampPath }: RuntimeDepsStampParams) => {
installCount += 1;
const nodeModulesDir = path.join(pluginDir, "node_modules", "left-pad");
fs.mkdirSync(nodeModulesDir, { recursive: true });
@@ -990,11 +1003,7 @@ describe("stageBundledPluginRuntimeDeps", () => {
"module.exports = 'nested';\n",
"utf8",
);
fs.writeFileSync(
path.join(pluginDir, ".openclaw-runtime-deps-stamp.json"),
`${JSON.stringify({ fingerprint }, null, 2)}\n`,
"utf8",
);
writeRuntimeDepsStamp(stampPath, fingerprint);
},
});
@@ -1024,7 +1033,7 @@ describe("stageBundledPluginRuntimeDeps", () => {
let installCount = 0;
stageBundledPluginRuntimeDeps({
cwd: repoRoot,
installPluginRuntimeDepsImpl: ({ fingerprint }: { fingerprint: string }) => {
installPluginRuntimeDepsImpl: ({ fingerprint, stampPath }: RuntimeDepsStampParams) => {
installCount += 1;
const nodeModulesDir = path.join(pluginDir, "node_modules", "tiny");
fs.mkdirSync(nodeModulesDir, { recursive: true });
@@ -1033,11 +1042,7 @@ describe("stageBundledPluginRuntimeDeps", () => {
'{ "name": "tiny", "version": "0.0.3" }\n',
"utf8",
);
fs.writeFileSync(
path.join(pluginDir, ".openclaw-runtime-deps-stamp.json"),
`${JSON.stringify({ fingerprint }, null, 2)}\n`,
"utf8",
);
writeRuntimeDepsStamp(stampPath, fingerprint);
},
});
@@ -1064,7 +1069,7 @@ describe("stageBundledPluginRuntimeDeps", () => {
let installCount = 0;
stageBundledPluginRuntimeDeps({
cwd: repoRoot,
installPluginRuntimeDepsImpl: ({ fingerprint }: { fingerprint: string }) => {
installPluginRuntimeDepsImpl: ({ fingerprint, stampPath }: RuntimeDepsStampParams) => {
installCount += 1;
const nodeModulesDir = path.join(pluginDir, "node_modules", "direct");
fs.mkdirSync(nodeModulesDir, { recursive: true });
@@ -1073,11 +1078,7 @@ describe("stageBundledPluginRuntimeDeps", () => {
'{ "name": "direct", "version": "1.2.3" }\n',
"utf8",
);
fs.writeFileSync(
path.join(pluginDir, ".openclaw-runtime-deps-stamp.json"),
`${JSON.stringify({ fingerprint }, null, 2)}\n`,
"utf8",
);
writeRuntimeDepsStamp(stampPath, fingerprint);
},
});
@@ -1097,7 +1098,7 @@ describe("stageBundledPluginRuntimeDeps", () => {
let installCount = 0;
stageBundledPluginRuntimeDeps({
cwd: repoRoot,
installPluginRuntimeDepsImpl: ({ fingerprint }: { fingerprint: string }) => {
installPluginRuntimeDepsImpl: ({ fingerprint, stampPath }: RuntimeDepsStampParams) => {
installCount += 1;
if (installCount < 3) {
throw new Error(`attempt ${installCount} failed`);
@@ -1105,11 +1106,7 @@ describe("stageBundledPluginRuntimeDeps", () => {
const nodeModulesDir = path.join(pluginDir, "node_modules");
fs.mkdirSync(nodeModulesDir, { recursive: true });
fs.writeFileSync(path.join(nodeModulesDir, "marker.txt"), "ok\n", "utf8");
fs.writeFileSync(
path.join(pluginDir, ".openclaw-runtime-deps-stamp.json"),
`${JSON.stringify({ fingerprint }, null, 2)}\n`,
"utf8",
);
writeRuntimeDepsStamp(stampPath, fingerprint);
},
});