Skip to content

Conversation

robobun
Copy link
Collaborator

@robobun robobun commented Oct 13, 2025

What does this PR do?

Fixes a race condition in git repository cloning during bun install by cloning to a temporary directory first, then atomically moving to the cache directory. This prevents cache corruption when multiple bun install processes run concurrently and try to clone the same git dependency.

Problem

Previously, git repositories were cloned directly to the cache directory:

git clone <url> /cache/<final-name>

This caused race conditions when multiple bun install processes ran concurrently and tried to clone the same git dependency, potentially corrupting the cache.

Solution

Changed both download() and checkout() functions to:

  1. Clone to temporary directory first using FileSystem.tmpname()
  2. Atomically move to cache using renameatConcurrently() with fallback
  3. Handle race conditions when destination already exists
  4. Best-effort cleanup of temp directories on failure via errdefer

This follows the same pattern used in extract_tarball.zig for handling npm tarballs.

Changes

src/install/repository.zig

  • Added temp_dir parameter to download() and checkout()
  • Added temp_path_buf threadlocal buffer
  • Clone operations now target temp directory first
  • Use renameatConcurrently() for atomic move to cache
  • Added errdefer cleanup and race condition handling
  • Log absolute paths for better debugging

src/install/PackageManagerTask.zig

  • Updated all call sites to pass manager.getTemporaryDirectory().handle

How did you verify your code works?

  • ✅ Cross-platform compilation verified (bun run zig:check-all) - all 13 platforms compile successfully
  • ✅ Single git dependency installation works
  • ✅ Multiple git dependencies work
  • Concurrent installs tested - 4 parallel bun install processes succeed without conflicts
  • ✅ Existing git dependency tests pass (should edit package json correctly with git dependencies)
  • ✅ Verified race condition handling with concurrent installs

Example

# Before: Race condition could corrupt cache
bun install & bun install & bun install &

# After: Atomic operations with race handling prevent corruption
bun install & bun install & bun install & # All succeed ✅

🤖 Generated with Claude Code

Co-Authored-By: Claude [email protected]

Changes git clone operations during bun install to:
1. Clone to temporary directory first
2. Use renameatConcurrently to atomically move to cache

This prevents race conditions when multiple bun install processes
run concurrently and clone the same git dependency.

Both download() and checkout() functions now:
- Accept a temp_dir parameter
- Use FileSystem.tmpname() for temporary directory names
- Clone to temp location first
- Atomically move to final cache location using renameatConcurrently

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <[email protected]>
@robobun
Copy link
Collaborator Author

robobun commented Oct 13, 2025

Updated 11:56 PM PT - Oct 20th, 2025

@dylan-conway, your commit 009adfd has 1 failures in Build #29808 (All Failures):


🧪   To try this PR locally:

bunx bun-pr 23601

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

bun-23601 --bun

@coderabbitai
Copy link
Contributor

coderabbitai bot commented Oct 13, 2025

Walkthrough

Passes a temporary-directory handle from PackageManagerTask into Repository download/checkout flows. Repository now clones/checks out into a temp dir, then atomically renames the result into the cache and performs cleanup. Repository struct and download/checkout signatures were updated to accept temp_dir: std.fs.Dir. Test ban-limits updated.

Changes

Cohort / File(s) Summary of Changes
Temp-dir propagation in task
src/install/PackageManagerTask.zig
Passes manager.getTemporaryDirectory().handle into three Repository invocations (git clone HTTPS, git clone SSH, git checkout). No public API changes in this file.
Atomic temp-dir workflow & API changes
src/install/repository.zig
Adds temporary-path buffer and workflow: create a generated temp target under provided temp_dir, clone/checkout into that temp path, atomically renameatConcurrently (with fallback) into cache, and perform cleanup on failure/success. Adds verbose timing/logging. Updates pub const Repository to include temp_dir: std.fs.Dir and changes download(...) and checkout(...) signatures to accept temp_dir: std.fs.Dir.
Test metadata bump
test/internal/ban-limits.json
Increments ban-limits numeric threshold for std.fs.Dir from 165 → 167; no behavioral changes.

Possibly related PRs

Suggested reviewers

  • taylordotfish

Pre-merge checks

✅ Passed checks (2 passed)
Check name Status Explanation
Title Check ✅ Passed The PR title "Fix git repository cloning to use temp directory first" directly captures the primary change across the modified files. The raw summary confirms the core modifications involve adding a temp_dir parameter and implementing a temp-first clone workflow with atomic moves to cache, exactly as the title describes. The title is concise, clear, and specific enough for a teammate to understand the main objective without being overly broad or vague.
Description Check ✅ Passed The PR description comprehensively addresses both required template sections. The "What does this PR do?" section provides a clear summary of the race condition fix, includes a detailed problem-solution explanation, and outlines specific changes to each file. The "How did you verify your code works?" section documents extensive verification including cross-platform compilation, concurrent install testing, and existing test validation. The description exceeds the template requirements with well-structured technical details and examples.

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

Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 3

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
src/install/repository.zig (1)

616-663: Add cleanup guard for checkout temp dir; avoid orphaning on failures

If clone/checkout/tagging fails, the temp directory is left behind. Add errdefer and set moved=true after successful rename; on rename “already exists,” delete temp explicitly.

             // Clone to temp directory first
             var tmpname_buf: bun.PathBuffer = undefined;
             const tmpname = try FileSystem.tmpname(folder_name[0..@min(folder_name.len, 32)], &tmpname_buf, bun.fastRandom());
             const temp_target = try bun.getFdPath(.fromStdDir(temp_dir), &temp_path_buf);
             const temp_target_full = Path.joinAbsString(temp_target, &.{tmpname}, .auto);
+            var moved = false;
+            errdefer if (!moved) temp_dir.deleteTree(tmpname) catch {};
@@
             _ = exec(allocator, env, &[_]string{ "git", "-C", temp_target_full, "checkout", "--quiet", resolved }) catch |err| {
                 log.addErrorFmt(
                     null,
                     logger.Loc.Empty,
                     allocator,
                     "\"git checkout\" for \"{s}\" failed",
                     .{name},
                 ) catch unreachable;
                 return err;
             };
@@
             var temp_pkg_dir = try bun.openDir(temp_dir, tmpname);
             temp_pkg_dir.deleteTree(".git") catch {};
@@
             temp_pkg_dir.close();
📜 Review details

Configuration used: CodeRabbit UI

Review profile: ASSERTIVE

Plan: Pro

Disabled knowledge base sources:

  • Linear integration is disabled by default for public repositories

You can enable these sources in your CodeRabbit configuration.

📥 Commits

Reviewing files that changed from the base of the PR and between c820c2b and c61f112.

📒 Files selected for processing (2)
  • src/install/PackageManagerTask.zig (3 hunks)
  • src/install/repository.zig (10 hunks)
🧰 Additional context used
📓 Path-based instructions (2)
**/*.zig

📄 CodeRabbit inference engine (.cursor/rules/javascriptcore-class.mdc)

**/*.zig: Declare the extern C symbol in Zig and export a Zig-friendly alias for use
Wrap the Bun____toJS extern in a Zig method that takes a JSGlobalObject and returns JSC.JSValue

Files:

  • src/install/PackageManagerTask.zig
  • src/install/repository.zig
src/**/*.zig

📄 CodeRabbit inference engine (CLAUDE.md)

In Zig code, manage memory carefully and use defer for cleanup of allocations/resources

src/**/*.zig: Use the # prefix to declare private fields in Zig structs (e.g., struct { #foo: u32 })
Prefer decl literals when initializing values in Zig (e.g., const decl: Decl = .{ .binding = 0, .value = 0 })
Place @import directives at the bottom of Zig files
Use @import("bun") instead of @import("root").bun

When adding debug logs in Zig, create a scoped logger and log via Bun APIs: const log = bun.Output.scoped(.${SCOPE}, .hidden); then log("...", .{})

Files:

  • src/install/PackageManagerTask.zig
  • src/install/repository.zig
🔇 Additional comments (3)
src/install/repository.zig (2)

5-5: LGTM: threadlocal temp path buffer

Consistent with other per-thread buffers; no concerns.


485-485: API: new temp_dir parameter — verify lifetime and cross-device behavior

Passing a std.fs.Dir for temp is right. Ensure manager.getTemporaryDirectory().handle remains valid during the whole clone/checkout and that rename across volumes is handled (you used move_fallback=true, which is good).

Also applies to: 605-605

src/install/PackageManagerTask.zig (1)

165-170: Correctly wires temp_dir into git clone/checkout flows

Parameter threading looks right and matches repository.zig changes.

Please confirm:

  • manager.getTemporaryDirectory().handle is a std.fs.Dir (or compatible) and remains open for the duration of the callback.
  • It points to a location on the same filesystem as the cache when possible; when not, move_fallback handles cross‑device moves.

Also applies to: 193-198, 213-223

Claude Bot and others added 2 commits October 13, 2025 16:52
- Add errdefer cleanup for temp directories on failure
- Handle race condition when destination already exists
- Set moved flag to prevent cleanup after successful rename
- Log absolute paths instead of relative in error messages
- Best-effort cleanup when another process wins the race

Co-Authored-By: CodeRabbit AI <[email protected]>

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <[email protected]>
Adds --verbose logging to track git clone and checkout operations:
- Log clone start with temp directory name
- Log checkout operations with resolved commit
- Log successful completion with timing
- Log when another process wins the race

Follows same pattern as extract_tarball.zig verbose logs.

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <[email protected]>
Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 3

📜 Review details

Configuration used: CodeRabbit UI

Review profile: ASSERTIVE

Plan: Pro

Disabled knowledge base sources:

  • Linear integration is disabled by default for public repositories

You can enable these sources in your CodeRabbit configuration.

📥 Commits

Reviewing files that changed from the base of the PR and between 63db502 and f0b7042.

📒 Files selected for processing (1)
  • src/install/repository.zig (11 hunks)
🧰 Additional context used
📓 Path-based instructions (2)
**/*.zig

📄 CodeRabbit inference engine (.cursor/rules/javascriptcore-class.mdc)

**/*.zig: Declare the extern C symbol in Zig and export a Zig-friendly alias for use
Wrap the Bun____toJS extern in a Zig method that takes a JSGlobalObject and returns JSC.JSValue

Files:

  • src/install/repository.zig
src/**/*.zig

📄 CodeRabbit inference engine (CLAUDE.md)

In Zig code, manage memory carefully and use defer for cleanup of allocations/resources

src/**/*.zig: Use the # prefix to declare private fields in Zig structs (e.g., struct { #foo: u32 })
Prefer decl literals when initializing values in Zig (e.g., const decl: Decl = .{ .binding = 0, .value = 0 })
Place @import directives at the bottom of Zig files
Use @import("bun") instead of @import("root").bun

When adding debug logs in Zig, create a scoped logger and log via Bun APIs: const log = bun.Output.scoped(.${SCOPE}, .hidden); then log("...", .{})

Files:

  • src/install/repository.zig
🔇 Additional comments (10)
src/install/repository.zig (10)

5-5: Thread-local temp path buffer: good addition

Appropriate for per-thread path assembly; avoids allocs and races.


484-485: API surface: temp_dir parameter

Signature change is clear and localized; aligns with temp-first workflow.


516-525: Temp-first clone setup and errdefer cleanup: solid

Good use of tmpname, absolute temp path, and errdefer to prevent leaks.


538-539: Passing absolute temp target to git clone: LGTM

Ensures clone creates the expected temp directory.


581-586: Verbose elapsed timing: LGTM

Accurate gating on verbose; duration calc is correct.

Also applies to: 733-737


630-631: API surface: temp_dir parameter (checkout)

Matches download(); consistent propagation.


676-679: Checkout progress log: LGTM

Clear and helpful when verbose is enabled.


681-691: Checkout command against temp clone: LGTM

Correct use of -C with absolute temp_target_full.


