Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
36 changes: 36 additions & 0 deletions apps/server/src/provider/providerMaintenanceRunner.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -451,6 +451,42 @@ describe("providerMaintenanceRunner", () => {
);
});

it.effect("installs the advertised version when pnpm owns the global install", () => {
const calls: Array<{ command: string; args: ReadonlyArray<string> }> = [];
return Effect.gen(function* () {
const { registry } = yield* makeRegistry(baseProvider);
const updater = yield* makeTestRunner({
...registry,
getProviderMaintenanceCapabilitiesForInstance: (_instanceId, provider) =>
Effect.succeed(
makeProviderMaintenanceCapabilities({
provider,
packageName: "@openai/codex",
updateExecutable: "pnpm",
updateArgs: ["add", "-g", "@openai/codex@latest"],
updateLockKey: "pnpm-global",
}),
),
});

yield* updater.updateProvider(CODEX_DRIVER);
assert.deepStrictEqual(calls, [
{ command: "pnpm", args: ["add", "-g", "@openai/codex@9.9.9"] },
]);
}).pipe(
Effect.provide(
Layer.mergeAll(
layerNonWindowsPlatform,
layerLatestVersionHttpClient("9.9.9"),
layerMockSpawner((command, args) => {
calls.push({ command, args });
return { stdout: "updated" };
}),
),
),
);
});

it("splits installer output into clean status lines", () => {
assert.deepStrictEqual(ProviderMaintenanceRunner.splitOutputLines("Down", "loading\r\n 5"), {
lines: ["Downloading"],
Expand Down
3 changes: 2 additions & 1 deletion apps/server/src/provider/providerMaintenanceRunner.ts
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,7 @@ import { makeProviderMaintenanceCommandCoordinator } from "./providerMaintenance
import {
enrichProviderSnapshotWithVersionAdvisory,
makeTargetedProviderUpdateAction,
resolveLatestProviderUpdateAction,
resolveLatestProviderVersion,
type ProviderMaintenanceCommandAction,
} from "@t3tools/provider-core/server/maintenanceResolver";
Expand Down Expand Up @@ -478,7 +479,7 @@ export const make = Effect.fn("ProviderMaintenanceRunner.make")(function* () {
const command =
targetVersion !== undefined
? makeTargetedProviderUpdateAction(fresh, targetVersion)
: fresh.update;
: resolveLatestProviderUpdateAction(fresh, candidateVersion);
const rejected =
targetVersion !== undefined
? !command ||
Expand Down
36 changes: 36 additions & 0 deletions packages/provider-core/src/server/maintenanceResolver.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -208,6 +208,42 @@ it.layer(NodeServices.layer)("providerMaintenance", (it) => {
});
});

it("pins the advertised version in a pnpm-global update command", () => {
const pnpmTool = makeProviderMaintenanceCapabilities({
provider: driver("packageTool"),
packageName: "@example/package-tool",
updateExecutable: "pnpm",
updateArgs: ["add", "-g", "@example/package-tool@latest"],
updateLockKey: "pnpm-global",
});
const npmTool = makeProviderMaintenanceCapabilities({
provider: driver("packageTool"),
packageName: "@example/package-tool",
updateExecutable: "npm",
updateArgs: ["install", "-g", "@example/package-tool@latest"],
updateLockKey: "npm-global:/opt/node",
});
const advisoryFor = (
maintenanceCapabilities: typeof pnpmTool,
latestVersion: string | null = "1.1.0",
) =>
createProviderVersionAdvisory({
driver: driver("packageTool"),
currentVersion: "1.0.0",
latestVersion,
maintenanceCapabilities,
});

expect(advisoryFor(pnpmTool).updateCommand).toBe("pnpm add -g @example/package-tool@1.1.0");
expect(advisoryFor(npmTool).updateCommand).toBe("npm install -g @example/package-tool@latest");
expect(advisoryFor(pnpmTool, "1.1.0-beta.1").updateCommand).toBe(
"pnpm add -g @example/package-tool@latest",
);
expect(advisoryFor(pnpmTool, null).updateCommand).toBe(
"pnpm add -g @example/package-tool@latest",
);
});

it("keeps the manual update hint when the install is behind but unowned", () => {
expect(
createProviderVersionAdvisory({
Expand Down
16 changes: 15 additions & 1 deletion packages/provider-core/src/server/maintenanceResolver.ts
Original file line number Diff line number Diff line change
Expand Up @@ -196,6 +196,20 @@ export function makeTargetedProviderUpdateAction(
return { ...update, args, command };
}

/**
* The action that installs `latestVersion`. pnpm 11 and later resolve `@latest`
* to the newest release older than `minimumReleaseAge` and still exit 0, so a
* pnpm-global update installs the advertised version by exact spec instead.
*/
export function resolveLatestProviderUpdateAction(
capabilities: ProviderMaintenanceCapabilities,
latestVersion: string | null,
): ProviderMaintenanceCommandAction | null {
const update = capabilities.update;
if (update?.lockKey !== "pnpm-global" || latestVersion === null) return update;
return makeTargetedProviderUpdateAction(capabilities, latestVersion) ?? update;
Comment thread
coderabbitai[bot] marked this conversation as resolved.
}

export function makeManualOnlyProviderMaintenanceCapabilities(input: {
readonly provider: ProviderDriverKind;
readonly packageName: string | null;
Expand Down Expand Up @@ -732,7 +746,7 @@ export function createProviderVersionAdvisory(input: {
status: advisory.status,
currentVersion: input.currentVersion,
latestVersion,
updateCommand: capabilities.update?.command ?? null,
updateCommand: resolveLatestProviderUpdateAction(capabilities, latestVersion)?.command ?? null,
canUpdate: capabilities.update !== null,
canInstallVersion: makeTargetedProviderUpdateAction(capabilities, "0.0.0") !== null,
checkedAt: input.checkedAt ?? null,
Expand Down
Loading