Skip to content

Commit c233e1f

Browse files
authored
fix: locate running binary via execPath instead of argv[0] (#16)
In a Bun single-file executable, process.argv[0] is the literal "bun", not the executable path. `rb update` therefore failed with "cannot locate current binary at bun", and the macOS quarantine check in `rb doctor` misresolved the same way. Resolve the path via process.execPath (the compiled binary) through a shared getBinPath() helper used by both update and doctor. Verified end-to-end on a windows-x64 standalone build: update now locates itself, downloads, checksum-verifies, and swaps successfully.
1 parent a4e3550 commit c233e1f

4 files changed

Lines changed: 35 additions & 6 deletions

File tree

src/__tests__/bin-path.test.ts

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
import { describe, it, expect } from "bun:test";
2+
import { getBinPath } from "../lib/bin-path.js";
3+
4+
describe("getBinPath", () => {
5+
it("returns process.execPath (the real executable), not argv[0]", () => {
6+
// In a compiled Bun binary, process.argv[0] is the literal "bun" while
7+
// process.execPath is the actual rb.exe path. We must use execPath.
8+
expect(getBinPath()).toBe(process.execPath);
9+
});
10+
11+
it("never returns the bare runtime name 'bun'", () => {
12+
expect(getBinPath()).not.toBe("bun");
13+
});
14+
15+
it("returns a non-empty path", () => {
16+
expect(getBinPath().length).toBeGreaterThan(0);
17+
});
18+
});

src/commands/doctor.ts

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@ import { spawnSync } from "node:child_process";
99
import { defineCommand } from "citty";
1010
import { VERSION } from "../generated/version.js";
1111
import { getConfigDir } from "../lib/auth.js";
12+
import { getBinPath } from "../lib/bin-path.js";
1213

1314
// Compile-time defines from `bun build --compile --define`. In dev mode they're undefined.
1415
declare const IS_STANDALONE: boolean | undefined;
@@ -263,7 +264,7 @@ async function checkGhRateLimit(): Promise<Check> {
263264
}
264265

265266
export async function runDoctor(opts: { json?: boolean; fix?: boolean } = {}): Promise<void> {
266-
const binPath = process.argv[0] ?? "";
267+
const binPath = getBinPath();
267268
const checks: Check[] = [
268269
await checkVersion(),
269270
checkInstallMethod(),

src/commands/update.ts

Lines changed: 1 addition & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@ import { spawnSync } from "node:child_process";
99
import { createHash } from "node:crypto";
1010
import { defineCommand } from "citty";
1111
import { VERSION } from "../generated/version.js";
12+
import { getBinPath } from "../lib/bin-path.js";
1213

1314
// Compile-time defines from `bun build --compile --define`. In dev mode they're undefined.
1415
declare const IS_STANDALONE: boolean | undefined;
@@ -102,11 +103,6 @@ function sha256(data: Uint8Array): string {
102103
return createHash("sha256").update(data).digest("hex");
103104
}
104105

105-
function getBinPath(): string {
106-
// argv[0] in a compiled bun binary is the binary path
107-
return process.argv[0] ?? "";
108-
}
109-
110106
function isSameVersion(latest: string): boolean {
111107
return parseVersion(latest) === VERSION;
112108
}

src/lib/bin-path.ts

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
/**
2+
* Absolute path to the currently-running `rb` executable.
3+
*
4+
* In a Bun single-file executable (`bun build --compile`), `process.execPath`
5+
* is the compiled binary itself (the `rb` / `rb.exe`). `process.argv[0]` is the
6+
* embedded runtime name — the literal string `"bun"` — and must NOT be used:
7+
* doing so broke `rb update` ("cannot locate current binary at bun") and the
8+
* macOS quarantine check, which both need the real on-disk path.
9+
*
10+
* Verified on a compiled binary: execPath → "<dir>/rb.exe", argv[0] → "bun".
11+
*/
12+
export function getBinPath(): string {
13+
return process.execPath || "";
14+
}

0 commit comments

Comments
 (0)