mirror of
https://github.com/badlogic/pi-mono.git
synced 2026-11-05 23:37:25 +00:00
fix(coding-agent): skip fd/rg downloads on unsupported architectures
Only x64 and arm64 map to prebuilt release archives. ARM32 and ia32 previously fell back to x86-64 binaries; now pi warns and recommends the system package manager. fixes #11209
This commit is contained in:
parent
ff68964715
commit
c8ec752cf9
2 changed files with 62 additions and 33 deletions
|
|
@ -24,7 +24,19 @@ interface ToolConfig {
|
|||
binaryName: string; // Name of the binary inside the archive
|
||||
systemBinaryNames?: string[]; // Alternative system command names to try before downloading
|
||||
tagPrefix: string; // Prefix for tags (e.g., "v" for v1.0.0, "" for 1.0.0)
|
||||
getAssetName: (version: string, plat: string, architecture: string) => string | null;
|
||||
getAssetName: (version: string, target: string) => string;
|
||||
}
|
||||
|
||||
// Map Node's platform/arch to the release target suffix shared by fd and ripgrep.
|
||||
// Only x64 and arm64 are mapped. Other architectures (e.g. ARM32 `arm`, `ia32`)
|
||||
// must not fall back to x86-64 binaries (#11209).
|
||||
function getReleaseTarget(plat: string, architecture: string): string | undefined {
|
||||
const archStr = architecture === "arm64" ? "aarch64" : architecture === "x64" ? "x86_64" : undefined;
|
||||
if (!archStr) return undefined;
|
||||
if (plat === "darwin") return `${archStr}-apple-darwin.tar.gz`;
|
||||
if (plat === "linux") return `${archStr}-unknown-linux-musl.tar.gz`;
|
||||
if (plat === "win32") return `${archStr}-pc-windows-msvc.zip`;
|
||||
return undefined;
|
||||
}
|
||||
|
||||
const TOOLS: Record<string, ToolConfig> = {
|
||||
|
|
@ -34,38 +46,14 @@ const TOOLS: Record<string, ToolConfig> = {
|
|||
binaryName: "fd",
|
||||
systemBinaryNames: ["fd", "fdfind"],
|
||||
tagPrefix: "v",
|
||||
getAssetName: (version, plat, architecture) => {
|
||||
if (plat === "darwin") {
|
||||
const archStr = architecture === "arm64" ? "aarch64" : "x86_64";
|
||||
return `fd-v${version}-${archStr}-apple-darwin.tar.gz`;
|
||||
} else if (plat === "linux") {
|
||||
const archStr = architecture === "arm64" ? "aarch64" : "x86_64";
|
||||
return `fd-v${version}-${archStr}-unknown-linux-musl.tar.gz`;
|
||||
} else if (plat === "win32") {
|
||||
const archStr = architecture === "arm64" ? "aarch64" : "x86_64";
|
||||
return `fd-v${version}-${archStr}-pc-windows-msvc.zip`;
|
||||
}
|
||||
return null;
|
||||
},
|
||||
getAssetName: (version, target) => `fd-v${version}-${target}`,
|
||||
},
|
||||
rg: {
|
||||
name: "ripgrep",
|
||||
repo: "BurntSushi/ripgrep",
|
||||
binaryName: "rg",
|
||||
tagPrefix: "",
|
||||
getAssetName: (version, plat, architecture) => {
|
||||
if (plat === "darwin") {
|
||||
const archStr = architecture === "arm64" ? "aarch64" : "x86_64";
|
||||
return `ripgrep-${version}-${archStr}-apple-darwin.tar.gz`;
|
||||
} else if (plat === "linux") {
|
||||
const archStr = architecture === "arm64" ? "aarch64" : "x86_64";
|
||||
return `ripgrep-${version}-${archStr}-unknown-linux-musl.tar.gz`;
|
||||
} else if (plat === "win32") {
|
||||
const archStr = architecture === "arm64" ? "aarch64" : "x86_64";
|
||||
return `ripgrep-${version}-${archStr}-pc-windows-msvc.zip`;
|
||||
}
|
||||
return null;
|
||||
},
|
||||
getAssetName: (version, target) => `ripgrep-${version}-${target}`,
|
||||
},
|
||||
};
|
||||
|
||||
|
|
@ -262,16 +250,15 @@ async function downloadTool(tool: "fd" | "rg"): Promise<string> {
|
|||
|
||||
const plat = platform();
|
||||
const architecture = arch();
|
||||
const target = getReleaseTarget(plat, architecture);
|
||||
if (!target) {
|
||||
throw new Error(`Unsupported platform: ${plat}/${architecture}`);
|
||||
}
|
||||
|
||||
// fd is pinned on darwin/x64, so skip the version lookup there.
|
||||
const version =
|
||||
tool === "fd" && plat === "darwin" && architecture === "x64" ? "10.3.0" : await getLatestVersion(config.repo);
|
||||
|
||||
// Get asset name for this platform
|
||||
const assetName = config.getAssetName(version, plat, architecture);
|
||||
if (!assetName) {
|
||||
throw new Error(`Unsupported platform: ${plat}/${architecture}`);
|
||||
}
|
||||
const assetName = config.getAssetName(version, target);
|
||||
|
||||
// Create tools directory
|
||||
mkdirSync(TOOLS_DIR, { recursive: true });
|
||||
|
|
@ -372,6 +359,15 @@ export async function ensureTool(
|
|||
return undefined;
|
||||
}
|
||||
|
||||
// Prebuilt downloads only exist for the targets mapped in getReleaseTarget.
|
||||
if (!getReleaseTarget(platform(), arch())) {
|
||||
onStatus?.({
|
||||
type: "warning",
|
||||
message: `${config.name} not found. Automatic download is not supported on ${platform()}/${arch()}. Install ${config.name} with your system package manager.`,
|
||||
});
|
||||
return undefined;
|
||||
}
|
||||
|
||||
// Tool not found - download it
|
||||
onStatus?.({ type: "info", message: `${config.name} not found. Downloading...` });
|
||||
|
||||
|
|
|
|||
|
|
@ -1,5 +1,7 @@
|
|||
import type * as ChildProcess from "node:child_process";
|
||||
import type * as Fs from "node:fs";
|
||||
import type * as Os from "node:os";
|
||||
import { arch, platform } from "os";
|
||||
import { afterEach, describe, expect, it, vi } from "vitest";
|
||||
import { ensureTool, getLatestVersion, type ToolStatus } from "../src/utils/tools-manager.ts";
|
||||
|
||||
|
|
@ -13,6 +15,15 @@ vi.mock("fs", async (importOriginal) => {
|
|||
};
|
||||
});
|
||||
|
||||
vi.mock("os", async (importOriginal) => {
|
||||
const actual = await importOriginal<typeof Os>();
|
||||
return {
|
||||
...actual,
|
||||
arch: vi.fn(() => "x64"),
|
||||
platform: vi.fn(() => "linux"),
|
||||
};
|
||||
});
|
||||
|
||||
vi.mock("child_process", async (importOriginal) => {
|
||||
const actual = await importOriginal<typeof ChildProcess>();
|
||||
return {
|
||||
|
|
@ -25,6 +36,8 @@ afterEach(() => {
|
|||
if (originalOffline === undefined) delete process.env.PI_OFFLINE;
|
||||
else process.env.PI_OFFLINE = originalOffline;
|
||||
vi.unstubAllGlobals();
|
||||
vi.mocked(arch).mockReturnValue("x64");
|
||||
vi.mocked(platform).mockReturnValue("linux");
|
||||
});
|
||||
|
||||
function redirectResponse(location: string): Response {
|
||||
|
|
@ -139,4 +152,24 @@ describe("ensureTool", () => {
|
|||
},
|
||||
]);
|
||||
});
|
||||
|
||||
// #11209: ARM32 previously fell back to x86-64 release archives.
|
||||
it.each(["arm", "ia32"])("does not download x86-64 binaries on %s", async (architecture) => {
|
||||
delete process.env.PI_OFFLINE;
|
||||
vi.mocked(arch).mockReturnValue(architecture as NodeJS.Architecture);
|
||||
const fetchMock = vi.fn();
|
||||
vi.stubGlobal("fetch", fetchMock);
|
||||
const statuses: ToolStatus[] = [];
|
||||
|
||||
const result = await ensureTool("rg", (status) => statuses.push(status));
|
||||
|
||||
expect(result).toBeUndefined();
|
||||
expect(fetchMock).not.toHaveBeenCalled();
|
||||
expect(statuses).toEqual([
|
||||
{
|
||||
type: "warning",
|
||||
message: `ripgrep not found. Automatic download is not supported on linux/${architecture}. Install ripgrep with your system package manager.`,
|
||||
},
|
||||
]);
|
||||
});
|
||||
});
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue