mirror of
https://github.com/badlogic/pi-mono.git
synced 2026-07-09 17:28:45 +00:00
fix(coding-agent): add pnpm self-update prune hint (#6279)
Adds a pnpm-specific self-update recovery hint when pi update fails during self-update. Fixes #6215.
This commit is contained in:
parent
c9715af358
commit
4a9c962b59
2 changed files with 61 additions and 2 deletions
|
|
@ -372,6 +372,11 @@ function printSelfUpdateFallback(command: SelfUpdateCommand): void {
|
|||
console.error(chalk.dim(`If this keeps failing, run this command yourself: ${command.display}`));
|
||||
}
|
||||
|
||||
function printPnpmSelfUpdateMetadataHint(): void {
|
||||
console.error(chalk.yellow("If pnpm reports missing package versions, its cached registry metadata may be stale."));
|
||||
console.error(chalk.yellow(`Run \`pnpm store prune\` and retry \`${APP_NAME} update --self\`.`));
|
||||
}
|
||||
|
||||
function printSelfUpdateNote(note: string): void {
|
||||
const trimmedNote = note.trim();
|
||||
if (!trimmedNote) {
|
||||
|
|
@ -753,6 +758,9 @@ export async function handlePackageCommand(
|
|||
} catch (error: unknown) {
|
||||
const message = error instanceof Error ? error.message : "Unknown package command error";
|
||||
console.error(chalk.red(`Error: ${message}`));
|
||||
if (installMethod === "pnpm") {
|
||||
printPnpmSelfUpdateMetadataHint();
|
||||
}
|
||||
printSelfUpdateFallback(selfUpdateCommand);
|
||||
process.exitCode = 1;
|
||||
return true;
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
import { existsSync, mkdirSync, readFileSync, realpathSync, rmSync, writeFileSync } from "node:fs";
|
||||
import { chmodSync, existsSync, mkdirSync, readFileSync, realpathSync, rmSync, writeFileSync } from "node:fs";
|
||||
import { tmpdir } from "node:os";
|
||||
import { join } from "node:path";
|
||||
import { delimiter, join } from "node:path";
|
||||
import { afterEach, beforeEach, describe, expect, it, vi } from "vitest";
|
||||
import { ENV_AGENT_DIR, PACKAGE_NAME, VERSION } from "../src/config.ts";
|
||||
import { ProjectTrustStore } from "../src/core/trust-manager.ts";
|
||||
|
|
@ -15,6 +15,7 @@ describe("package commands", () => {
|
|||
let originalCwd: string;
|
||||
let originalAgentDir: string | undefined;
|
||||
let originalPiPackageDir: string | undefined;
|
||||
let originalPath: string | undefined;
|
||||
let originalExitCode: typeof process.exitCode;
|
||||
let originalExecPath: string;
|
||||
|
||||
|
|
@ -39,6 +40,7 @@ describe("package commands", () => {
|
|||
originalCwd = process.cwd();
|
||||
originalAgentDir = process.env[ENV_AGENT_DIR];
|
||||
originalPiPackageDir = process.env.PI_PACKAGE_DIR;
|
||||
originalPath = process.env.PATH;
|
||||
originalExitCode = process.exitCode;
|
||||
originalExecPath = process.execPath;
|
||||
process.exitCode = undefined;
|
||||
|
|
@ -69,6 +71,11 @@ describe("package commands", () => {
|
|||
} else {
|
||||
process.env.PI_PACKAGE_DIR = originalPiPackageDir;
|
||||
}
|
||||
if (originalPath === undefined) {
|
||||
delete process.env.PATH;
|
||||
} else {
|
||||
process.env.PATH = originalPath;
|
||||
}
|
||||
Object.defineProperty(process, "execPath", { value: originalExecPath, configurable: true });
|
||||
rmSync(tempDir, { recursive: true, force: true });
|
||||
});
|
||||
|
|
@ -524,6 +531,50 @@ else {
|
|||
}
|
||||
});
|
||||
|
||||
it("prints a pnpm metadata hint when self-update fails", async () => {
|
||||
const globalRoot = join(tempDir, "pnpm", "global", "v11");
|
||||
const selfPackageDir = join(globalRoot, "node_modules", "@earendil-works", "pi-coding-agent");
|
||||
const fakeBinDir = join(tempDir, "bin");
|
||||
const fakePnpmPath = join(fakeBinDir, process.platform === "win32" ? "pnpm.cmd" : "pnpm");
|
||||
mkdirSync(selfPackageDir, { recursive: true });
|
||||
mkdirSync(fakeBinDir, { recursive: true });
|
||||
writeFileSync(join(selfPackageDir, "package.json"), JSON.stringify({ name: PACKAGE_NAME, version: VERSION }));
|
||||
const fakePnpmScript =
|
||||
process.platform === "win32"
|
||||
? `@echo off\r\nif "%1"=="root" if "%2"=="-g" (echo ${globalRoot} & exit /b 0)\r\nexit /b 23\r\n`
|
||||
: `#!/bin/sh\nif [ "$1" = "root" ] && [ "$2" = "-g" ]; then\n\tprintf '%s\\n' '${globalRoot.replaceAll("'", "'\\''")}'\n\texit 0\nfi\nexit 23\n`;
|
||||
writeFileSync(fakePnpmPath, fakePnpmScript);
|
||||
chmodSync(fakePnpmPath, 0o755);
|
||||
process.env.PATH = `${fakeBinDir}${process.env.PATH ? `${delimiter}${process.env.PATH}` : ""}`;
|
||||
process.env.PI_PACKAGE_DIR = selfPackageDir;
|
||||
Object.defineProperty(process, "execPath", {
|
||||
value: join(tempDir, "pnpm", "bin", "node"),
|
||||
configurable: true,
|
||||
});
|
||||
vi.stubGlobal(
|
||||
"fetch",
|
||||
vi.fn(async () => Response.json({ version: getNewerPatchVersion() })),
|
||||
);
|
||||
|
||||
const logSpy = vi.spyOn(console, "log").mockImplementation(() => {});
|
||||
const errorSpy = vi.spyOn(console, "error").mockImplementation(() => {});
|
||||
|
||||
try {
|
||||
await expect(runPackageCommandDirectly(["update", "--self"])).resolves.toBeUndefined();
|
||||
|
||||
expect(process.exitCode).toBe(1);
|
||||
const stdout = logSpy.mock.calls.map(([message]) => String(message)).join("\n");
|
||||
const stderr = errorSpy.mock.calls.map(([message]) => String(message)).join("\n");
|
||||
expect(stdout).not.toContain("Updated pi");
|
||||
expect(stderr).toContain("exited with code 23");
|
||||
expect(stderr).toContain("If pnpm reports missing package versions");
|
||||
expect(stderr).toContain("Run `pnpm store prune` and retry `pi update --self`.");
|
||||
} finally {
|
||||
logSpy.mockRestore();
|
||||
errorSpy.mockRestore();
|
||||
}
|
||||
});
|
||||
|
||||
it("fails self-update when renamed npm package installation fails", async () => {
|
||||
const globalPrefix = join(tempDir, "global-prefix");
|
||||
const selfPackageDir = join(globalPrefix, "lib", "node_modules", "@mariozechner", "pi-coding-agent");
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue