Skip to content

Commit a0bcde4

Browse files
roodboiclaude
andcommitted
fix(global): install prepares host trust env independent of keychain step
Mirrors the globalTrust restructure into bootstrapMacGlobalInstall: configureMacHostTlsTrust runs once a cert path exists, with a note when the keychain step didn't complete. Also neutralizes fixture paths that tripped the privacy check in CI. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
1 parent fc56b74 commit a0bcde4

3 files changed

Lines changed: 82 additions & 4 deletions

File tree

src/commands/global.ts

Lines changed: 9 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -723,8 +723,15 @@ async function bootstrapMacGlobalInstall(): Promise<void> {
723723
const certPath = await exportCaddyLocalCaCert();
724724
if (certPath) {
725725
const trustReady = await ensureMacTrustCaddyLocalCa({ certPath });
726-
if (trustReady) {
727-
await configureMacHostTlsTrust({ certPath });
726+
// Mirror globalTrust: the host trust env (Bun/Node/curl/git) is
727+
// independent of the macOS System keychain step — prepare it regardless
728+
// so non-interactive installs still get CLI-tool trust.
729+
await configureMacHostTlsTrust({ certPath });
730+
if (!trustReady) {
731+
note(
732+
"Host trust env is prepared, but the browser will still show warnings for https://*.hack until the System keychain step runs. Run `hack global trust` interactively to finish it.",
733+
"TLS"
734+
);
728735
}
729736
}
730737
await maybeOfferMacRecoverySetup();

tests/daemon-orphan.test.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@ import { expect, test } from "bun:test";
33
import { findOrphanDaemonProcesses } from "../src/daemon/process.ts";
44

55
const PS_LINES = [
6-
" 123 /Users/x/.hack/bin/hack daemon start --foreground",
6+
" 123 /tmp/hack-home/.hack/bin/hack daemon start --foreground",
77
" 456 /opt/homebrew/bin/hack daemon start --foreground",
88
" 789 vim src/commands/daemon.ts",
99
" 999 hack daemon status",
@@ -44,7 +44,7 @@ import {
4444

4545
test("virtual bunfs executable paths are rejected", () => {
4646
expect(isVirtualExecutablePath("/$bunfs/root/hack")).toBe(true);
47-
expect(isVirtualExecutablePath("/Users/x/.hack/bin/hack")).toBe(false);
47+
expect(isVirtualExecutablePath("/tmp/hack-home/.hack/bin/hack")).toBe(false);
4848
});
4949

5050
test("extracts the launchd program path from plist text", () => {

tests/global-command.macos.test.ts

Lines changed: 71 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1412,3 +1412,74 @@ test("global trust skips the keychain step under --no-interactive when sudo need
14121412
])
14131413
);
14141414
});
1415+
1416+
test("global install still prepares host TLS env when keychain trust is declined", async () => {
1417+
await prepareManagedTools(tempDir!);
1418+
reachabilityByHost = {
1419+
[DEFAULT_CADDY_IP]: true,
1420+
[DEFAULT_HOST_DNS_IP]: true,
1421+
};
1422+
confirmResponder = () => false;
1423+
1424+
const localCaPath = join(
1425+
tempDir!,
1426+
GLOBAL_HACK_DIR_NAME,
1427+
GLOBAL_CADDY_DIR_NAME,
1428+
"pki",
1429+
"caddy-local-authority.crt"
1430+
);
1431+
await mkdir(dirname(localCaPath), { recursive: true });
1432+
await writeFile(
1433+
localCaPath,
1434+
"-----BEGIN CERTIFICATE-----\nLOCAL\n-----END CERTIFICATE-----\n"
1435+
);
1436+
1437+
execMockResponder = (cmd) => {
1438+
if (
1439+
cmd[0] === "docker" &&
1440+
cmd[1] === "compose" &&
1441+
cmd[2] === "-f" &&
1442+
cmd[4] === "ps"
1443+
) {
1444+
return { exitCode: 0, stdout: "caddy-123\n", stderr: "" };
1445+
}
1446+
if (
1447+
cmd[0] === "security" &&
1448+
cmd[1] === "find-certificate" &&
1449+
cmd[2] === "-a" &&
1450+
cmd[3] === "-p"
1451+
) {
1452+
return {
1453+
exitCode: 0,
1454+
stdout:
1455+
"-----BEGIN CERTIFICATE-----\nSYSTEM\n-----END CERTIFICATE-----\n",
1456+
stderr: "",
1457+
};
1458+
}
1459+
return null;
1460+
};
1461+
1462+
const { runCli } = await import("../src/cli/run.ts");
1463+
const code = await runCli(["global", "install"]);
1464+
1465+
const bundlePath = join(
1466+
tempDir!,
1467+
GLOBAL_HACK_DIR_NAME,
1468+
GLOBAL_CADDY_DIR_NAME,
1469+
"pki",
1470+
"caddy-host-trust-bundle.pem"
1471+
);
1472+
const envScriptPath = join(
1473+
tempDir!,
1474+
GLOBAL_HACK_DIR_NAME,
1475+
GLOBAL_CADDY_DIR_NAME,
1476+
"pki",
1477+
"caddy-host-trust-env.sh"
1478+
);
1479+
1480+
expect(code).toBe(0);
1481+
// Same contract as `global trust`: declining the System keychain step
1482+
// (browser trust) must not block the Bun/Node/curl/git host trust env.
1483+
expect(await Bun.file(bundlePath).text()).toContain("LOCAL");
1484+
expect(await fileExists(envScriptPath)).toBe(true);
1485+
});

0 commit comments

Comments
 (0)