fix(doctor): require an executable Hopper launcher
This commit is contained in:
parent
a67054f815
commit
aee9cb8e4e
|
|
@ -26,6 +26,7 @@ export interface DoctorHost {
|
|||
readonly configuredHopperPath?: string;
|
||||
macosVersion(): Promise<string | undefined>;
|
||||
readable(path: string): Promise<boolean>;
|
||||
executable(path: string): Promise<boolean>;
|
||||
brewHopperPath(): Promise<string | undefined>;
|
||||
manualHopperPaths(): Promise<readonly string[]>;
|
||||
}
|
||||
|
|
@ -72,7 +73,7 @@ export const runDoctor = async (
|
|||
].filter((value): value is string => value !== undefined);
|
||||
let hopperPath: string | undefined;
|
||||
for (const candidate of new Set(candidates))
|
||||
if (await host.readable(candidate)) {
|
||||
if (await host.executable(candidate)) {
|
||||
hopperPath = candidate;
|
||||
break;
|
||||
}
|
||||
|
|
@ -124,6 +125,14 @@ export const systemDoctorHost = (): DoctorHost => ({
|
|||
return false;
|
||||
}
|
||||
},
|
||||
async executable(path) {
|
||||
try {
|
||||
await access(path, constants.X_OK);
|
||||
return true;
|
||||
} catch {
|
||||
return false;
|
||||
}
|
||||
},
|
||||
async brewHopperPath() {
|
||||
return probeHomebrew(async (command) => {
|
||||
try {
|
||||
|
|
|
|||
|
|
@ -6,6 +6,7 @@ const host = (overrides: Partial<DoctorHost> = {}): DoctorHost => ({
|
|||
nodeVersion: "22.1.0",
|
||||
macosVersion: () => Promise.resolve("12.0"),
|
||||
readable: (path) => Promise.resolve(path.includes("Hopper")),
|
||||
executable: (path) => Promise.resolve(path.includes("Hopper")),
|
||||
brewHopperPath: () => Promise.resolve(undefined),
|
||||
manualHopperPaths: () => Promise.resolve([]),
|
||||
...overrides,
|
||||
|
|
@ -29,7 +30,7 @@ describe("doctor", () => {
|
|||
undefined,
|
||||
host({
|
||||
configuredHopperPath: "/manual/Hopper",
|
||||
readable: (path) => Promise.resolve(path === "/manual/Hopper"),
|
||||
executable: (path) => Promise.resolve(path === "/manual/Hopper"),
|
||||
}),
|
||||
);
|
||||
expect(result.hopperPath).toBe("/manual/Hopper");
|
||||
|
|
@ -41,7 +42,7 @@ describe("doctor", () => {
|
|||
const result = await runDoctor(
|
||||
undefined,
|
||||
host({
|
||||
readable: (candidate) => Promise.resolve(candidate === path),
|
||||
executable: (candidate) => Promise.resolve(candidate === path),
|
||||
brewHopperPath: () => Promise.resolve(path),
|
||||
}),
|
||||
);
|
||||
|
|
@ -53,10 +54,24 @@ describe("doctor", () => {
|
|||
const result = await runDoctor(
|
||||
undefined,
|
||||
host({
|
||||
readable: (candidate) => Promise.resolve(candidate === path),
|
||||
executable: (candidate) => Promise.resolve(candidate === path),
|
||||
manualHopperPaths: () => Promise.resolve([path]),
|
||||
}),
|
||||
);
|
||||
expect(result.hopperPath).toBe(path);
|
||||
});
|
||||
|
||||
it("rejects a readable Hopper launcher without execute permission", async () => {
|
||||
const path = "/manual/Hopper";
|
||||
const result = await runDoctor(
|
||||
undefined,
|
||||
host({
|
||||
configuredHopperPath: path,
|
||||
readable: () => Promise.resolve(true),
|
||||
executable: () => Promise.resolve(false),
|
||||
}),
|
||||
);
|
||||
expect(result.hopperPath).toBeUndefined();
|
||||
expect(result.checks.find(({ name }) => name === "hopper")?.ok).toBe(false);
|
||||
});
|
||||
});
|
||||
|
|
|
|||
Loading…
Reference in New Issue