739-740: Return opened cache dir: LGTM

Happy-path is clear and resource-safe with defer above.


802-803: Output alias import: LGTM

Used by new debug logs; imports remain at bottom.

Comment on lines +692 to +703
var temp_pkg_dir = try bun.openDir(temp_dir, tmpname);
temp_pkg_dir.deleteTree(".git") catch {};

if (resolved.len > 0) insert_tag: {
const git_tag = dir.createFileZ(".bun-tag", .{ .truncate = true }) catch break :insert_tag;
const git_tag = temp_pkg_dir.createFileZ(".bun-tag", .{ .truncate = true }) catch break :insert_tag;
defer git_tag.close();
git_tag.writeAll(resolved) catch {
dir.deleteFileZ(".bun-tag") catch {};
temp_pkg_dir.deleteFileZ(".bun-tag") catch {};
};
}
temp_pkg_dir.close();

Copy link
Contributor

Choose a reason for hiding this comment

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

🧹 Nitpick | 🔵 Trivial

Remove .git and tag the worktree: LGTM (optional verbose on failure)

Silently ignoring .git removal is acceptable; consider emitting a verbose-only note if removal fails.

- Show absolute temp path (temp_target_full) in verbose logs instead of tmpname
- Set moved=true on EEXIST path to cancel errdefer cleanup
- Log absolute destination path in error messages for better diagnostics

Co-Authored-By: CodeRabbit AI <[email protected]>

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <[email protected]>
Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 1

♻️ Duplicate comments (2)
src/install/repository.zig (2)

695-706: Optional: verbose note on .git removal failure (already noted previously)

Silently ignoring .git removal is fine. Consider a verbose-only note if it fails, for diagnostics.


526-529: Prefer a scoped logger for new debug logs

Per guidelines, use a scoped logger instead of Output.prettyErrorln/flush for new debug logs. Example:

-                Output.prettyErrorln("[{s}] Cloning git repository (bare) to {s}<r>", .{ name, temp_target_full });
-                Output.flush();
+                const dbg = Output.scoped(.install_repository, .hidden);
+                dbg("[{s}] Cloning git repository (bare) to {s}<r>", .{ name, temp_target_full });

As per coding guidelines

Also applies to: 584-588, 655-658, 679-682, 739-743

📜 Review details

Configuration used: CodeRabbit UI

Review profile: ASSERTIVE

Plan: Pro

Disabled knowledge base sources:

  • Linear integration is disabled by default for public repositories

You can enable these sources in your CodeRabbit configuration.

📥 Commits

Reviewing files that changed from the base of the PR and between f0b7042 and bc6cdda.

📒 Files selected for processing (1)
  • src/install/repository.zig (11 hunks)
🧰 Additional context used
📓 Path-based instructions (2)
**/*.zig

📄 CodeRabbit inference engine (.cursor/rules/javascriptcore-class.mdc)

**/*.zig: Declare the extern C symbol in Zig and export a Zig-friendly alias for use
Wrap the Bun____toJS extern in a Zig method that takes a JSGlobalObject and returns JSC.JSValue

Files:

  • src/install/repository.zig
src/**/*.zig

📄 CodeRabbit inference engine (CLAUDE.md)

In Zig code, manage memory carefully and use defer for cleanup of allocations/resources

src/**/*.zig: Use the # prefix to declare private fields in Zig structs (e.g., struct { #foo: u32 })
Prefer decl literals when initializing values in Zig (e.g., const decl: Decl = .{ .binding = 0, .value = 0 })
Place @import directives at the bottom of Zig files
Use @import("bun") instead of @import("root").bun

When adding debug logs in Zig, create a scoped logger and log via Bun APIs: const log = bun.Output.scoped(.${SCOPE}, .hidden); then log("...", .{})

Files:

  • src/install/repository.zig
🔇 Additional comments (4)
src/install/repository.zig (4)

5-5: LGTM: thread-local temp path buffer

Thread-local buffer addition is consistent with existing pattern.


516-525: Good: best-effort cleanup with errdefer

Guarded temp-dir cleanup with moved flag prevents leaks on failure paths. Nice.


552-583: Atomic rename + EEXIST handling: solid

Temp-first clone, atomic rename with move_fallback, and treating destination-exists as success with cleanup looks correct and concurrency-safe. Absolute path logging is helpful.

Also applies to: 707-737


809-809: Minor: import alias addition

OK. If adopting scoped logger, keep Output alias and add a file- or function-scoped logger constant.

⛔ Skipped due to learnings
Learnt from: Jarred-Sumner
PR: oven-sh/bun#23373
File: src/bun.js/api/BunObject.zig:2514-2521
Timestamp: 2025-10-08T13:56:00.875Z
Learning: For Bun codebase: prefer using `bun.path` utilities (e.g., `bun.path.joinAbsStringBuf`, `bun.path.join`) over `std.fs.path` functions for path operations.

Comment on lines +484 to 485
temp_dir: std.fs.Dir,
task_id: Install.Task.Id,
Copy link
Contributor

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🔴 Critical

🧩 Analysis chain

Signature change: ensure all call sites pass temp_dir

Both download() and checkout() now require temp_dir. Compile passed, but please double-check all call sites.

Run:

Also applies to: 633-634


🏁 Script executed:

#!/usr/bin/env bash
set -euo pipefail

echo "Repository.download call sites:"
rg -nP --type=zig -C2 '\bRepository\.download\s*\('

echo
echo "Repository.checkout call sites:"
rg -nP --type=zig -C2 '\bRepository\.checkout\s*\('

echo
echo "Potential method-style calls (may include noise):"
rg -nP --type=zig -C2 '\.\s*(download|checkout)\s*\('

Length of output: 5006


🏁 Script executed:

#!/usr/bin/env bash
set -euo pipefail

echo "Full Repository.download invocations in PackageManagerTask.zig:"
rg -nP --type=zig -C5 'Repository\.download\(' src/install/PackageManagerTask.zig

echo
echo "Full Repository.checkout invocations in PackageManagerTask.zig:"
rg -nP --type=zig -C5 'Repository\.checkout\(' src/install/PackageManagerTask.zig

echo
echo "Signature of download in repository.zig:"
rg -nP --type=zig -C3 'pub fn download' src/install/repository.zig

echo
echo "Signature of checkout in repository.zig:"
rg -nP --type=zig -C3 'pub fn checkout' src/install/repository.zig

Length of output: 2133


Add missing task_id argument to Repository.download and checkout calls
The invocations in src/install/PackageManagerTask.zig (lines 160, 188, 213) pass allocator, env, log, cacheDir, and tempDir but omit the new task_id parameter. Append the appropriate Install.Task.Id to each call.

🤖 Prompt for AI Agents
In src/install/repository.zig around lines 484-485, the Repository API now
requires a task_id parameter; update the three calls in
src/install/PackageManagerTask.zig (around lines 160, 188, 213) that currently
pass allocator, env, log, cacheDir, tempDir to also pass the current
Install.Task.Id (e.g., task.id) as the final argument so each
Repository.download and Repository.checkout invocation matches the new
signature.

Increased limit from 165 to 167 to account for new temp_dir parameters
in Repository.download() and Repository.checkout() functions.

These follow the same pattern as extract_tarball.zig which also uses
std.fs.Dir for cache_dir and temp_dir parameters.

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <[email protected]>
Comment on lines +563 to +564
temp_dir.deleteTree(tmpname) catch {};
moved = true; // cancel errdefer; already cleaned up
Copy link
Member

Choose a reason for hiding this comment

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

Why can't we just let the errdefer delete the temp dir? The only difference would be that it runs after printing the verbose install message rather than before, but I don't think that's a big deal.

Then we can get rid of the moved variable.

Copy link
Member

@taylordotfish taylordotfish left a comment

Choose a reason for hiding this comment

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

There is an extreme level of code duplication between the download and checkout functions. We should not merge code like this.

Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 1

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (5)
src/install/repository.zig (5)

534-542: Memory leak: captured stdout from exec() is discarded

