mirror of
https://github.com/openclaw/openclaw.git
synced 2026-08-12 21:53:00 -06:00
fix(scripts): rewrite transitive OCM workspace packs (#122214)
This commit is contained in:
@@ -15,6 +15,7 @@ const supportedRuntimeBuildProfiles = new Set(["sourcePerformance"]);
|
||||
const fullGitCommitPattern = /^[0-9a-f]{40}$/iu;
|
||||
|
||||
type WorkspacePackage = { name: string; version: string; tarball: string };
|
||||
type WorkspacePackageSource = Omit<WorkspacePackage, "tarball"> & { dir: string };
|
||||
|
||||
export function parseWorkspaceDependencyDirs(
|
||||
raw: string | undefined = process.env[WORKSPACE_DIRS_ENV],
|
||||
@@ -223,7 +224,7 @@ function packWorkspaceDependencies(
|
||||
workspaceDirs: string[],
|
||||
outputDir: string,
|
||||
): WorkspacePackage[] {
|
||||
return workspaceDirs.map((packageDir) => {
|
||||
const sources: WorkspacePackageSource[] = workspaceDirs.map((packageDir) => {
|
||||
const packageJson = JSON.parse(readFileSync(join(packageDir, "package.json"), "utf8"));
|
||||
if (typeof packageJson.name !== "string" || packageJson.name.trim() === "") {
|
||||
throw new Error(`workspace dependency has no package name: ${packageDir}`);
|
||||
@@ -231,28 +232,47 @@ function packWorkspaceDependencies(
|
||||
if (typeof packageJson.version !== "string" || packageJson.version.trim() === "") {
|
||||
throw new Error(`workspace dependency has no package version: ${packageDir}`);
|
||||
}
|
||||
return {
|
||||
dir: packageDir,
|
||||
name: packageJson.name,
|
||||
version: packageJson.version,
|
||||
};
|
||||
});
|
||||
const workspacePackages = sources.map(({ dir, name, version }) => {
|
||||
const before = new Set(readdirSync(outputDir));
|
||||
const result = runNpm(npm, ["pack", packageDir, "--pack-destination", outputDir, "--silent"], {
|
||||
const result = runNpm(npm, ["pack", dir, "--pack-destination", outputDir, "--silent"], {
|
||||
encoding: "utf8",
|
||||
stdio: ["ignore", "pipe", "inherit"],
|
||||
});
|
||||
if (result.status !== 0) {
|
||||
throw new Error(`npm pack failed for ${packageJson.name} with status ${result.status ?? 1}`);
|
||||
throw new Error(`npm pack failed for ${name} with status ${result.status ?? 1}`);
|
||||
}
|
||||
const tarballs = readdirSync(outputDir).filter(
|
||||
(entry) => entry.endsWith(".tgz") && !before.has(entry),
|
||||
);
|
||||
if (tarballs.length !== 1) {
|
||||
throw new Error(
|
||||
`expected npm pack to create one archive for ${packageJson.name}, found ${tarballs.length}`,
|
||||
`expected npm pack to create one archive for ${name}, found ${tarballs.length}`,
|
||||
);
|
||||
}
|
||||
return {
|
||||
name: packageJson.name,
|
||||
version: packageJson.version,
|
||||
name,
|
||||
version,
|
||||
tarball: join(outputDir, tarballs[0]!),
|
||||
};
|
||||
});
|
||||
return workspacePackages.map((workspacePackage, index) => {
|
||||
return {
|
||||
name: workspacePackage.name,
|
||||
version: workspacePackage.version,
|
||||
tarball: patchPackageArchiveWorkspaceDependencies(
|
||||
workspacePackage.tarball,
|
||||
workspacePackages,
|
||||
outputDir,
|
||||
`workspace-${index}`,
|
||||
),
|
||||
};
|
||||
});
|
||||
}
|
||||
|
||||
export function rewriteWorkspaceDependencyVersions(
|
||||
@@ -277,7 +297,7 @@ export function rewriteWorkspaceDependencyVersions(
|
||||
}
|
||||
const version = workspaceVersions.get(name);
|
||||
if (!version) {
|
||||
throw new Error(`root archive references unconfigured workspace dependency: ${name}`);
|
||||
throw new Error(`package archive references unconfigured workspace dependency: ${name}`);
|
||||
}
|
||||
Reflect.set(dependencies, name, version);
|
||||
rewritten += 1;
|
||||
@@ -286,28 +306,42 @@ export function rewriteWorkspaceDependencyVersions(
|
||||
return rewritten;
|
||||
}
|
||||
|
||||
function patchRootArchiveWorkspaceDependencies(
|
||||
rootArchive: string,
|
||||
function patchPackageArchiveWorkspaceDependencies(
|
||||
archive: string,
|
||||
workspacePackages: WorkspacePackage[],
|
||||
outputDir: string,
|
||||
outputStem: string,
|
||||
): string {
|
||||
const unpackDir = join(outputDir, "root-archive");
|
||||
const unpackDir = join(outputDir, `${outputStem}-archive`);
|
||||
mkdirSync(unpackDir);
|
||||
runTar(["-xzf", rootArchive, "-C", unpackDir]);
|
||||
runTar(["-xzf", archive, "-C", unpackDir]);
|
||||
|
||||
const packageJsonPath = join(unpackDir, "package", "package.json");
|
||||
const packageJson = JSON.parse(readFileSync(packageJsonPath, "utf8"));
|
||||
const rewritten = rewriteWorkspaceDependencyVersions(packageJson, workspacePackages);
|
||||
if (rewritten === 0) {
|
||||
return rootArchive;
|
||||
return archive;
|
||||
}
|
||||
|
||||
writeFileSync(packageJsonPath, `${JSON.stringify(packageJson, null, 2)}\n`);
|
||||
const patchedArchive = join(outputDir, "openclaw-root-patched.tgz");
|
||||
const patchedArchive = join(outputDir, `${outputStem}-patched.tgz`);
|
||||
runTar(["-czf", patchedArchive, "-C", unpackDir, "package"]);
|
||||
return patchedArchive;
|
||||
}
|
||||
|
||||
function patchRootArchiveWorkspaceDependencies(
|
||||
rootArchive: string,
|
||||
workspacePackages: WorkspacePackage[],
|
||||
outputDir: string,
|
||||
): string {
|
||||
return patchPackageArchiveWorkspaceDependencies(
|
||||
rootArchive,
|
||||
workspacePackages,
|
||||
outputDir,
|
||||
"openclaw-root",
|
||||
);
|
||||
}
|
||||
|
||||
function main(): number {
|
||||
const args = process.argv.slice(2);
|
||||
const npm = process.env[REAL_NPM_ENV]?.trim() || "npm";
|
||||
|
||||
@@ -270,16 +270,30 @@ describe("OCM npm workspace dependency adapter", () => {
|
||||
});
|
||||
});
|
||||
|
||||
it("installs a packed root with a local workspace dependency", () => {
|
||||
it("rejects package archives with an unconfigured workspace dependency", () => {
|
||||
const packageJson = {
|
||||
dependencies: {
|
||||
"@openclaw/normalization-core": "workspace:*",
|
||||
},
|
||||
};
|
||||
|
||||
expect(() => rewriteWorkspaceDependencyVersions(packageJson, [])).toThrow(
|
||||
"package archive references unconfigured workspace dependency: @openclaw/normalization-core",
|
||||
);
|
||||
});
|
||||
|
||||
it("installs a packed root with transitive local workspace dependencies", () => {
|
||||
const root = mkdtempSync(join(tmpdir(), "openclaw-ocm-adapter-test-"));
|
||||
try {
|
||||
const archiveRoot = join(root, "archive");
|
||||
const packagedRoot = join(archiveRoot, "package");
|
||||
const workspaceDir = join(root, "ai");
|
||||
const transitiveWorkspaceDir = join(root, "normalization-core");
|
||||
const installDir = join(root, "install");
|
||||
const rootArchive = join(root, "openclaw.tgz");
|
||||
mkdirSync(packagedRoot, { recursive: true });
|
||||
mkdirSync(workspaceDir, { recursive: true });
|
||||
mkdirSync(transitiveWorkspaceDir, { recursive: true });
|
||||
writeFileSync(
|
||||
join(packagedRoot, "package.json"),
|
||||
`${JSON.stringify({
|
||||
@@ -294,9 +308,19 @@ describe("OCM npm workspace dependency adapter", () => {
|
||||
name: "@openclaw/ai",
|
||||
version: "1.0.0",
|
||||
main: "index.js",
|
||||
dependencies: { "@openclaw/normalization-core": "workspace:*" },
|
||||
})}\n`,
|
||||
);
|
||||
writeFileSync(join(workspaceDir, "index.js"), "export const ready = true;\n");
|
||||
writeFileSync(
|
||||
join(transitiveWorkspaceDir, "package.json"),
|
||||
`${JSON.stringify({
|
||||
name: "@openclaw/normalization-core",
|
||||
version: "1.0.0",
|
||||
main: "index.js",
|
||||
})}\n`,
|
||||
);
|
||||
writeFileSync(join(transitiveWorkspaceDir, "index.js"), "export const normalized = true;\n");
|
||||
execFileSync("tar", ["-czf", rootArchive, "-C", archiveRoot, "package"]);
|
||||
|
||||
execFileSync(
|
||||
@@ -315,7 +339,9 @@ describe("OCM npm workspace dependency adapter", () => {
|
||||
env: {
|
||||
...process.env,
|
||||
OPENCLAW_OCM_REAL_NPM_BIN: process.platform === "win32" ? "npm.cmd" : "npm",
|
||||
OPENCLAW_OCM_WORKSPACE_DEPENDENCY_DIRS: workspaceDir,
|
||||
OPENCLAW_OCM_WORKSPACE_DEPENDENCY_DIRS: [workspaceDir, transitiveWorkspaceDir].join(
|
||||
delimiter,
|
||||
),
|
||||
npm_config_audit: "false",
|
||||
npm_config_cache: join(root, "npm-cache"),
|
||||
npm_config_fund: "false",
|
||||
@@ -332,6 +358,18 @@ describe("OCM npm workspace dependency adapter", () => {
|
||||
JSON.parse(readFileSync(join(installDir, "node_modules/@openclaw/ai/package.json"), "utf8"))
|
||||
.version,
|
||||
).toBe("1.0.0");
|
||||
expect(
|
||||
JSON.parse(
|
||||
readFileSync(
|
||||
join(installDir, "node_modules/@openclaw/normalization-core/package.json"),
|
||||
"utf8",
|
||||
),
|
||||
).version,
|
||||
).toBe("1.0.0");
|
||||
expect(
|
||||
JSON.parse(readFileSync(join(installDir, "node_modules/@openclaw/ai/package.json"), "utf8"))
|
||||
.dependencies["@openclaw/normalization-core"],
|
||||
).toBe("1.0.0");
|
||||
} finally {
|
||||
rmSync(root, { force: true, recursive: true });
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user