mirror of
https://github.com/openclaw/openclaw.git
synced 2026-07-09 15:59:30 +00:00
fix(ci): rewrite workspace deps for OCM installs
This commit is contained in:
parent
b63317d07e
commit
e8c1780999
2 changed files with 168 additions and 1 deletions
|
|
@ -64,12 +64,28 @@ function runNpm(npm, args, options = {}) {
|
|||
return result;
|
||||
}
|
||||
|
||||
function runTar(args) {
|
||||
const result = spawnSync("tar", args, {
|
||||
env: process.env,
|
||||
stdio: "inherit",
|
||||
});
|
||||
if (result.error) {
|
||||
throw result.error;
|
||||
}
|
||||
if (result.status !== 0) {
|
||||
throw new Error(`tar failed with status ${result.status ?? 1}`);
|
||||
}
|
||||
}
|
||||
|
||||
function packWorkspaceDependencies(npm, workspaceDirs, outputDir) {
|
||||
return 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}`);
|
||||
}
|
||||
if (typeof packageJson.version !== "string" || packageJson.version.trim() === "") {
|
||||
throw new Error(`workspace dependency has no package version: ${packageDir}`);
|
||||
}
|
||||
const before = new Set(readdirSync(outputDir));
|
||||
const result = runNpm(npm, ["pack", packageDir, "--pack-destination", outputDir, "--silent"], {
|
||||
encoding: "utf8",
|
||||
|
|
@ -88,11 +104,58 @@ function packWorkspaceDependencies(npm, workspaceDirs, outputDir) {
|
|||
}
|
||||
return {
|
||||
name: packageJson.name,
|
||||
version: packageJson.version,
|
||||
tarball: join(outputDir, tarballs[0]),
|
||||
};
|
||||
});
|
||||
}
|
||||
|
||||
export function rewriteWorkspaceDependencyVersions(packageJson, workspacePackages) {
|
||||
const workspaceVersions = new Map(workspacePackages.map(({ name, version }) => [name, version]));
|
||||
let rewritten = 0;
|
||||
for (const section of [
|
||||
"dependencies",
|
||||
"optionalDependencies",
|
||||
"peerDependencies",
|
||||
"devDependencies",
|
||||
]) {
|
||||
const dependencies = packageJson[section];
|
||||
if (!dependencies || typeof dependencies !== "object" || Array.isArray(dependencies)) {
|
||||
continue;
|
||||
}
|
||||
for (const [name, spec] of Object.entries(dependencies)) {
|
||||
if (typeof spec !== "string" || !spec.startsWith("workspace:")) {
|
||||
continue;
|
||||
}
|
||||
const version = workspaceVersions.get(name);
|
||||
if (!version) {
|
||||
throw new Error(`root archive references unconfigured workspace dependency: ${name}`);
|
||||
}
|
||||
dependencies[name] = version;
|
||||
rewritten += 1;
|
||||
}
|
||||
}
|
||||
return rewritten;
|
||||
}
|
||||
|
||||
function patchRootArchiveWorkspaceDependencies(rootArchive, workspacePackages, outputDir) {
|
||||
const unpackDir = join(outputDir, "root-archive");
|
||||
mkdirSync(unpackDir);
|
||||
runTar(["-xzf", rootArchive, "-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;
|
||||
}
|
||||
|
||||
writeFileSync(packageJsonPath, `${JSON.stringify(packageJson, null, 2)}\n`);
|
||||
const patchedArchive = join(outputDir, "openclaw-root-patched.tgz");
|
||||
runTar(["-czf", patchedArchive, "-C", unpackDir, "package"]);
|
||||
return patchedArchive;
|
||||
}
|
||||
|
||||
function main() {
|
||||
const args = process.argv.slice(2);
|
||||
const npm = process.env[REAL_NPM_ENV]?.trim() || "npm";
|
||||
|
|
@ -106,10 +169,15 @@ function main() {
|
|||
const packDir = mkdtempSync(join(tmpdir(), "openclaw-ocm-workspace-deps-"));
|
||||
try {
|
||||
const workspacePackages = packWorkspaceDependencies(npm, workspaceDirs, packDir);
|
||||
const rootArchive = patchRootArchiveWorkspaceDependencies(
|
||||
plan.rootArchive,
|
||||
workspacePackages,
|
||||
packDir,
|
||||
);
|
||||
mkdirSync(plan.prefixDir, { recursive: true });
|
||||
writeFileSync(
|
||||
join(plan.prefixDir, "package.json"),
|
||||
`${JSON.stringify(buildInstallManifest(plan.rootArchive, workspacePackages), null, 2)}\n`,
|
||||
`${JSON.stringify(buildInstallManifest(rootArchive, workspacePackages), null, 2)}\n`,
|
||||
);
|
||||
const result = runNpm(npm, plan.installArgs, { stdio: "inherit" });
|
||||
return result.status ?? 1;
|
||||
|
|
|
|||
|
|
@ -1,11 +1,20 @@
|
|||
import { execFileSync } from "node:child_process";
|
||||
import { mkdirSync, mkdtempSync, readFileSync, rmSync, writeFileSync } from "node:fs";
|
||||
import { tmpdir } from "node:os";
|
||||
import { delimiter, join } from "node:path";
|
||||
import { fileURLToPath } from "node:url";
|
||||
import { describe, expect, it } from "vitest";
|
||||
import {
|
||||
buildInstallManifest,
|
||||
parseWorkspaceDependencyDirs,
|
||||
resolveWorkspaceInstallPlan,
|
||||
rewriteWorkspaceDependencyVersions,
|
||||
} from "../../scripts/ocm-npm-workspace-deps.mjs";
|
||||
|
||||
const adapterPath = fileURLToPath(
|
||||
new URL("../../scripts/ocm-npm-workspace-deps.mjs", import.meta.url),
|
||||
);
|
||||
|
||||
describe("OCM npm workspace dependency adapter", () => {
|
||||
it("resolves workspace package directories", () => {
|
||||
expect(
|
||||
|
|
@ -60,4 +69,94 @@ describe("OCM npm workspace dependency adapter", () => {
|
|||
},
|
||||
});
|
||||
});
|
||||
|
||||
it("rewrites packed workspace protocols to the local package version", () => {
|
||||
const packageJson = {
|
||||
dependencies: {
|
||||
"@openclaw/ai": "workspace:*",
|
||||
chalk: "5.6.2",
|
||||
},
|
||||
};
|
||||
|
||||
expect(
|
||||
rewriteWorkspaceDependencyVersions(packageJson, [
|
||||
{
|
||||
name: "@openclaw/ai",
|
||||
version: "2026.7.1-beta.3",
|
||||
tarball: "/tmp/openclaw-ai.tgz",
|
||||
},
|
||||
]),
|
||||
).toBe(1);
|
||||
expect(packageJson.dependencies).toEqual({
|
||||
"@openclaw/ai": "2026.7.1-beta.3",
|
||||
chalk: "5.6.2",
|
||||
});
|
||||
});
|
||||
|
||||
it("installs a packed root with a local workspace dependency", () => {
|
||||
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 installDir = join(root, "install");
|
||||
const rootArchive = join(root, "openclaw.tgz");
|
||||
mkdirSync(packagedRoot, { recursive: true });
|
||||
mkdirSync(workspaceDir, { recursive: true });
|
||||
writeFileSync(
|
||||
join(packagedRoot, "package.json"),
|
||||
`${JSON.stringify({
|
||||
name: "openclaw",
|
||||
version: "1.0.0",
|
||||
dependencies: { "@openclaw/ai": "workspace:*" },
|
||||
})}\n`,
|
||||
);
|
||||
writeFileSync(
|
||||
join(workspaceDir, "package.json"),
|
||||
`${JSON.stringify({
|
||||
name: "@openclaw/ai",
|
||||
version: "1.0.0",
|
||||
main: "index.js",
|
||||
})}\n`,
|
||||
);
|
||||
writeFileSync(join(workspaceDir, "index.js"), "export const ready = true;\n");
|
||||
execFileSync("tar", ["-czf", rootArchive, "-C", archiveRoot, "package"]);
|
||||
|
||||
execFileSync(
|
||||
process.execPath,
|
||||
[
|
||||
adapterPath,
|
||||
"install",
|
||||
"--prefix",
|
||||
installDir,
|
||||
"--omit=dev",
|
||||
"--no-save",
|
||||
"--package-lock=false",
|
||||
rootArchive,
|
||||
],
|
||||
{
|
||||
env: {
|
||||
...process.env,
|
||||
OPENCLAW_OCM_REAL_NPM_BIN: process.platform === "win32" ? "npm.cmd" : "npm",
|
||||
OPENCLAW_OCM_WORKSPACE_DEPENDENCY_DIRS: workspaceDir,
|
||||
npm_config_audit: "false",
|
||||
npm_config_cache: join(root, "npm-cache"),
|
||||
npm_config_fund: "false",
|
||||
},
|
||||
stdio: "pipe",
|
||||
},
|
||||
);
|
||||
|
||||
expect(
|
||||
JSON.parse(readFileSync(join(installDir, "node_modules/openclaw/package.json"), "utf8"))
|
||||
.version,
|
||||
).toBe("1.0.0");
|
||||
expect(
|
||||
JSON.parse(readFileSync(join(installDir, "node_modules/@openclaw/ai/package.json"), "utf8"))
|
||||
.version,
|
||||
).toBe("1.0.0");
|
||||
} finally {
|
||||
rmSync(root, { force: true, recursive: true });
|
||||
}
|
||||
});
|
||||
});
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue