mirror of
https://github.com/openclaw/openclaw.git
synced 2026-05-06 18:10:45 +00:00
fix: simplify bundled runtime dependency repair (#75183)
Summary: - Merged fix: simplify bundled runtime dependency repair after ClawSweeper review. ClawSweeper fixups: - Included follow-up commit: fix: verify cached bundled runtime roots - Included follow-up commit: refactor: simplify plugin runtime startup paths - Included follow-up commit: refactor: trim plugin startup policy helpers - Included follow-up commit: refactor: trust package manager runtime deps materialization - Included follow-up commit: fix: narrow channel runtime deps skip policy - Included follow-up commit: refactor: defer startup plugin runtime deps - Ran the ClawSweeper repair loop before final review. Validation: - ClawSweeper review passed for head04dc566534. - Required merge gates passed before the squash merge. Prepared head SHA:04dc566534Review: https://github.com/openclaw/openclaw/pull/75183#issuecomment-4358383786 Co-authored-by: Peter Steinberger <steipete@gmail.com> Co-authored-by: Shakker <shakkerdroid@gmail.com> Co-authored-by: clawsweeper-repair <clawsweeper-repair@users.noreply.github.com>
This commit is contained in:
committed by
GitHub
parent
8ce44b057f
commit
250376f885
@@ -6,6 +6,8 @@ import {
|
||||
createBundledRuntimeDependencyInstallArgs,
|
||||
createBundledRuntimeDependencyInstallEnv,
|
||||
createNestedNpmInstallEnv,
|
||||
} from "../../scripts/lib/bundled-runtime-deps-install.mjs";
|
||||
import {
|
||||
isDirectPostinstallInvocation,
|
||||
pruneOpenClawCompileCache,
|
||||
pruneInstalledPackageDist,
|
||||
@@ -51,41 +53,6 @@ async function writePluginPackage(
|
||||
}
|
||||
|
||||
describe("bundled plugin postinstall", () => {
|
||||
function createNpmInstallArgs(...packages: string[]) {
|
||||
return createBundledRuntimeDependencyInstallArgs(packages);
|
||||
}
|
||||
|
||||
function createBareNpmRunner(packages: string[]) {
|
||||
return {
|
||||
command: "npm",
|
||||
args: createNpmInstallArgs(...packages),
|
||||
env: {
|
||||
HOME: "/tmp/home",
|
||||
PATH: "/tmp/node/bin",
|
||||
},
|
||||
shell: false as const,
|
||||
};
|
||||
}
|
||||
|
||||
function expectNpmInstallSpawn(
|
||||
spawnSync: ReturnType<typeof vi.fn>,
|
||||
packageRoot: string,
|
||||
packages: string[],
|
||||
) {
|
||||
expect(spawnSync).toHaveBeenCalledWith("npm", createNpmInstallArgs(...packages), {
|
||||
cwd: packageRoot,
|
||||
encoding: "utf8",
|
||||
env: {
|
||||
HOME: "/tmp/home",
|
||||
PATH: "/tmp/node/bin",
|
||||
},
|
||||
shell: false,
|
||||
stdio: "pipe",
|
||||
windowsHide: true,
|
||||
windowsVerbatimArguments: undefined,
|
||||
});
|
||||
}
|
||||
|
||||
it("recognizes direct invocation through symlinked temp prefixes", () => {
|
||||
const realpathSync = vi.fn((value: string) =>
|
||||
value.replace(/^\/var\/folders\//u, "/private/var/folders/"),
|
||||
@@ -125,9 +92,14 @@ describe("bundled plugin postinstall", () => {
|
||||
it("clears global npm config before nested installs", () => {
|
||||
expect(
|
||||
createNestedNpmInstallEnv({
|
||||
NPM_CONFIG_WORKSPACES: "true",
|
||||
npm_config_global: "true",
|
||||
npm_config_include_workspace_root: "true",
|
||||
npm_config_ignore_scripts: "false",
|
||||
npm_config_location: "global",
|
||||
npm_config_prefix: "/opt/homebrew",
|
||||
npm_config_workspace: "extensions/telegram",
|
||||
npm_config_workspaces: "true",
|
||||
HOME: "/tmp/home",
|
||||
}),
|
||||
).toEqual({
|
||||
@@ -139,13 +111,17 @@ describe("bundled plugin postinstall", () => {
|
||||
expect(createBundledRuntimeDependencyInstallArgs(["acpx@0.4.1"])).toEqual([
|
||||
"install",
|
||||
"--ignore-scripts",
|
||||
"--workspaces=false",
|
||||
"acpx@0.4.1",
|
||||
]);
|
||||
expect(
|
||||
createBundledRuntimeDependencyInstallEnv({
|
||||
HOME: "/tmp/home",
|
||||
NPM_CONFIG_IGNORE_SCRIPTS: "false",
|
||||
npm_config_dry_run: "true",
|
||||
npm_config_ignore_scripts: "false",
|
||||
npm_config_prefix: "/opt/homebrew",
|
||||
npm_config_workspaces: "true",
|
||||
}),
|
||||
).toEqual({
|
||||
HOME: "/tmp/home",
|
||||
@@ -154,9 +130,11 @@ describe("bundled plugin postinstall", () => {
|
||||
npm_config_fetch_retry_maxtimeout: "120000",
|
||||
npm_config_fetch_retry_mintimeout: "10000",
|
||||
npm_config_fetch_timeout: "300000",
|
||||
npm_config_ignore_scripts: "true",
|
||||
npm_config_legacy_peer_deps: "true",
|
||||
npm_config_package_lock: "false",
|
||||
npm_config_save: "false",
|
||||
npm_config_workspaces: "false",
|
||||
});
|
||||
});
|
||||
|
||||
@@ -174,7 +152,6 @@ describe("bundled plugin postinstall", () => {
|
||||
env: { HOME: "/tmp/home" },
|
||||
extensionsDir,
|
||||
packageRoot,
|
||||
npmRunner: createBareNpmRunner(["acpx@0.4.1"]),
|
||||
spawnSync,
|
||||
log: { log: vi.fn(), warn: vi.fn() },
|
||||
});
|
||||
@@ -714,34 +691,6 @@ describe("bundled plugin postinstall", () => {
|
||||
expect(unlinkSync).toHaveBeenCalledWith("/pkg/dist/stale.js");
|
||||
});
|
||||
|
||||
it("runs nested local installs with sanitized env when the sentinel package is missing", async () => {
|
||||
const extensionsDir = await createExtensionsDir();
|
||||
const packageRoot = path.dirname(path.dirname(extensionsDir));
|
||||
await writePluginPackage(extensionsDir, "acpx", {
|
||||
dependencies: {
|
||||
acpx: "0.4.1",
|
||||
},
|
||||
});
|
||||
const spawnSync = vi.fn(() => ({ status: 0, stderr: "", stdout: "" }));
|
||||
|
||||
runBundledPluginPostinstall({
|
||||
env: {
|
||||
OPENCLAW_EAGER_BUNDLED_PLUGIN_DEPS: "1",
|
||||
npm_config_global: "true",
|
||||
npm_config_location: "global",
|
||||
npm_config_prefix: "/opt/homebrew",
|
||||
HOME: "/tmp/home",
|
||||
},
|
||||
extensionsDir,
|
||||
packageRoot,
|
||||
npmRunner: createBareNpmRunner(["acpx@0.4.1"]),
|
||||
spawnSync,
|
||||
log: { log: vi.fn(), warn: vi.fn() },
|
||||
});
|
||||
|
||||
expectNpmInstallSpawn(spawnSync, packageRoot, ["acpx@0.4.1"]);
|
||||
});
|
||||
|
||||
it("skips reinstall when the bundled sentinel package already exists", async () => {
|
||||
const extensionsDir = await createExtensionsDir();
|
||||
const packageRoot = path.dirname(path.dirname(extensionsDir));
|
||||
@@ -768,26 +717,6 @@ describe("bundled plugin postinstall", () => {
|
||||
expect(spawnSync).not.toHaveBeenCalled();
|
||||
});
|
||||
|
||||
it("reinstalls bundled runtime deps when optional native children are missing", async () => {
|
||||
const extensionsDir = await createExtensionsDir();
|
||||
const packageRoot = path.dirname(path.dirname(extensionsDir));
|
||||
await writeDiscordDaveyOptionalDependencyFixture(extensionsDir, packageRoot);
|
||||
const spawnSync = vi.fn(() => ({ status: 0, stderr: "", stdout: "" }));
|
||||
|
||||
runBundledPluginPostinstall({
|
||||
env: { HOME: "/tmp/home", OPENCLAW_EAGER_BUNDLED_PLUGIN_DEPS: "1" },
|
||||
extensionsDir,
|
||||
packageRoot,
|
||||
arch: "arm64",
|
||||
npmRunner: createBareNpmRunner(["@snazzah/davey@0.1.11"]),
|
||||
platform: "win32",
|
||||
spawnSync,
|
||||
log: { log: vi.fn(), warn: vi.fn() },
|
||||
});
|
||||
|
||||
expectNpmInstallSpawn(spawnSync, packageRoot, ["@snazzah/davey@0.1.11"]);
|
||||
});
|
||||
|
||||
it("does not reinstall when only another platform optional native child is missing", async () => {
|
||||
const extensionsDir = await createExtensionsDir();
|
||||
const packageRoot = path.dirname(path.dirname(extensionsDir));
|
||||
@@ -863,103 +792,6 @@ describe("bundled plugin postinstall", () => {
|
||||
);
|
||||
});
|
||||
|
||||
it("installs missing bundled plugin runtime deps during global installs", async () => {
|
||||
const extensionsDir = await createExtensionsDir();
|
||||
const packageRoot = path.dirname(path.dirname(extensionsDir));
|
||||
await writePluginPackage(extensionsDir, "slack", {
|
||||
dependencies: {
|
||||
"@slack/web-api": "7.11.0",
|
||||
},
|
||||
});
|
||||
await writePluginPackage(extensionsDir, "telegram", {
|
||||
dependencies: {
|
||||
grammy: "1.38.4",
|
||||
},
|
||||
});
|
||||
const spawnSync = vi.fn(() => ({ status: 0, stderr: "", stdout: "" }));
|
||||
|
||||
runBundledPluginPostinstall({
|
||||
env: {
|
||||
OPENCLAW_EAGER_BUNDLED_PLUGIN_DEPS: "1",
|
||||
npm_config_global: "true",
|
||||
npm_config_location: "global",
|
||||
npm_config_prefix: "/opt/homebrew",
|
||||
HOME: "/tmp/home",
|
||||
},
|
||||
extensionsDir,
|
||||
packageRoot,
|
||||
npmRunner: createBareNpmRunner(["@slack/web-api@7.11.0", "grammy@1.38.4"]),
|
||||
spawnSync,
|
||||
log: { log: vi.fn(), warn: vi.fn() },
|
||||
});
|
||||
|
||||
expectNpmInstallSpawn(spawnSync, packageRoot, ["@slack/web-api@7.11.0", "grammy@1.38.4"]);
|
||||
});
|
||||
|
||||
it("installs only missing bundled plugin runtime deps", async () => {
|
||||
const extensionsDir = await createExtensionsDir();
|
||||
const packageRoot = path.dirname(path.dirname(extensionsDir));
|
||||
await writePluginPackage(extensionsDir, "slack", {
|
||||
dependencies: {
|
||||
"@slack/web-api": "7.11.0",
|
||||
},
|
||||
});
|
||||
await writePluginPackage(extensionsDir, "telegram", {
|
||||
dependencies: {
|
||||
grammy: "1.38.4",
|
||||
},
|
||||
});
|
||||
await fs.mkdir(path.join(packageRoot, "node_modules", "@slack", "web-api"), {
|
||||
recursive: true,
|
||||
});
|
||||
await fs.writeFile(
|
||||
path.join(packageRoot, "node_modules", "@slack", "web-api", "package.json"),
|
||||
"{}\n",
|
||||
);
|
||||
const spawnSync = vi.fn(() => ({ status: 0, stderr: "", stdout: "" }));
|
||||
|
||||
runBundledPluginPostinstall({
|
||||
env: {
|
||||
OPENCLAW_EAGER_BUNDLED_PLUGIN_DEPS: "1",
|
||||
HOME: "/tmp/home",
|
||||
},
|
||||
extensionsDir,
|
||||
packageRoot,
|
||||
npmRunner: createBareNpmRunner(["grammy@1.38.4"]),
|
||||
spawnSync,
|
||||
log: { log: vi.fn(), warn: vi.fn() },
|
||||
});
|
||||
|
||||
expectNpmInstallSpawn(spawnSync, packageRoot, ["grammy@1.38.4"]);
|
||||
});
|
||||
|
||||
it("installs bundled plugin deps when npm location is global", async () => {
|
||||
const extensionsDir = await createExtensionsDir();
|
||||
const packageRoot = path.dirname(path.dirname(extensionsDir));
|
||||
await writePluginPackage(extensionsDir, "telegram", {
|
||||
dependencies: {
|
||||
grammy: "1.38.4",
|
||||
},
|
||||
});
|
||||
const spawnSync = vi.fn(() => ({ status: 0, stderr: "", stdout: "" }));
|
||||
|
||||
runBundledPluginPostinstall({
|
||||
env: {
|
||||
OPENCLAW_EAGER_BUNDLED_PLUGIN_DEPS: "1",
|
||||
npm_config_location: "global",
|
||||
npm_config_prefix: "/opt/homebrew",
|
||||
HOME: "/tmp/home",
|
||||
},
|
||||
extensionsDir,
|
||||
packageRoot,
|
||||
npmRunner: createBareNpmRunner(["grammy@1.38.4"]),
|
||||
spawnSync,
|
||||
log: { log: vi.fn(), warn: vi.fn() },
|
||||
});
|
||||
|
||||
expectNpmInstallSpawn(spawnSync, packageRoot, ["grammy@1.38.4"]);
|
||||
});
|
||||
|
||||
it("prunes only bundled plugin package node_modules in source checkouts", async () => {
|
||||
const packageRoot = await createTempDirAsync("openclaw-source-prune-");
|
||||
const extensionsDir = path.join(packageRoot, "extensions");
|
||||
|
||||
Reference in New Issue
Block a user