Skip to content

test(install): give each long-label bun patch test its own install cache - #38255

Open
robobun wants to merge 2 commits into
mainfrom
farm/af65d987/bun-patch-long-label-cache-isolation
Open

test(install): give each long-label bun patch test its own install cache#38255
robobun wants to merge 2 commits into
mainfrom
farm/af65d987/bun-patch-long-label-cache-isolation

Conversation

@robobun

@robobun robobun commented Aug 13, 2026

Copy link
Copy Markdown
Collaborator

Problem

  • test/cli/install/bun-patch.test.ts fails on main on the Windows lanes (x64 and aarch64), in the packages whose label is longer than 1024 bytes block added by install: stop panicking on package labels longer than the stack buffers #37469: the bun install step exits 1, as expect(exitCode).toBe(0) at install() (builds 94812, 94844; about half of the runs, so it usually passes on a retry and fails outright on PRs whose retries all lose).
  • The child's stderr is one of error: failed to verify cache dir for "bar": ENOENT, ENOENT: failed opening cache/package/version dir for package bar or ENOENT: failed copying files from cache to destination for package bar.
  • Cause: the three tests are test.concurrent, and two of them install bar from the identical spec. They spawn with plain bunEnv, so in CI they inherit the BUN_INSTALL_CACHE_DIR the test runner exports (scripts/runner.node.mjs), which fetch_cache_directory_path (src/install/PackageManager/PackageManagerDirectories.rs:379) consults before the install.cache that createTestDir writes to bunfig.toml. So both installs extract and publish the same cache entry (@T@<hash of the spec>) into one shared cache at the same time.
  • On Windows the install that publishes second cannot rename over the existing entry, and the retry arm in move_to_cache_directory (src/install/extract_tarball.rs:576) moves the first install's entry out of the cache and deletes it before publishing its own, while the first install is still opening or copying from it. The first install exits 1 with one of the messages above. This is Windows: parallel bun install --no-cache with shared BUN_INSTALL_CACHE_DIR can fail with ENOENT opening cache/package/version dir #28062 (fix in flight in install: never delete a concurrently published cache entry on Windows #33884); the POSIX arm swaps atomically, which is why only the Windows lanes see this.

Fix

  • VerdaccioRegistry.createTestDir() now also returns env: bunEnv plus BUN_INSTALL_CACHE_DIR pointing at the same <project>/.bun-cache it already writes to bunfig.toml. The long-label tests run every command (install, patch, patch --commit) with their project's env, so the two installs no longer share a cache and have nothing to race on.
  • This is the right layer for this failure: the test is about formatting labels longer than the stack buffers and nothing about it needs two projects to share a cache. The bun commands and their arguments are unchanged, so the code paths install: stop panicking on package labels longer than the stack buffers #37469 covers are exercised exactly as before.
  • Returning the env from the harness rather than pinning it in this file: the harness already intends per-project caches (that is what it writes to bunfig.toml), and the same re-pin is currently copied by hand in bun-workspaces.test.ts, bun-install-lifecycle-scripts.test.ts, bun-install-registry.test.ts, bun-install-native-binlink.test.ts and isolated-install.test.ts; the other createTestDir callers are unaffected by the extra property (they destructure or read packageDir/packageJson) and can switch over when they go concurrent. Those existing copies are left alone here to keep this a small fix for the red lane.
  • The install's stderr is attached to the exit code assertion (expect(exitCode, stderr)), so a failing install reports why; in the CI failures above it did not, and the reason had to be dug out of the job log.
  • No change under src/; the Windows eviction itself is Windows: parallel bun install --no-cache with shared BUN_INSTALL_CACHE_DIR can fail with ENOENT opening cache/package/version dir #28062 / install: never delete a concurrently published cache entry on Windows #33884, after which the isolation here is still correct, just no longer load-bearing.
  • Verified on Windows Server 2019 x64 with the current canary, mirroring CI by exporting a fresh shared BUN_INSTALL_CACHE_DIR per run: unfixed test 5/10 runs fail, fixed test 0/20; the full file passes there. On Linux the full file passes with the debug build (34/34), hoist.test.ts and catalogs.test.ts (other createTestDir callers) pass, and the three tests still fail with panic: unreachable: Error on a canary from before install: stop panicking on package labels longer than the stack buffers #37469, so they still cover what they did. The first CI run of this PR had the file passing first try on both Windows lanes.