exec() returns an allocated stdout slice; assigning to _ leaks it. Capture and free.

-            _ = exec(allocator, env, &[_]string{
+            const _git_out = exec(allocator, env, &[_]string{
                 "git",
                 "clone",
                 "-c core.longpaths=true",
                 "--quiet",
                 "--bare",
                 url,
                 temp_target_full,
             }) catch |err| {
                 if (err == error.RepositoryNotFound or attempt > 1) {
                     ...
                 }
                 return err;
             };
+            defer allocator.free(_git_out);

Also apply this pattern to similar exec() calls below.


666-674: Memory leak: captured stdout from exec() is discarded (clone --no-checkout)

Same leak as above; free the returned buffer.

-            _ = exec(allocator, env, &[_]string{
+            const _git_out = exec(allocator, env, &[_]string{
                 "git",
                 "clone",
                 "-c core.longpaths=true",
                 "--quiet",
                 "--no-checkout",
                 try bun.getFdPath(.fromStdDir(repo_dir), &final_path_buf),
                 temp_target_full,
             }) catch |err| {
                 ...
             };
+            defer allocator.free(_git_out);

690-700: Memory leak: captured stdout from exec() is discarded (checkout)

Free stdout returned by exec().

-            _ = exec(allocator, env, &[_]string{ "git", "-C", temp_target_full, "checkout", "--quiet", resolved }) catch |err| {
+            const _git_out = exec(allocator, env, &[_]string{ "git", "-C", temp_target_full, "checkout", "--quiet", resolved }) catch |err| {
                 log.addErrorFmt(
                     ...
                 ) catch unreachable;
                 return err;
             };
+            defer allocator.free(_git_out);

497-511: Also free stdout in the fetch fast-path

fetch output is ignored; free it to avoid leaks during repeated installs.

-            _ = exec(
+            const _fetch_out = exec(
                 allocator,
                 env,
                 &[_]string{ "git", "-C", path, "fetch", "--quiet" },
             ) catch |err| {
                 ...
             };
+            defer allocator.free(_fetch_out);

362-401: Optional: harden exec() to reduce accidental leaks

Even with call-site fixes, exec() can leak stderr on success. Free stderr always; free stdout on error.

     const result = if (comptime Environment.isWindows)
         try std.process.Child.run(.{
             .allocator = allocator,
             .argv = argv,
             .env_map = std_map.get(),
         })
     else
         try std.process.Child.run(.{
             .allocator = allocator,
             .argv = argv,
             .env_map = std_map.get(),
         });
+    // Always free stderr; free stdout on error paths.
+    defer allocator.free(result.stderr);
+    errdefer allocator.free(result.stdout);

     switch (result.term) {
         .Exited => |sig| if (sig == 0) return result.stdout else if (
             ...
         ) {
             return error.RepositoryNotFound;
         },
         else => {},
     }

     return error.InstallFailed;

This minimizes leaks even if a caller forgets to free the returned stdout.

📜 Review details

Configuration used: CodeRabbit UI

Review profile: ASSERTIVE

Plan: Pro

Disabled knowledge base sources:

  • Linear integration is disabled by default for public repositories

You can enable these sources in your CodeRabbit configuration.

📥 Commits

Reviewing files that changed from the base of the PR and between 8ce582d and 009adfd.

📒 Files selected for processing (1)
  • src/install/repository.zig (10 hunks)
🧰 Additional context used
📓 Path-based instructions (2)
**/*.zig

📄 CodeRabbit inference engine (.cursor/rules/javascriptcore-class.mdc)

**/*.zig: Declare the extern C symbol in Zig and export a Zig-friendly alias for use
Wrap the Bun____toJS extern in a Zig method that takes a JSGlobalObject and returns JSC.JSValue

Files:

  • src/install/repository.zig
src/**/*.zig

📄 CodeRabbit inference engine (.cursor/rules/building-bun.mdc)

When adding debug logs in Zig, create a scoped logger and log via Bun APIs: const log = bun.Output.scoped(.${SCOPE}, .hidden); then log("...", .{})

src/**/*.zig: Use private fields in Zig with the # prefix (e.g., struct { #foo: u32 };)
Prefer decl literals in Zig (e.g., const decl: Decl = .{ .binding = 0, .value = 0 };)
Prefer placing @import statements at the bottom of the Zig file (formatter may reorder automatically)
Prefer @import("bun") rather than @import("root").bun or @import("../bun.zig")

In Zig code, be careful with allocators and use defer for cleanup

Files:

  • src/install/repository.zig
🔇 Additional comments (4)
src/install/repository.zig (4)

483-485: API change (temp_dir): LGTM

New temp_dir in download() signature is appropriate for temp-first flow.


515-521: Path buffer pooling and timing: LGTM

Correct use of path_buffer_pool and verbose timing.

Also applies to: 648-654


522-528: errdefer cleanup guard: LGTM

Temp dir best-effort cleanup with moved flag is sound.

Also applies to: 655-660


555-586: Atomic rename and EEXIST handling: LGTM

Race-safe move, absolute-path logging, and cleanup look correct; moved flag cancels errdefer.

If possible, add a stress test that forces the EEXIST branch (two concurrent installs of the same git dep) to guard regressions.

Also applies to: 713-744

Comment on lines +529 to +532
if (PackageManager.verbose_install) {
Output.prettyErrorln("[{s}] Cloning git repository (bare) to {s}<r>", .{ name, temp_target_full });
Output.flush();
}
Copy link
Contributor

Choose a reason for hiding this comment

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

🧹 Nitpick | 🔵 Trivial

Use a scoped logger for new verbose logs

Switch prettyErrorln/flush to a scoped logger per project guidelines.

Apply once near top-level (or per function) and replace calls:

+const RepoLog = Output.scoped(.install_repository, .hidden);
...
-                Output.prettyErrorln("[{s}] Cloning git repository (bare) to {s}<r>", .{ name, temp_target_full });
-                Output.flush();
+                RepoLog("[{s}] Cloning git repository (bare) to {s}", .{ name, temp_target_full });
...
-                Output.prettyErrorln("[{s}] Cloned bare repository to cache ({})<r>", .{ name, std.fmt.fmtDuration(elapsed) });
-                Output.flush();
+                RepoLog("[{s}] Cloned bare repository to cache ({})", .{ name, std.fmt.fmtDuration(elapsed) });
...
-                Output.prettyErrorln("[{s}] Cloning git repository to {s}<r>", .{ name, temp_target_full });
-                Output.flush();
+                RepoLog("[{s}] Cloning git repository to {s}", .{ name, temp_target_full });
...
-                Output.prettyErrorln("[{s}] Checking out {s}<r>", .{ name, resolved });
-                Output.flush();
+                RepoLog("[{s}] Checking out {s}", .{ name, resolved });
...
-                Output.prettyErrorln("[{s}] Checked out to cache ({})<r>", .{ name, std.fmt.fmtDuration(elapsed) });
-                Output.flush();
+                RepoLog("[{s}] Checked out to cache ({})", .{ name, std.fmt.fmtDuration(elapsed) });

As per coding guidelines

Also applies to: 587-591, 661-664, 685-688, 745-749

🤖 Prompt for AI Agents
In src/install/repository.zig around lines 529-532 (and similarly for 587-591,
661-664, 685-688, 745-749), replace direct calls to Output.prettyErrorln and
Output.flush with a scoped logger instance per the project guidelines: create or
obtain a scoped logger at the top of the function (or top-level scope for the
file), then call the scoped logger's verbose/pretty logging method instead of
Output.prettyErrorln and remove explicit Output.flush calls (or replace them
with the logger's flush if required). Ensure the logger is passed into any
helper functions that need to log so all verbose logs use the scoped logger
consistently.

@robobun
Copy link
Collaborator Author

robobun commented Oct 22, 2025

Closing this PR as it was a test/learning PR. The actual fix for issue #23600 is in PR #23957.

@robobun robobun closed this Oct 22, 2025
@robobun robobun reopened this Oct 22, 2025
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.

4 participants