Repository navigation
Conversation
On macOS, fs.realpathSync() resolves a path through fcntl(F_GETPATH). For a file with several hard links, F_GETPATH reports the name that was looked up last. The test hard-linked bunExe() into its temporary directory. Other tests of the same CI batch run bunExe() all the time, so the realpath of the link was sometimes the path of bunExe() itself, and the assertion on the symlink target failed. A clone (COPYFILE_FICLONE) is a file of its own, so its realpath is stable. Linux keeps the hardlink.
|
Status: ready for review.
|
|
Understand this PR’s impact Explore downstream dependencies and potential security impact with Blast Radius. No actionable comments were generated in the recent review. 🎉 ℹ️ Recent review info⚙️ Run configurationConfiguration used: Repository: oven-sh/bun/.coderabbit.yaml Review profile: ASSERTIVE Plan: Essentials Run ID: 📒 Files selected for processing (1)
Included review availability: Your plan provides up to 10 included reviews per hour; 2 remain after this review. WalkthroughChangesThe completions test now detects macOS and creates its temporary executable with an APFS clone. Other platforms use a hardlink and fall back to copying when linking fails. Executable preparation —
Suggested reviewers: Priority: ⬇️ Low 🚥 Pre-merge checks | ✅ 4✅ Passed checks (4 passed)
Comment |
|
Updated 4:40 PM PT - Sep 21st, 2026
✅ @robobun, your commit 381103d08effa8b54acf5171f30b716a2837b6f2 passed in 🧪 To try this PR locally: bunx bun-pr 43721That installs a local version of the PR into your bun-43721 --bun |
|
The runtime fix is in #43760. With it, If #43760 lands, this PR is not needed. The hard link in |
Problem
test/cli/bun.test.ts> "installs a bunx symlink to the executable, falling back through the install directories" fails its first attempt in 36 of the last 400 CI builds, only on macOS aarch64.expect(received).toBe(expected)at:287:Expected: ".../bun-darwin-aarch64-profile/bun-profile",Received: "/private/tmp/buntmp-.../bin/bun".bunExe()into its temporary directory and expects the symlink target to befs.realpathSync()of the link. On macOS that call sometimes returns the path ofbunExe()itself.fcntl(F_GETPATH)(src/sys/lib.rs:7529), whichfs.realpathSync()uses on macOS. For a file with several hard links it reports the name that was looked up last. Other tests of the batch runbunExe()all the time.Fix
fs.copyFileSyncwithCOPYFILE_FICLONE). A clone has one name, so its realpath is stable.realpathreads the name the file was opened with.test/cli/bun.test.tspasses.Background
F_GETPATHbuilds a path from the name and parent that the kernel cached for the open file. A lookup through a different link replaces that cached name.Notes
CI data. Last 400 finished builds (118369 to 119356, all branches): 36 builds, each time the retry passes, 33 on
:mac: any aarch64and 3 on:mac: 14 aarch64, always "1 retry". The 12 failing jobs I mapped to agents all ran on macOS 15 Tart guests (darwin-arm64-{ciabatta,sourdough,focaccia}-tart-15-*).The primitive alone, macOS 15.7.9, bun 1.3.14. A second process runs the original executable in a loop. The script calls
fs.realpathSync()2000 times on a hardlink to that executable and 2000 times on a clone of it:Without the second process the hardlink resolves to its own path in 200 of 200 calls. macOS 26.6.1 shows the same race (157 of 3000 calls return the other name for a copy of
/bin/echo).The first step of the test, same host, same second process, 150 runs per variant (create the private executable, run
<exe> completions, comparereadlink(bin/bunx)withfs.realpathSync(exe)):Not in this PR.
fs.realpathSync()of a hard link can name a different link on macOS. Node'sfs.realpathSyncwalks the path and returns the link that was passed. That is a runtime question with a cost trade-off (F_GETPATHis the fast path), so it is left for its own change.copyFileSyncon macOS already triesclonefile()first for a file over 128 KB (src/runtime/node/node_fs.rs:4877) and falls back tocopyfile()when the volume cannot clone, so the flag states the intent and the fallback still works across devices.no test proof · iteration 1 · platform-specific test(s) that do not run on this machine, deferring to CI, which covers all platforms: test/cli/bun.test.ts