Background

  • BUN_INSTALL_CACHE_DIR is the env override for bun's package cache. It wins over install.cache in bunfig.toml, and the test runner exports one per test-file invocation, so a test that wants a cache of its own has to set the env var; setting it in bunfig is not enough in CI.
  • The cache entry for a tarball dependency is named after a hash of its spec, so two projects installing the same spec into one cache read and write the same entry.
  • bun install extracts a package into a temp dir and then renames that dir into the cache. On POSIX a collision with an entry another process published is resolved with an atomic swap, so the entry never disappears; the Windows arm instead renames the existing entry away and deletes it before retrying, which is the window the first process falls into (Windows: parallel bun install --no-cache with shared BUN_INSTALL_CACHE_DIR can fail with ENOENT opening cache/package/version dir #28062).
Probe: concurrent installs sharing a cache on Windows (no test involved)

N project dirs with {"dependencies":{"bar":"<long ./x/../... spec>/bar-0.0.2.tgz"}} and the tarball, bun install spawned in all of them at once, repeated. bun 1.4.0-canary.1+18059646e, Windows Server 2019 x64.

shared BUN_INSTALL_CACHE_DIR, 3 installs x 30 iterations:   9 of 90 installs exit 1
  ENOENT: failed copying files from cache to destination for package bar   (4)
  ENOENT: failed opening cache/package/version dir for package bar         (5)
  (always the process that finished extracting first)
shared BUN_INSTALL_CACHE_DIR, 2 installs x 10 iterations:   2 of 20 exit 1
one BUN_INSTALL_CACHE_DIR per project, 2 x 30 iterations:   0 of 60

The test itself under the same CI-like env (fresh shared BUN_INSTALL_CACHE_DIR per bun test run): unfixed 5/10 runs fail, always at install(); fixed 0/20.

Earlier revision

The first revision pinned BUN_INSTALL_CACHE_DIR inside bun-patch.test.ts only (the same local wrapper the files listed above have). Self-review pointed out that this would be the sixth copy of that wrapper and that the harness is where the intent already lives, so the pin moved into createTestDir; the test-side behavior is identical.


no test proof · iteration 0 · Platform-specific test-only change; deferring to CI.

@coderabbitai

coderabbitai Bot commented Aug 13, 2026

Copy link
Copy Markdown
Contributor

Warning

Review limit reached

@robobun, you've reached your PR review limit, so we couldn't start this review.

Next review available in: 18 minutes

Limit details: You’ve used all 5 included reviews currently available under your plan.

Enable usage-based reviews in Billing to review now. Otherwise, wait until the next included review is available.
You're only billed for reviews past your plan's rate limits ($0.25/file).

How can I continue?

After more reviews become available, a review can be triggered using the @coderabbitai review command as a PR comment. Alternatively, push new commits to this PR.

To avoid repeated limits, reduce automatic review volume by pausing incremental auto-reviews earlier, using label-based review opt-in, excluding WIP or generated PR titles, or requesting reviews manually when the PR is ready. If your team needs uninterrupted high-volume reviews, an organization admin can enable usage-based reviews.

How do review limits work?

CodeRabbit enforces per-developer PR review limits for each organization. Most developers receive the normal plan review availability.

For paid Pro and Pro+ PR reviews, CodeRabbit uses adaptive limits for sustained high-volume activity. When a developer's recent PR review activity reaches the 95th percentile or higher among CodeRabbit users, additional reviews become available more gradually as earlier reviews age out of the rolling window.

Please refer docs for additional details.

Review details
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: ASSERTIVE

Plan: Pro

Run ID: 0408779c-1e73-498c-9f27-a0ab659956e5

📥 Commits

Reviewing files that changed from the base of the PR and between 8326d1b and debec67.

📒 Files selected for processing (2)
  • test/cli/install/bun-patch.test.ts
  • test/harness.ts

Comment @coderabbitai help to get the list of available commands.

@robobun

robobun commented Aug 13, 2026

Copy link
Copy Markdown
Collaborator Author

Status: fixed, CI green on the rebased head (debec67), ready for a maintainer.

@claude claude Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM — test-only de-flake that isolates concurrent installs into per-project caches.

What was reviewed:

  • Confirmed { ...bunEnv, BUN_INSTALL_CACHE_DIR: join(packageDir, ".bun-cache") } matches the pattern in bun-install-lifecycle-scripts.test.ts, bun-install-native-binlink.test.ts, symlink-path-traversal.test.ts.
  • Confirmed scripts/runner.node.mjs:1791 exports BUN_INSTALL_CACHE_DIR per test invocation, so the env override is required (bunfig alone is insufficient in CI).
  • Verified expect(value, message) is an established pattern in the suite; assertions and command args are otherwise unchanged, so #37469's coverage is preserved.
Extended reasoning...

Overview

This PR touches a single test file, test/cli/install/bun-patch.test.ts, refactoring the three test.concurrent cases in the "packages whose label is longer than 1024 bytes" describe block. createProject now returns { packageDir, env } where env is { ...bunEnv, BUN_INSTALL_CACHE_DIR: join(packageDir, ".bun-cache") }, and runBun/install thread that env through to every spawned bun process. The install helper also attaches stderr as the assertion message on the exit-code check. No src/ changes.

Security risks

None. Test-only change; the only new value is a temp-dir path passed as an env var to a child process.

Level of scrutiny

Low. This is a test isolation/de-flake fix with a thoroughly documented root cause (CI runner exports a shared BUN_INSTALL_CACHE_DIR, two concurrent tests install bar from an identical spec, Windows move_to_cache_directory retry arm evicts the other process's entry). I verified the runner does export that var (scripts/runner.node.mjs:1791) and that the per-project-cache pattern is already the convention across test/cli/install/ (lifecycle-scripts, native-binlink, symlink-path-traversal, git-deps, etc.).

Other factors

  • The refactor is mechanical: same bun commands, same arguments, same assertions — only the env gains one key. The packageDir is still destructured where filesystem paths are needed, so Bun.write/Bun.file calls are unchanged.
  • expect(exitCode, stderr).toBe(0) uses the two-arg custom-message form already used elsewhere in the suite (e.g. test/package-json-lint.test.ts, test/js/bun/jsonc/jsonc.test.ts).
  • The PR description explicitly notes the underlying Windows cache-eviction race is a real bun install bug being tracked separately, and confirms the tests still fail on a pre-#37469 canary — so this is not weakening coverage to route around a runtime bug.
  • REVIEW.md's de-flake guidance ("keep asserting the property the original assertion protected") is satisfied: no assertions were removed or loosened.

@robobun

robobun commented Aug 14, 2026

Copy link
Copy Markdown
Collaborator Author
Updated 12:59 PM PT - Aug 16th, 2026

@robobun, your commit debec678c3437f92fa1de6f7a77dd9b1f8cb3659 passed in Build #99466! 🎉


🧪   To try this PR locally:

bunx bun-pr 38255

That installs a local version of the PR into your bun-38255 executable, so you can run:

bun-38255 --bun

@claude claude Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM — test-only de-flake that gives each concurrent long-label test its own BUN_INSTALL_CACHE_DIR, matching the pattern used across the install test suite.

What was reviewed

  • Confirmed the { ...bunEnv, BUN_INSTALL_CACHE_DIR: join(packageDir, ".bun-cache") } shape matches the existing pattern in bun-install-lifecycle-scripts.test.ts, bun-install-native-binlink.test.ts, and others.
  • Verified all three tests thread the per-project env through every spawn (install, patch, patch --commit) — none still uses bare bunEnv.
  • Checked no assertions were weakened or removed; the bun commands and their arguments are unchanged, so the coverage from #37469 is preserved.
Extended reasoning...

Overview

This PR touches a single test file, test/cli/install/bun-patch.test.ts, and only the packages whose label is longer than 1024 bytes describe block within it. It refactors the block's local createProject helper to return { packageDir, env } (where env spreads bunEnv and adds a per-project BUN_INSTALL_CACHE_DIR), refactors runBun to accept that object, and updates the three test.concurrent bodies to pass it. It also attaches stderr as the assertion message on the install exit-code check so future failures self-report. No src/ code is touched.

Security risks

None. This is test-only infrastructure that changes which temp directory child bun install processes use as their package cache. No auth, crypto, network, or user-facing code paths are involved.

Level of scrutiny

Low. This is a targeted de-flake of a known Windows CI failure with a well-understood root cause (two concurrent installs of the identical tarball spec racing on a shared BUN_INSTALL_CACHE_DIR inherited from the CI runner). The fix — pinning a per-project cache via the env var — is the established idiom in this test suite (grep shows the same join(packageDir, ".bun-cache") pattern in bun-install-lifecycle-scripts.test.ts, bun-install-native-binlink.test.ts, 36577.test.ts, and many others). The change is mechanical and self-contained within one describe block.

Other factors

Per REVIEW.md's de-flaking guidance ("keep asserting the property the original assertion protected"), I checked that no assertions were removed or loosened: every expect from the original tests is still present, and the bun invocations (install, patch <name>@<label>, patch <path>, patch --commit) run with identical arguments — only the cache directory differs, which is orthogonal to the long-label formatting these tests cover. The PR description confirms the tests still fail on a pre-#37469 canary, so coverage is intact. The added expect(exitCode, stderr).toBe(0) improves diagnosability without changing pass/fail semantics. The underlying Windows cache-eviction race is explicitly noted as tracked separately, which is the right layering — this PR fixes the test isolation, not the runtime bug.

@claude claude Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM — test-only de-flake that gives each concurrent long-label bun patch test its own BUN_INSTALL_CACHE_DIR.

What was reviewed:

  • The createTestDir change in test/harness.ts is additive (packageDir/packageJson unchanged; new env field), so the ~200 other callers that destructure only those fields are unaffected.
  • The three tests still run the same bun install/patch/patch --commit invocations with the same arguments; only cwd/env plumbing changed, so the #37469 coverage is preserved.
  • Checked that BUN_INSTALL_CACHE_DIR points at the same .bun-cache dir writeBunfig already sets in install.cache, so this just makes the intended per-project isolation actually win over the CI-exported env var.
Extended reasoning...

Overview

This PR de-flakes the packages whose label is longer than 1024 bytes block in test/cli/install/bun-patch.test.ts, which fails ~50% of the time on Windows CI because two test.concurrent tests install the same tarball spec into a shared cache and race on the Windows rename-and-delete publish path. The fix threads a per-project env (with BUN_INSTALL_CACHE_DIR=<packageDir>/.bun-cache) from VerdaccioRegistry.createTestDir through the local runBun helper so each test uses its own cache. It also attaches stderr to the exit-code assertion for better diagnostics.

Security risks

None. Test-only change; no runtime code under src/ is touched. The new env var points into an ephemeral temp dir the test already creates.

Level of scrutiny

Low. This is a mechanical refactor of test plumbing: createProject now returns the whole createTestDir result instead of just packageDir, and runBun destructures {packageDir, env} from it. The harness change adds one field to a returned object; I grep-verified other createTestDir callers only destructure packageDir/packageJson, so they are unaffected. The cache dir chosen matches what writeBunfig already writes to bunfig.toml, so the only behavioral difference is that the env var now overrides the CI runner's export — exactly the intended isolation.

Other factors

  • The PR description documents a clean before/after reproduction on Windows (5/10 → 0/20) and confirms the tests still fail on a pre-#37469 canary, so coverage is preserved rather than weakened.
  • No CODEOWNERS entries match these paths, no outstanding human review comments, and no prior claude[bot] review on this PR.
  • The underlying Windows cache-eviction bug is explicitly noted as tracked separately; this PR correctly does not paper over it in src/.

@robobun

robobun commented Aug 14, 2026

Copy link
Copy Markdown
Collaborator Author

Some extra data points for this flake, collected while triaging it independently (same diagnosis as the PR body, so nothing here changes the fix):

  • The block has kept failing on main since this was opened: builds 95598, 95706, 95850 and 96102 (Windows 2019 x64 and Windows 11 aarch64, passing on retry each time), plus PR build 95638. Every failure is in the two tests that install bar from the identical spec (lines 120 and 137); the baz test has never failed, which matches the shared @T@<hash> entry being the thing raced on.
  • A third stderr shape, from build 95638, in addition to the ENOENT ones listed above: error: failed to verify cache dir is one step earlier, and error: failed to resolve cache dir for "bar": EBADF is one step later. That one is the same eviction landing after the winner has already opened the new entry: get_fd_path on the now deleted directory fails, and on Windows it reports every GetFinalPathNameByHandle failure as EBADF. So the EBADF is a label, not a closed handle.
  • Probe on Windows Server 2019 x64 with canary eabb96de7, N projects installing the same tarball spec at once into the shared BUN_INSTALL_CACHE_DIR the bootstrap exports: 3 at a time fails 6 of 30 installs with the long spec and 15 of 60 with a plain ./bar-0.0.2.tgz spec, so the label length is irrelevant; 1 at a time is 0 of 40; a private BUN_TMPDIR/TEMP per project still fails (8 of 60), so only the cache needs isolating, which is what this PR does.

The "packages whose label is longer than 1024 bytes" tests run
concurrently and two of them install `bar` from the identical spec. They
relied on the cache dir createTestDir writes to bunfig.toml to keep the
projects apart, but CI exports BUN_INSTALL_CACHE_DIR, which takes
precedence, so both installs published the same cache entry into one
shared cache at the same time. On Windows the install that publishes
second evicts the first one's entry while it is still being read, and
that install exits 1, which failed the test on both Windows lanes.

Pin BUN_INSTALL_CACHE_DIR per project, as the other install tests do, and
attach the install's stderr to the exit code assertion so a failing
install says why.
The cache that createTestDir writes to bunfig.toml loses to the
BUN_INSTALL_CACHE_DIR the CI runner exports, and several install test
files re-pin the env var per project by hand to get the isolation back.
Return that env from createTestDir instead and have the long-label
bun patch tests use it, rather than adding another local copy.
@robobun
robobun force-pushed the farm/af65d987/bun-patch-long-label-cache-isolation branch from 7b31af1 to debec67 Compare August 16, 2026 19:28

@claude claude Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM — test-only de-flake giving each concurrent long-label bun patch test its own BUN_INSTALL_CACHE_DIR.

What was reviewed:

  • createTestDir() return-shape change is additive; spot-checked callers (frozen-lockfile-pruned, nested-overrides, pnpm-migration, hoist) all destructure packageDir/packageJson and are unaffected.
  • .bun-cache path in the new env matches what writeBunfig already writes to install.cache, so bunfig and env agree.
  • The three tests' commands and assertions are unchanged; packageDir is still available where needed for filesystem writes.
Extended reasoning...

Overview

This PR fixes a Windows-only flake in the packages whose label is longer than 1024 bytes block of test/cli/install/bun-patch.test.ts. Two of the three test.concurrent cases install bar from an identical tarball spec; in CI they inherit the file-wide BUN_INSTALL_CACHE_DIR the runner exports, which overrides the per-project install.cache in bunfig, so both installs race on the same cache entry and one hits the Windows-only eviction bug (#28062). The fix has VerdaccioRegistry.createTestDir() also return an env (bunEnv + BUN_INSTALL_CACHE_DIR pointing at the same <project>/.bun-cache it already writes to bunfig), and the three tests spawn every bun command with that env instead of plain bunEnv. The test file is refactored to pass the whole Project object through runBun/install instead of just packageDir, and install() now attaches stderr to the exit-code assertion.

Security risks

None. Test-only change; no src/ edits, no new inputs, no external network.

Level of scrutiny

Low. This is a self-contained test-harness/de-flake change. The harness edit is purely additive (a new property on the returned object); I grep-checked createTestDir callers across test/ and every one either destructures packageDir/packageJson or holds the whole object without enumerating its keys, so nothing can be broken by the extra property. The test-side refactor is mechanical — the bun invocations, arguments, and assertions are byte-identical to before, only the env passed to Bun.spawn changes.

Other factors

  • The PR body documents a clear root-cause chain (env > bunfig precedence in fetch_cache_directory_path, Windows non-atomic cache publish in move_to_cache_directory), reproduces it standalone on Windows (5/10 fail → 0/20), and confirms the tests still fail on a pre-#37469 canary so their coverage is preserved.
  • Placing the pin in createTestDir rather than copying the same wrapper a sixth time matches REVIEW.md's "one implementation, in the right place" guidance; existing per-file copies are intentionally left alone to keep the change scoped to fixing the red lane.
  • No prior claude reviews on this PR and no outstanding human reviewer comments.

robobun added a commit that referenced this pull request Aug 22, 2026
…y.createTestDir

createTestDir now returns the env that pins BUN_INSTALL_CACHE_DIR to the
project's own .bun-cache (the hunk #38255 proposes), so the file-local
wrapper goes. install() takes (env, cwd) like runBunInstall.

The ranged peer case pins no-deps@1.1.0 through the nd11 alias instead of
reaching it through two-range-deps' range, as #38858 does.
@robobun

robobun commented Aug 22, 2026

Copy link
Copy Markdown
Collaborator Author

Heads-up on an overlap: #40087 rewrites test/cli/install/bun-patch.test.ts to run against the local registry, and since 85fce1d the three long-label tests there spawn bun with a per-project BUN_INSTALL_CACHE_DIR (the file's projectEnv helper). That covers the bun-patch.test.ts half of this PR, so the two will conflict in that describe. The createTestDir().env addition in test/harness.ts is independent of #40087 and still applies.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant