fix(coding-agent): detect renamed npm self updates
This commit is contained in:
@@ -81,24 +81,19 @@ export function detectInstallMethod(): InstallMethod {
|
|||||||
return "unknown";
|
return "unknown";
|
||||||
}
|
}
|
||||||
|
|
||||||
function getInferredNpmInstall(packageName: string): { root: string; prefix: string } | undefined {
|
function getInferredNpmInstall(): { root: string; prefix: string } | undefined {
|
||||||
const packageDir = getPackageDir();
|
const packageDir = getPackageDir();
|
||||||
const path = process.platform === "win32" || packageDir.includes("\\") ? win32 : { basename, dirname };
|
const path = process.platform === "win32" || packageDir.includes("\\") ? win32 : { basename, dirname };
|
||||||
const [scope, name] = packageName.split("/");
|
const parent = path.dirname(packageDir);
|
||||||
let root: string | undefined;
|
let root: string | undefined;
|
||||||
if (
|
if (path.basename(parent).startsWith("@") && path.basename(path.dirname(parent)) === "node_modules") {
|
||||||
name &&
|
root = path.dirname(parent);
|
||||||
scope?.startsWith("@") &&
|
} else if (path.basename(parent) === "node_modules") {
|
||||||
path.basename(path.dirname(packageDir)) === scope &&
|
root = parent;
|
||||||
path.basename(packageDir) === name
|
|
||||||
) {
|
|
||||||
root = path.dirname(path.dirname(packageDir));
|
|
||||||
} else if (!name && path.basename(packageDir) === packageName) {
|
|
||||||
root = path.dirname(packageDir);
|
|
||||||
}
|
}
|
||||||
if (!root || path.basename(root) !== "node_modules") return undefined;
|
if (!root) return undefined;
|
||||||
const parent = path.dirname(root);
|
const rootParent = path.dirname(root);
|
||||||
if (path.basename(parent) === "lib") return { root, prefix: path.dirname(parent) };
|
if (path.basename(rootParent) === "lib") return { root, prefix: path.dirname(rootParent) };
|
||||||
// Windows global npm prefixes use `<prefix>\\node_modules`, which is
|
// Windows global npm prefixes use `<prefix>\\node_modules`, which is
|
||||||
// indistinguishable from local project installs by path shape alone. Do not
|
// indistinguishable from local project installs by path shape alone. Do not
|
||||||
// infer unsupported Windows custom prefixes without `npm root -g` evidence.
|
// infer unsupported Windows custom prefixes without `npm root -g` evidence.
|
||||||
@@ -137,7 +132,7 @@ function getSelfUpdateCommandForMethod(
|
|||||||
);
|
);
|
||||||
case "npm": {
|
case "npm": {
|
||||||
const [command = "npm", ...npmArgs] = npmCommand ?? [];
|
const [command = "npm", ...npmArgs] = npmCommand ?? [];
|
||||||
const inferred = npmCommand?.length ? undefined : getInferredNpmInstall(installedPackageName);
|
const inferred = npmCommand?.length ? undefined : getInferredNpmInstall();
|
||||||
const prefixArgs = [...npmArgs, ...(inferred ? ["--prefix", inferred.prefix] : [])];
|
const prefixArgs = [...npmArgs, ...(inferred ? ["--prefix", inferred.prefix] : [])];
|
||||||
const installStep = makeSelfUpdateCommandStep(command, [...prefixArgs, "install", "-g", updatePackageName]);
|
const installStep = makeSelfUpdateCommandStep(command, [...prefixArgs, "install", "-g", updatePackageName]);
|
||||||
const uninstallStep =
|
const uninstallStep =
|
||||||
@@ -169,7 +164,7 @@ function readCommandOutput(
|
|||||||
return undefined;
|
return undefined;
|
||||||
}
|
}
|
||||||
|
|
||||||
function getGlobalPackageRoots(method: InstallMethod, packageName: string, npmCommand?: string[]): string[] {
|
function getGlobalPackageRoots(method: InstallMethod, _packageName: string, npmCommand?: string[]): string[] {
|
||||||
switch (method) {
|
switch (method) {
|
||||||
case "npm": {
|
case "npm": {
|
||||||
const configured = !!npmCommand?.length;
|
const configured = !!npmCommand?.length;
|
||||||
@@ -187,7 +182,7 @@ function getGlobalPackageRoots(method: InstallMethod, packageName: string, npmCo
|
|||||||
const root = readCommandOutput(command, [...npmArgs, "root", "-g"], {
|
const root = readCommandOutput(command, [...npmArgs, "root", "-g"], {
|
||||||
requireSuccess: configured,
|
requireSuccess: configured,
|
||||||
});
|
});
|
||||||
const inferred = configured ? undefined : getInferredNpmInstall(packageName);
|
const inferred = configured ? undefined : getInferredNpmInstall();
|
||||||
return [root, inferred?.root].filter((x): x is string => !!x);
|
return [root, inferred?.root].filter((x): x is string => !!x);
|
||||||
}
|
}
|
||||||
case "pnpm": {
|
case "pnpm": {
|
||||||
|
|||||||
@@ -2,7 +2,7 @@ import { mkdirSync, readFileSync, realpathSync, rmSync, writeFileSync } from "no
|
|||||||
import { tmpdir } from "node:os";
|
import { tmpdir } from "node:os";
|
||||||
import { join } from "node:path";
|
import { join } from "node:path";
|
||||||
import { afterEach, beforeEach, describe, expect, it, vi } from "vitest";
|
import { afterEach, beforeEach, describe, expect, it, vi } from "vitest";
|
||||||
import { ENV_AGENT_DIR, PACKAGE_NAME } from "../src/config.js";
|
import { ENV_AGENT_DIR, PACKAGE_NAME, VERSION } from "../src/config.js";
|
||||||
import { main } from "../src/main.js";
|
import { main } from "../src/main.js";
|
||||||
|
|
||||||
describe("package commands", () => {
|
describe("package commands", () => {
|
||||||
@@ -16,6 +16,11 @@ describe("package commands", () => {
|
|||||||
let originalExitCode: typeof process.exitCode;
|
let originalExitCode: typeof process.exitCode;
|
||||||
let originalExecPath: string;
|
let originalExecPath: string;
|
||||||
|
|
||||||
|
function getNewerPatchVersion(): string {
|
||||||
|
const [major = "0", minor = "0", patch = "0"] = VERSION.split(".");
|
||||||
|
return `${major}.${minor}.${Number.parseInt(patch, 10) + 1}`;
|
||||||
|
}
|
||||||
|
|
||||||
beforeEach(() => {
|
beforeEach(() => {
|
||||||
tempDir = join(tmpdir(), `pi-package-commands-${Date.now()}-${Math.random().toString(36).slice(2)}`);
|
tempDir = join(tmpdir(), `pi-package-commands-${Date.now()}-${Math.random().toString(36).slice(2)}`);
|
||||||
agentDir = join(tempDir, "agent");
|
agentDir = join(tempDir, "agent");
|
||||||
@@ -201,7 +206,7 @@ else fs.writeFileSync(${JSON.stringify(recordPath)},JSON.stringify(args));
|
|||||||
value: join(selfPackageDir, "dist", "cli.js"),
|
value: join(selfPackageDir, "dist", "cli.js"),
|
||||||
configurable: true,
|
configurable: true,
|
||||||
});
|
});
|
||||||
const fetchMock = vi.fn(async () => Response.json({ version: "0.73.1" }));
|
const fetchMock = vi.fn(async () => Response.json({ version: getNewerPatchVersion() }));
|
||||||
vi.stubGlobal("fetch", fetchMock);
|
vi.stubGlobal("fetch", fetchMock);
|
||||||
|
|
||||||
const logSpy = vi.spyOn(console, "log").mockImplementation(() => {});
|
const logSpy = vi.spyOn(console, "log").mockImplementation(() => {});
|
||||||
|
|||||||
Reference in New Issue
Block a user