fix(coding-agent): install checked pi update version

This commit is contained in:
Armin Ronacher
2026-06-21 14:46:07 +02:00
parent d93b92baca
commit bc0db64350
5 changed files with 105 additions and 42 deletions
+28 -14
View File
@@ -38,6 +38,18 @@ export interface SelfUpdateCommand extends SelfUpdateCommandStep {
steps?: SelfUpdateCommandStep[];
}
export type SelfUpdatePackageTarget = string | { packageName: string; installSpec?: string };
function normalizeSelfUpdatePackageTarget(target: SelfUpdatePackageTarget): {
packageName: string;
installSpec: string;
} {
if (typeof target === "string") {
return { packageName: target, installSpec: target };
}
return { packageName: target.packageName, installSpec: target.installSpec ?? target.packageName };
}
function makeSelfUpdateCommand(
installStep: SelfUpdateCommandStep,
uninstallStep?: SelfUpdateCommandStep,
@@ -103,9 +115,10 @@ function getInferredNpmInstall(): { root: string; prefix: string } | undefined {
function getSelfUpdateCommandForMethod(
method: InstallMethod,
installedPackageName: string,
updatePackageName = installedPackageName,
updatePackageTarget: SelfUpdatePackageTarget = installedPackageName,
npmCommand?: string[],
): SelfUpdateCommand | undefined {
const target = normalizeSelfUpdatePackageTarget(updatePackageTarget);
switch (method) {
case "bun-binary":
return undefined;
@@ -123,17 +136,17 @@ function getSelfUpdateCommandForMethod(
"--ignore-scripts",
"--config.minimumReleaseAge=0",
...binDirArgs,
updatePackageName,
target.installSpec,
]),
updatePackageName === installedPackageName
target.packageName === installedPackageName
? undefined
: makeSelfUpdateCommandStep("pnpm", ["remove", "-g", ...binDirArgs, installedPackageName]),
);
}
case "yarn":
return makeSelfUpdateCommand(
makeSelfUpdateCommandStep("yarn", ["global", "add", "--ignore-scripts", updatePackageName]),
updatePackageName === installedPackageName
makeSelfUpdateCommandStep("yarn", ["global", "add", "--ignore-scripts", target.installSpec]),
target.packageName === installedPackageName
? undefined
: makeSelfUpdateCommandStep("yarn", ["global", "remove", installedPackageName]),
);
@@ -144,9 +157,9 @@ function getSelfUpdateCommandForMethod(
"-g",
"--ignore-scripts",
"--minimum-release-age=0",
updatePackageName,
target.installSpec,
]),
updatePackageName === installedPackageName
target.packageName === installedPackageName
? undefined
: makeSelfUpdateCommandStep("bun", ["uninstall", "-g", installedPackageName]),
);
@@ -160,10 +173,10 @@ function getSelfUpdateCommandForMethod(
"-g",
"--ignore-scripts",
"--min-release-age=0",
updatePackageName,
target.installSpec,
]);
const uninstallStep =
updatePackageName === installedPackageName
target.packageName === installedPackageName
? undefined
: makeSelfUpdateCommandStep(command, [...prefixArgs, "uninstall", "-g", installedPackageName]);
return makeSelfUpdateCommand(installStep, uninstallStep);
@@ -302,10 +315,10 @@ function isManagedByGlobalPackageManager(method: InstallMethod, packageName: str
export function getSelfUpdateCommand(
packageName: string,
npmCommand?: string[],
updatePackageName = packageName,
updatePackageTarget: SelfUpdatePackageTarget = packageName,
): SelfUpdateCommand | undefined {
const method = detectInstallMethod();
const command = getSelfUpdateCommandForMethod(method, packageName, updatePackageName, npmCommand);
const command = getSelfUpdateCommandForMethod(method, packageName, updatePackageTarget, npmCommand);
if (!command || !isManagedByGlobalPackageManager(method, packageName, npmCommand) || !isSelfUpdatePathWritable()) {
return undefined;
}
@@ -315,20 +328,21 @@ export function getSelfUpdateCommand(
export function getSelfUpdateUnavailableInstruction(
packageName: string,
npmCommand?: string[],
updatePackageName = packageName,
updatePackageTarget: SelfUpdatePackageTarget = packageName,
): string {
const method = detectInstallMethod();
const target = normalizeSelfUpdatePackageTarget(updatePackageTarget);
if (method === "bun-binary") {
return `Download from: https://github.com/earendil-works/pi-mono/releases/latest`;
}
const command = getSelfUpdateCommandForMethod(method, packageName, updatePackageName, npmCommand);
const command = getSelfUpdateCommandForMethod(method, packageName, target, npmCommand);
if (command) {
if (isManagedByGlobalPackageManager(method, packageName, npmCommand) && !isSelfUpdatePathWritable()) {
return `This installation is managed by a global ${method} install, but the install path is not writable. Update it yourself with: ${command.display}`;
}
return `This installation is not managed by a global ${method} install. Update it with the package manager, wrapper, or source checkout that provides it.`;
}
return `Update ${updatePackageName} using the package manager, wrapper, or source checkout that provides this installation.`;
return `Update ${target.installSpec} using the package manager, wrapper, or source checkout that provides this installation.`;
}
export function getUpdateInstruction(packageName: string): string {
@@ -12,6 +12,7 @@ import {
getSelfUpdateUnavailableInstruction,
PACKAGE_NAME,
type SelfUpdateCommand,
type SelfUpdatePackageTarget,
VERSION,
} from "./config.ts";
import type { ExtensionFactory } from "./core/extensions/types.ts";
@@ -353,9 +354,12 @@ function updateTargetIncludesExtensions(target: UpdateTarget): boolean {
return target.type === "all" || target.type === "extensions";
}
function printSelfUpdateUnavailable(npmCommand?: string[], updatePackageName = PACKAGE_NAME): void {
function printSelfUpdateUnavailable(
npmCommand?: string[],
updatePackageTarget: SelfUpdatePackageTarget = PACKAGE_NAME,
): void {
console.error(`error: ${APP_NAME} cannot self-update this installation.`);
console.error(getSelfUpdateUnavailableInstruction(PACKAGE_NAME, npmCommand, updatePackageName));
console.error(getSelfUpdateUnavailableInstruction(PACKAGE_NAME, npmCommand, updatePackageTarget));
const entrypoint = process.argv[1];
if (entrypoint) {
@@ -390,27 +394,38 @@ function printSelfUpdateNote(note: string): void {
interface SelfUpdatePlan {
packageName: string;
installSpec: string;
version: string;
shouldRun: boolean;
note?: string;
}
async function getSelfUpdatePlan(force: boolean): Promise<SelfUpdatePlan> {
if (force) {
return { packageName: PACKAGE_NAME, shouldRun: true };
let latestRelease: Awaited<ReturnType<typeof getLatestPiRelease>>;
try {
latestRelease = await getLatestPiRelease(VERSION);
} catch (error: unknown) {
const message = error instanceof Error ? error.message : String(error);
throw new Error(`Could not determine latest ${APP_NAME} version: ${message}`);
}
if (!latestRelease) {
throw new Error(`Could not determine latest ${APP_NAME} version.`);
}
try {
const latestRelease = await getLatestPiRelease(VERSION);
const packageName = latestRelease?.packageName ?? PACKAGE_NAME;
if (!latestRelease || packageName !== PACKAGE_NAME || isNewerPackageVersion(latestRelease.version, VERSION)) {
return { packageName, shouldRun: true, ...(latestRelease?.note ? { note: latestRelease.note } : {}) };
}
} catch {
return { packageName: PACKAGE_NAME, shouldRun: true };
const packageName = latestRelease.packageName ?? PACKAGE_NAME;
const installSpec = `${packageName}@${latestRelease.version}`;
if (force || packageName !== PACKAGE_NAME || isNewerPackageVersion(latestRelease.version, VERSION)) {
return {
packageName,
installSpec,
version: latestRelease.version,
...(latestRelease.note ? { note: latestRelease.note } : {}),
shouldRun: true,
};
}
console.log(chalk.green(`${APP_NAME} is already up to date (v${VERSION})`));
return { packageName: PACKAGE_NAME, shouldRun: false };
return { packageName, installSpec, version: latestRelease.version, shouldRun: false };
}
async function runSelfUpdate(command: SelfUpdateCommand): Promise<void> {
@@ -717,13 +732,13 @@ export async function handlePackageCommand(
process.exitCode = 1;
return true;
}
const selfUpdateCommand = getSelfUpdateCommand(
PACKAGE_NAME,
selfUpdateNpmCommand,
selfUpdatePlan.packageName,
);
const selfUpdateTarget = {
packageName: selfUpdatePlan.packageName,
installSpec: selfUpdatePlan.installSpec,
};
const selfUpdateCommand = getSelfUpdateCommand(PACKAGE_NAME, selfUpdateNpmCommand, selfUpdateTarget);
if (!selfUpdateCommand) {
printSelfUpdateUnavailable(selfUpdateNpmCommand, selfUpdatePlan.packageName);
printSelfUpdateUnavailable(selfUpdateNpmCommand, selfUpdateTarget);
process.exitCode = 1;
return true;
}
@@ -742,7 +757,7 @@ export async function handlePackageCommand(
process.exitCode = 1;
return true;
}
console.log(chalk.green(`Updated ${APP_NAME}`));
console.log(chalk.green(`Updated ${APP_NAME} from ${VERSION} to ${selfUpdatePlan.version}`));
}
return true;
}