Skip to content
Merged
38 changes: 30 additions & 8 deletions apps/cli/src/helpers/core/install-dependencies.ts
Original file line number Diff line number Diff line change
Expand Up @@ -66,6 +66,19 @@ export function getInstallArgs(packageManager: PackageManager): string[] {
return ["install"];
}

export function getInstallRetryArgs(
packageManager: PackageManager,
error: unknown,
): string[] | null {
if (packageManager !== "bun") return null;

const stderr = (error as { stderr?: unknown })?.stderr;
const output = typeof stderr === "string" ? stderr : toErrorMessage(error);
return /No version matching .+ found for specifier .+\(but package exists\)/is.test(output)
? ["install", "--force"]
: null;
}

export async function installDependencies({
projectDir,
packageManager,
Expand All @@ -81,14 +94,23 @@ export async function installDependencies({
s.start(`Running ${packageManager} install...`);

const installArgs = getInstallArgs(packageManager);
await $({
cwd: projectDir,
env: {
...process.env,
...getInstallEnvironment(packageManager),
},
stderr: ["inherit", "pipe"],
})`${packageManager} ${installArgs}`;
const runInstall = async (args: string[]) =>
$({
cwd: projectDir,
env: {
...process.env,
...getInstallEnvironment(packageManager),
},
stderr: ["inherit", "pipe"],
})`${packageManager} ${args}`;

try {
await runInstall(installArgs);
} catch (error) {
const retryArgs = getInstallRetryArgs(packageManager, error);
if (!retryArgs) throw error;
await runInstall(retryArgs);
}

s.stop("Dependencies installed successfully");
return { step, success: true };
Expand Down
17 changes: 17 additions & 0 deletions apps/cli/test/generation/install-dependencies.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ import { setupDatabase } from "@/helpers/core/db-setup";
import {
getInstallArgs,
getInstallEnvironment,
getInstallRetryArgs,
installDependencies,
} from "@/helpers/core/install-dependencies";

Expand Down Expand Up @@ -38,6 +39,22 @@ describe("getInstallArgs", () => {
});
});

describe("getInstallRetryArgs", () => {
it("refreshes Bun metadata after a registry propagation miss", () => {
const error = {
stderr:
'error: No version matching "4.0.71" found for specifier "@ai-sdk/gateway" (but package exists)',
};

expect(getInstallRetryArgs("bun", error)).toEqual(["install", "--force"]);
expect(getInstallRetryArgs("npm", error)).toBeNull();
});

it("does not retry unrelated Bun install failures", () => {
expect(getInstallRetryArgs("bun", new Error("network unavailable"))).toBeNull();
});
});

describe("installDependencies", () => {
it("returns a failure result instead of swallowing the error when install fails", async () => {
// A non-existent cwd makes the install command fail fast (ENOENT) without
Expand Down
Loading
Loading