feat(doctor): admit the Windows x64 Ghidra boundary

This commit is contained in:
N0zoM1z0 2026-07-17 01:15:31 +08:00 committed by N0zoM1z0
parent 0b3551076d
commit 32dda0c19e
3 changed files with 63 additions and 5 deletions

View File

@ -64,6 +64,7 @@ export interface DoctorProviderInspection {
/** Read-only host capabilities required by diagnostics. */ /** Read-only host capabilities required by diagnostics. */
export interface DoctorHost { export interface DoctorHost {
readonly platform: NodeJS.Platform; readonly platform: NodeJS.Platform;
readonly architecture: NodeJS.Architecture;
readonly nodeVersion: string; readonly nodeVersion: string;
readonly configuredHopperPath?: string; readonly configuredHopperPath?: string;
readonly configuredIlspyCmdPath?: string; readonly configuredIlspyCmdPath?: string;
@ -141,18 +142,19 @@ export const runDoctor = async (
host.platform === "linux" ? await host.linuxDistribution() : undefined; host.platform === "linux" ? await host.linuxDistribution() : undefined;
const supportedHost = const supportedHost =
(host.platform === "darwin" && macosMajor >= 12) || (host.platform === "darwin" && macosMajor >= 12) ||
(host.platform === "linux" && linuxDistribution?.supported === true); (host.platform === "linux" && linuxDistribution?.supported === true) ||
(host.platform === "win32" && host.architecture === "x64");
checks.push( checks.push(
check( check(
"host", "host",
supportedHost, supportedHost,
macosVersion ?? macosVersion ??
(linuxDistribution === undefined (linuxDistribution === undefined
? host.platform ? `${host.platform} ${host.architecture}`
: `${linuxDistribution.id} ${linuxDistribution.versionId ?? "unknown"}`), : `${linuxDistribution.id} ${linuxDistribution.versionId ?? "unknown"}`),
{ {
remediation: remediation:
"REA supports macOS 12+, Ubuntu 24.04+, Fedora 41+, and 64-bit Arch Linux.", "REA supports macOS 12+, Ubuntu 24.04+, Fedora 41+, 64-bit Arch Linux, and the experimental Windows x64 Ghidra P0 boundary.",
classification: "unsupported_host", classification: "unsupported_host",
}, },
), ),
@ -357,6 +359,7 @@ export const systemDoctorHost = (
options: SystemDoctorHostOptions = {}, options: SystemDoctorHostOptions = {},
): DoctorHost => ({ ): DoctorHost => ({
platform: process.platform, platform: process.platform,
architecture: process.arch,
nodeVersion: process.versions.node, nodeVersion: process.versions.node,
...(process.env.HOPPER_LAUNCHER_PATH === undefined ...(process.env.HOPPER_LAUNCHER_PATH === undefined
? {} ? {}

View File

@ -5,6 +5,7 @@ import { PRODUCT_IDENTITY } from "../src/identity.js";
const host = (overrides: Partial<DoctorHost> = {}): DoctorHost => ({ const host = (overrides: Partial<DoctorHost> = {}): DoctorHost => ({
platform: "darwin", platform: "darwin",
architecture: "x64",
nodeVersion: "24.18.0", nodeVersion: "24.18.0",
macosVersion: () => Promise.resolve("12.0"), macosVersion: () => Promise.resolve("12.0"),
linuxDistribution: () => Promise.resolve(undefined), linuxDistribution: () => Promise.resolve(undefined),
@ -40,7 +41,7 @@ describe("doctor", () => {
), ),
).toEqual({ ).toEqual({
node: "Install Node.js 22.19+ or 24.11+.", node: "Install Node.js 22.19+ or 24.11+.",
host: "REA supports macOS 12+, Ubuntu 24.04+, Fedora 41+, and 64-bit Arch Linux.", host: "REA supports macOS 12+, Ubuntu 24.04+, Fedora 41+, 64-bit Arch Linux, and the experimental Windows x64 Ghidra P0 boundary.",
hopper: "Run rea setup to install Hopper, or set HOPPER_LAUNCHER_PATH.", hopper: "Run rea setup to install Hopper, or set HOPPER_LAUNCHER_PATH.",
target: "Supply a readable local app or program path.", target: "Supply a readable local app or program path.",
}); });
@ -56,6 +57,36 @@ describe("doctor", () => {
expect(result.checks.find(({ name }) => name === "host")?.ok).toBe(false); expect(result.checks.find(({ name }) => name === "host")?.ok).toBe(false);
}, },
); );
it("admits only the experimental Windows x64 host boundary", async () => {
const windows = await runDoctor(
undefined,
host({
platform: "win32",
architecture: "x64",
macosVersion: () => Promise.resolve(undefined),
executable: () => Promise.resolve(false),
}),
);
const arm = await runDoctor(
undefined,
host({
platform: "win32",
architecture: "arm64",
macosVersion: () => Promise.resolve(undefined),
executable: () => Promise.resolve(false),
}),
);
expect(windows.checks.find(({ name }) => name === "host")).toMatchObject({
ok: true,
detail: "win32 x64",
});
expect(arm.checks.find(({ name }) => name === "host")).toMatchObject({
ok: false,
detail: "win32 arm64",
});
});
it("detects a manual configured Hopper before Homebrew", async () => { it("detects a manual configured Hopper before Homebrew", async () => {
const result = await runDoctor( const result = await runDoctor(
undefined, undefined,

View File

@ -30,12 +30,13 @@ const inspection = (
options: { options: {
readonly installDir?: string; readonly installDir?: string;
readonly architecture?: NodeJS.Architecture; readonly architecture?: NodeJS.Architecture;
readonly platform?: NodeJS.Platform;
} = { installDir: INSTALL }, } = { installDir: INSTALL },
overrides: Partial<GhidraInstallationHost> = {}, overrides: Partial<GhidraInstallationHost> = {},
): GhidraInstallationInspection => ): GhidraInstallationInspection =>
inspectGhidraInstallation( inspectGhidraInstallation(
{ {
platform: "linux", platform: options.platform ?? "linux",
architecture: options.architecture ?? "x64", architecture: options.architecture ?? "x64",
...(options.installDir === undefined ...(options.installDir === undefined
? {} ? {}
@ -49,6 +50,7 @@ const doctorHost = (
hopperAvailable = false, hopperAvailable = false,
): DoctorHost => ({ ): DoctorHost => ({
platform: "linux", platform: "linux",
architecture: "x64",
nodeVersion: "24.18.0", nodeVersion: "24.18.0",
macosVersion: () => Promise.resolve(undefined), macosVersion: () => Promise.resolve(undefined),
linuxDistribution: () => linuxDistribution: () =>
@ -98,6 +100,28 @@ describe("Ghidra doctor integration", () => {
}); });
}); });
it("accepts the experimental Windows x64 Ghidra provider boundary", async () => {
const ghidra = inspection({
installDir: "C:\\tools\\ghidra_12.1.2_PUBLIC",
platform: "win32",
});
const result = await runDoctor(undefined, {
...doctorHost(ghidra),
platform: "win32",
architecture: "x64",
linuxDistribution: () => Promise.resolve(undefined),
});
expect(result.healthy).toBe(true);
expect(result.checks.find(({ name }) => name === "host")).toMatchObject({
ok: true,
detail: "win32 x64",
});
expect(result.checks.find(({ name }) => name === "ghidra")).toMatchObject({
ok: true,
});
});
it.each([ it.each([
[ [
"bad installation", "bad installation",