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

Closed
RO
2mo ago
+34-21

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 #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 #28062 (fix in flight in #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 #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 #28062 / #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 #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 (#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.