fix(ci): preserve OCM workspace prepack (#107844)

* fix(ci): preserve OCM workspace prepack

* fix(ci): bind OCM prepack to pack context

* fix(ci): avoid path import shadowing
This commit is contained in:
Peter Steinberger
2026-07-14 17:28:45 -07:00
committed by GitHub
parent 4be39280e6
commit ced61db64a
3 changed files with 64 additions and 1 deletions
+1 -1
View File
@@ -18,7 +18,7 @@ export function buildInstallManifest(
openclaw: string;
};
};
export function resolveNpmEnvironment(args: unknown, env?: NodeJS.ProcessEnv): NodeJS.ProcessEnv;
export function resolveNpmEnvironment(args: string[], env?: NodeJS.ProcessEnv): NodeJS.ProcessEnv;
export function resolveRuntimePackPlan(
args: string[],
env?: NodeJS.ProcessEnv,
+35
View File
@@ -3,6 +3,7 @@
import { spawnSync, type SpawnSyncOptions } from "node:child_process";
import { existsSync, readFileSync, readdirSync } from "node:fs";
import { basename, delimiter, join } from "node:path";
import { pathToFileURL } from "node:url";
import { formatErrorMessage } from "../src/infra/errors.ts";
import { writePackageDistInventory } from "./lib/package-dist-inventory.ts";
@@ -18,6 +19,10 @@ const requiredControlUiCompressionSuffixes = [".br", ".gz"] as const;
const DEFAULT_PREPACK_COMMAND_TIMEOUT_MS = 30 * 60 * 1000;
const ALLOW_UNRELEASED_CHANGELOG_ENV = "OPENCLAW_PREPACK_ALLOW_UNRELEASED_CHANGELOG";
const PREPARED_RELEASE_ENV = "OPENCLAW_PREPACK_PREPARED";
const OCM_INTERNAL_NPM_BIN_ENV = "OCM_INTERNAL_NPM_BIN";
const OCM_WORKSPACE_DIRS_ENV = "OPENCLAW_OCM_WORKSPACE_DEPENDENCY_DIRS";
const OCM_ADAPTER_BASENAME = "ocm-npm-workspace-deps.mjs";
const NPM_COMMAND_ENV = "npm_command";
const SELF_CONTAINED_SOURCE_PACK_COMMAND =
"node scripts/package-openclaw-for-docker.mjs --allow-unreleased-changelog";
@@ -28,8 +33,35 @@ type PreparedFileReader = {
type PackageManifest = {
dependencies?: Record<string, unknown>;
name?: unknown;
};
function ocmExternalizesWorkspacePackage(packageName: string, env: NodeJS.ProcessEnv): boolean {
if (env[NPM_COMMAND_ENV] !== "pack") {
return false;
}
const adapterPath = env[OCM_INTERNAL_NPM_BIN_ENV]?.trim();
if (!adapterPath || basename(adapterPath) !== OCM_ADAPTER_BASENAME) {
return false;
}
const workspaceDirs = (env[OCM_WORKSPACE_DIRS_ENV] ?? "")
.split(delimiter)
.map((entry) => entry.trim())
.filter(Boolean);
// OCM uses these same manifests to pack and install dependencies beside the root archive.
// Require the exact package here so unrelated ambient paths cannot bypass the plain-pack guard.
return workspaceDirs.some((workspaceDir) => {
try {
const manifest = JSON.parse(
readFileSync(join(workspaceDir, "package.json"), "utf8"),
) as PackageManifest;
return manifest.name === packageName;
} catch {
return false;
}
});
}
function normalizeFiles(files: Iterable<string>): Set<string> {
return new Set(Array.from(files, (file) => file.replace(/\\/g, "/")));
}
@@ -45,6 +77,9 @@ export function collectSourcePackWorkspaceDependencyErrors(
if (typeof aiDependency !== "string" || !aiDependency.trim().startsWith("workspace:")) {
return [];
}
if (ocmExternalizesWorkspacePackage("@openclaw/ai", env)) {
return [];
}
return [
`plain root packing cannot safely resolve @openclaw/ai from ${aiDependency}: pnpm rewrites the workspace dependency to an exact version without bundling the package`,
`use \`${SELF_CONTAINED_SOURCE_PACK_COMMAND}\` for a self-contained source package; official npm release automation prepares and publishes @openclaw/ai separately`,
+28
View File
@@ -92,6 +92,34 @@ describe("collectSourcePackWorkspaceDependencyErrors", () => {
OPENCLAW_PREPACK_PREPARED: "1",
}),
).toEqual([]);
expect(
collectSourcePackWorkspaceDependencyErrors(rootPackageJson, {
npm_command: "pack",
OCM_INTERNAL_NPM_BIN: path.join(rootDir, "scripts", "ocm-npm-workspace-deps.mjs"),
OPENCLAW_OCM_WORKSPACE_DEPENDENCY_DIRS: aiDir,
}),
).toEqual([]);
expect(
collectSourcePackWorkspaceDependencyErrors(rootPackageJson, {
npm_command: "pack",
OCM_INTERNAL_NPM_BIN: path.join(rootDir, "scripts", "ocm-npm-workspace-deps.mjs"),
OPENCLAW_OCM_WORKSPACE_DEPENDENCY_DIRS: rootDir,
}),
).toHaveLength(2);
expect(
collectSourcePackWorkspaceDependencyErrors(rootPackageJson, {
npm_command: "pack",
OCM_INTERNAL_NPM_BIN: path.join(rootDir, "scripts", "other-npm-wrapper.mjs"),
OPENCLAW_OCM_WORKSPACE_DEPENDENCY_DIRS: aiDir,
}),
).toHaveLength(2);
expect(
collectSourcePackWorkspaceDependencyErrors(rootPackageJson, {
npm_command: "publish",
OCM_INTERNAL_NPM_BIN: path.join(rootDir, "scripts", "ocm-npm-workspace-deps.mjs"),
OPENCLAW_OCM_WORKSPACE_DEPENDENCY_DIRS: aiDir,
}),
).toHaveLength(2);
});
});