mirror of
https://github.com/openclaw/openclaw.git
synced 2026-04-22 22:52:03 +00:00
fix: verify global npm correction installs
This commit is contained in:
@@ -28,6 +28,14 @@ const pathExists = vi.fn();
|
||||
const syncPluginsForUpdateChannel = vi.fn();
|
||||
const updateNpmInstalledPlugins = vi.fn();
|
||||
const { defaultRuntime: runtimeCapture, resetRuntimeCapture } = createCliRuntimeCapture();
|
||||
const REQUIRED_BUNDLED_RUNTIME_SIDECARS = [
|
||||
"dist/extensions/whatsapp/light-runtime-api.js",
|
||||
"dist/extensions/whatsapp/runtime-api.js",
|
||||
"dist/extensions/matrix/helper-api.js",
|
||||
"dist/extensions/matrix/runtime-api.js",
|
||||
"dist/extensions/matrix/thread-bindings-runtime.js",
|
||||
"dist/extensions/msteams/runtime-api.js",
|
||||
] as const;
|
||||
|
||||
vi.mock("@clack/prompts", () => ({
|
||||
confirm,
|
||||
@@ -615,6 +623,58 @@ describe("update-cli", () => {
|
||||
}
|
||||
});
|
||||
|
||||
it("fails package updates when the installed correction version does not match the requested target", async () => {
|
||||
const tempDir = createCaseDir("openclaw-update");
|
||||
const nodeModules = path.join(tempDir, "node_modules");
|
||||
const pkgRoot = path.join(nodeModules, "openclaw");
|
||||
mockPackageInstallStatus(tempDir);
|
||||
await fs.mkdir(pkgRoot, { recursive: true });
|
||||
await fs.writeFile(
|
||||
path.join(pkgRoot, "package.json"),
|
||||
JSON.stringify({ name: "openclaw", version: "2026.3.23" }),
|
||||
"utf-8",
|
||||
);
|
||||
for (const relativePath of REQUIRED_BUNDLED_RUNTIME_SIDECARS) {
|
||||
const absolutePath = path.join(pkgRoot, relativePath);
|
||||
await fs.mkdir(path.dirname(absolutePath), { recursive: true });
|
||||
await fs.writeFile(absolutePath, "export {};\n", "utf-8");
|
||||
}
|
||||
readPackageVersion.mockResolvedValue("2026.3.23");
|
||||
pathExists.mockImplementation(async (candidate: string) =>
|
||||
REQUIRED_BUNDLED_RUNTIME_SIDECARS.some(
|
||||
(relativePath) => candidate === path.join(pkgRoot, relativePath),
|
||||
),
|
||||
);
|
||||
vi.mocked(runCommandWithTimeout).mockImplementation(async (argv) => {
|
||||
if (Array.isArray(argv) && argv[0] === "npm" && argv[1] === "root" && argv[2] === "-g") {
|
||||
return {
|
||||
stdout: nodeModules,
|
||||
stderr: "",
|
||||
code: 0,
|
||||
signal: null,
|
||||
killed: false,
|
||||
termination: "exit",
|
||||
};
|
||||
}
|
||||
return {
|
||||
stdout: "",
|
||||
stderr: "",
|
||||
code: 0,
|
||||
signal: null,
|
||||
killed: false,
|
||||
termination: "exit",
|
||||
};
|
||||
});
|
||||
|
||||
await updateCommand({ yes: true, tag: "2026.3.23-2" });
|
||||
|
||||
expect(defaultRuntime.exit).toHaveBeenCalledWith(1);
|
||||
expect(writeConfigFile).not.toHaveBeenCalled();
|
||||
const logs = vi.mocked(defaultRuntime.log).mock.calls.map((call) => String(call[0]));
|
||||
expect(logs.join("\n")).toContain("global install verify");
|
||||
expect(logs.join("\n")).toContain("expected installed version 2026.3.23-2, found 2026.3.23");
|
||||
});
|
||||
|
||||
it("prepends portable Git PATH for package updates on Windows", async () => {
|
||||
const platformSpy = vi.spyOn(process, "platform", "get").mockReturnValue("win32");
|
||||
const tempDir = createCaseDir("openclaw-update");
|
||||
|
||||
@@ -24,10 +24,12 @@ import {
|
||||
checkUpdateStatus,
|
||||
} from "../../infra/update-check.js";
|
||||
import {
|
||||
collectInstalledGlobalPackageErrors,
|
||||
canResolveRegistryVersionForPackageTarget,
|
||||
createGlobalInstallEnv,
|
||||
cleanupGlobalRenameDirs,
|
||||
globalInstallArgs,
|
||||
resolveExpectedInstalledVersionFromSpec,
|
||||
resolveGlobalInstallSpec,
|
||||
resolveGlobalPackageRoot,
|
||||
} from "../../infra/update-global.js";
|
||||
@@ -343,9 +345,27 @@ async function runPackageInstallUpdate(params: {
|
||||
const steps = [updateStep];
|
||||
let afterVersion = beforeVersion;
|
||||
|
||||
if (pkgRoot) {
|
||||
afterVersion = await readPackageVersion(pkgRoot);
|
||||
const entryPath = path.join(pkgRoot, "dist", "entry.js");
|
||||
const verifiedPackageRoot =
|
||||
(await resolveGlobalPackageRoot(manager, runCommand, params.timeoutMs)) ?? pkgRoot;
|
||||
if (verifiedPackageRoot) {
|
||||
afterVersion = await readPackageVersion(verifiedPackageRoot);
|
||||
const expectedVersion = resolveExpectedInstalledVersionFromSpec(packageName, installSpec);
|
||||
const verificationErrors = await collectInstalledGlobalPackageErrors({
|
||||
packageRoot: verifiedPackageRoot,
|
||||
expectedVersion,
|
||||
});
|
||||
if (verificationErrors.length > 0) {
|
||||
steps.push({
|
||||
name: "global install verify",
|
||||
command: `verify ${verifiedPackageRoot}`,
|
||||
cwd: verifiedPackageRoot,
|
||||
durationMs: 0,
|
||||
exitCode: 1,
|
||||
stderrTail: verificationErrors.join("\n"),
|
||||
stdoutTail: null,
|
||||
});
|
||||
}
|
||||
const entryPath = path.join(verifiedPackageRoot, "dist", "entry.js");
|
||||
if (await pathExists(entryPath)) {
|
||||
const doctorStep = await runUpdateStep({
|
||||
name: `${CLI_NAME} doctor`,
|
||||
@@ -361,7 +381,7 @@ async function runPackageInstallUpdate(params: {
|
||||
return {
|
||||
status: failedStep ? "error" : "ok",
|
||||
mode: manager,
|
||||
root: pkgRoot ?? params.root,
|
||||
root: verifiedPackageRoot ?? params.root,
|
||||
reason: failedStep ? failedStep.name : undefined,
|
||||
before: { version: beforeVersion },
|
||||
after: { version: afterVersion },
|
||||
|
||||
Reference in New Issue
Block a user