From c61f11242b0fe6e4a0c9f79c490113092d263902 Mon Sep 17 00:00:00 2001 From: Claude Bot Date: Mon, 13 Oct 2025 16:02:31 +0000 Subject: [PATCH 1/6] Fix git repository cloning to use temp directory first MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 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 --- src/install/PackageManagerTask.zig | 3 ++ src/install/repository.zig | 71 +++++++++++++++++++++++++----- 2 files changed, 62 insertions(+), 12 deletions(-) diff --git a/src/install/PackageManagerTask.zig b/src/install/PackageManagerTask.zig index b34acf6a0fe233..3b95abc29c921e 100644 --- a/src/install/PackageManagerTask.zig +++ b/src/install/PackageManagerTask.zig @@ -162,6 +162,7 @@ pub fn callback(task: *ThreadPool.Task) void { this.request.git_clone.env, &this.log, manager.getCacheDirectory(), + manager.getTemporaryDirectory().handle, this.id, name, https, @@ -189,6 +190,7 @@ pub fn callback(task: *ThreadPool.Task) void { this.request.git_clone.env, &this.log, manager.getCacheDirectory(), + manager.getTemporaryDirectory().handle, this.id, name, ssh, @@ -213,6 +215,7 @@ pub fn callback(task: *ThreadPool.Task) void { this.request.git_checkout.env, &this.log, manager.getCacheDirectory(), + manager.getTemporaryDirectory().handle, git_checkout.repo_dir.stdDir(), git_checkout.name.slice(), git_checkout.url.slice(), diff --git a/src/install/repository.zig b/src/install/repository.zig index 59feeef8b66809..0b7c06f7b03d2e 100644 --- a/src/install/repository.zig +++ b/src/install/repository.zig @@ -2,6 +2,7 @@ threadlocal var final_path_buf: bun.PathBuffer = undefined; threadlocal var ssh_path_buf: bun.PathBuffer = undefined; threadlocal var folder_name_buf: bun.PathBuffer = undefined; threadlocal var json_path_buf: bun.PathBuffer = undefined; +threadlocal var temp_path_buf: bun.PathBuffer = undefined; const SloppyGlobalGitConfig = struct { has_askpass: bool = false, @@ -480,6 +481,7 @@ pub const Repository = extern struct { env: DotEnv.Map, log: *logger.Log, cache_dir: std.fs.Dir, + temp_dir: std.fs.Dir, task_id: Install.Task.Id, name: string, url: string, @@ -511,7 +513,11 @@ pub const Repository = extern struct { } else |not_found| clone: { if (not_found != error.FileNotFound) return not_found; - const target = Path.joinAbsString(PackageManager.get().cache_directory_path, &.{folder_name}, .auto); + // 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); _ = exec(allocator, env, &[_]string{ "git", @@ -520,7 +526,7 @@ pub const Repository = extern struct { "--quiet", "--bare", url, - target, + temp_target_full, }) catch |err| { if (err == error.RepositoryNotFound or attempt > 1) { log.addErrorFmt( @@ -534,6 +540,24 @@ pub const Repository = extern struct { return err; }; + // Move from temp to cache atomically + if (bun.sys.renameatConcurrently( + .fromStdDir(temp_dir), + tmpname, + .fromStdDir(cache_dir), + folder_name, + .{ .move_fallback = true }, + ).asErr()) |err| { + log.addErrorFmt( + null, + logger.Loc.Empty, + allocator, + "moving \"{s}\" to cache dir failed: {}\n From: {s}\n To: {s}", + .{ name, err, tmpname, folder_name }, + ) catch unreachable; + return error.InstallFailed; + } + break :clone try cache_dir.openDirZ(folder_name, .{}); }; } @@ -577,6 +601,7 @@ pub const Repository = extern struct { env: DotEnv.Map, log: *logger.Log, cache_dir: std.fs.Dir, + temp_dir: std.fs.Dir, repo_dir: std.fs.Dir, name: string, url: string, @@ -588,7 +613,11 @@ pub const Repository = extern struct { var package_dir = bun.openDir(cache_dir, folder_name) catch |not_found| brk: { if (not_found != error.ENOENT) return not_found; - const target = Path.joinAbsString(PackageManager.get().cache_directory_path, &.{folder_name}, .auto); + // 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); _ = exec(allocator, env, &[_]string{ "git", @@ -597,7 +626,7 @@ pub const Repository = extern struct { "--quiet", "--no-checkout", try bun.getFdPath(.fromStdDir(repo_dir), &final_path_buf), - target, + temp_target_full, }) catch |err| { log.addErrorFmt( null, @@ -609,9 +638,7 @@ pub const Repository = extern struct { return err; }; - const folder = Path.joinAbsString(PackageManager.get().cache_directory_path, &.{folder_name}, .auto); - - _ = exec(allocator, env, &[_]string{ "git", "-C", folder, "checkout", "--quiet", resolved }) catch |err| { + _ = exec(allocator, env, &[_]string{ "git", "-C", temp_target_full, "checkout", "--quiet", resolved }) catch |err| { log.addErrorFmt( null, logger.Loc.Empty, @@ -621,18 +648,38 @@ pub const Repository = extern struct { ) catch unreachable; return err; }; - var dir = try bun.openDir(cache_dir, folder_name); - dir.deleteTree(".git") catch {}; + + 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(); + + // Move from temp to cache atomically + if (bun.sys.renameatConcurrently( + .fromStdDir(temp_dir), + tmpname, + .fromStdDir(cache_dir), + folder_name, + .{ .move_fallback = true }, + ).asErr()) |err| { + log.addErrorFmt( + null, + logger.Loc.Empty, + allocator, + "moving \"{s}\" to cache dir failed: {}\n From: {s}\n To: {s}", + .{ name, err, tmpname, folder_name }, + ) catch unreachable; + return error.InstallFailed; + } - break :brk dir; + break :brk try bun.openDir(cache_dir, folder_name); }; defer package_dir.close(); From 63db5023dde5ff45a05dc10eec80c683b25e7b4d Mon Sep 17 00:00:00 2001 From: Claude Bot Date: Mon, 13 Oct 2025 16:52:15 +0000 Subject: [PATCH 2/6] Address CodeRabbit review: Add cleanup and race handling MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - 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 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude --- src/install/repository.zig | 22 ++++++++++++++++++++-- 1 file changed, 20 insertions(+), 2 deletions(-) diff --git a/src/install/repository.zig b/src/install/repository.zig index 0b7c06f7b03d2e..f5843df48bdc8f 100644 --- a/src/install/repository.zig +++ b/src/install/repository.zig @@ -518,6 +518,8 @@ pub const Repository = extern struct { 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", @@ -548,15 +550,22 @@ pub const Repository = extern struct { folder_name, .{ .move_fallback = true }, ).asErr()) |err| { + // If destination exists, another process likely completed the work + if (cache_dir.openDirZ(folder_name, .{})) |dir2| { + // Best-effort cleanup of our temp path + temp_dir.deleteTree(tmpname) catch {}; + break :clone dir2; + } else |_| {} log.addErrorFmt( null, logger.Loc.Empty, allocator, "moving \"{s}\" to cache dir failed: {}\n From: {s}\n To: {s}", - .{ name, err, tmpname, folder_name }, + .{ name, err, temp_target_full, folder_name }, ) catch unreachable; return error.InstallFailed; } + moved = true; break :clone try cache_dir.openDirZ(folder_name, .{}); }; @@ -618,6 +627,8 @@ pub const Repository = extern struct { 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", @@ -669,15 +680,22 @@ pub const Repository = extern struct { folder_name, .{ .move_fallback = true }, ).asErr()) |err| { + // If destination exists, another process likely completed the work + if (bun.openDir(cache_dir, folder_name)) |_| { + // Best-effort cleanup of our temp path + temp_dir.deleteTree(tmpname) catch {}; + break :brk try bun.openDir(cache_dir, folder_name); + } else |_| {} log.addErrorFmt( null, logger.Loc.Empty, allocator, "moving \"{s}\" to cache dir failed: {}\n From: {s}\n To: {s}", - .{ name, err, tmpname, folder_name }, + .{ name, err, temp_target_full, folder_name }, ) catch unreachable; return error.InstallFailed; } + moved = true; break :brk try bun.openDir(cache_dir, folder_name); }; From f0b70424fcdaac9ba84d3322a9b936c1badff4ef Mon Sep 17 00:00:00 2001 From: Claude Bot Date: Mon, 13 Oct 2025 17:04:37 +0000 Subject: [PATCH 3/6] Add verbose logging for git repository operations MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 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 --- src/install/repository.zig | 40 ++++++++++++++++++++++++++++++++++++++ 1 file changed, 40 insertions(+) diff --git a/src/install/repository.zig b/src/install/repository.zig index f5843df48bdc8f..97496a4af56daa 100644 --- a/src/install/repository.zig +++ b/src/install/repository.zig @@ -513,6 +513,8 @@ pub const Repository = extern struct { } else |not_found| clone: { if (not_found != error.FileNotFound) return not_found; + const time_started_for_verbose_logs: u64 = if (PackageManager.verbose_install) bun.getRoughTickCount().ns() else 0; + // 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()); @@ -521,6 +523,11 @@ pub const Repository = extern struct { var moved = false; errdefer if (!moved) temp_dir.deleteTree(tmpname) catch {}; + if (PackageManager.verbose_install) { + Output.prettyErrorln("[{s}] Cloning git repository (bare) to {s}", .{ name, tmpname }); + Output.flush(); + } + _ = exec(allocator, env, &[_]string{ "git", "clone", @@ -554,6 +561,10 @@ pub const Repository = extern struct { if (cache_dir.openDirZ(folder_name, .{})) |dir2| { // Best-effort cleanup of our temp path temp_dir.deleteTree(tmpname) catch {}; + if (PackageManager.verbose_install) { + Output.prettyErrorln("[{s}] Cache dir already exists (another process completed first)", .{name}); + Output.flush(); + } break :clone dir2; } else |_| {} log.addErrorFmt( @@ -567,6 +578,12 @@ pub const Repository = extern struct { } moved = true; + if (PackageManager.verbose_install) { + const elapsed = bun.getRoughTickCount().ns() - time_started_for_verbose_logs; + Output.prettyErrorln("[{s}] Cloned bare repository to cache ({})", .{ name, std.fmt.fmtDuration(elapsed) }); + Output.flush(); + } + break :clone try cache_dir.openDirZ(folder_name, .{}); }; } @@ -622,6 +639,8 @@ pub const Repository = extern struct { var package_dir = bun.openDir(cache_dir, folder_name) catch |not_found| brk: { if (not_found != error.ENOENT) return not_found; + const time_started_for_verbose_logs: u64 = if (PackageManager.verbose_install) bun.getRoughTickCount().ns() else 0; + // 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()); @@ -630,6 +649,11 @@ pub const Repository = extern struct { var moved = false; errdefer if (!moved) temp_dir.deleteTree(tmpname) catch {}; + if (PackageManager.verbose_install) { + Output.prettyErrorln("[{s}] Cloning git repository to {s}", .{ name, tmpname }); + Output.flush(); + } + _ = exec(allocator, env, &[_]string{ "git", "clone", @@ -649,6 +673,11 @@ pub const Repository = extern struct { return err; }; + if (PackageManager.verbose_install) { + Output.prettyErrorln("[{s}] Checking out {s}", .{ name, resolved }); + Output.flush(); + } + _ = exec(allocator, env, &[_]string{ "git", "-C", temp_target_full, "checkout", "--quiet", resolved }) catch |err| { log.addErrorFmt( null, @@ -684,6 +713,10 @@ pub const Repository = extern struct { if (bun.openDir(cache_dir, folder_name)) |_| { // Best-effort cleanup of our temp path temp_dir.deleteTree(tmpname) catch {}; + if (PackageManager.verbose_install) { + Output.prettyErrorln("[{s}] Cache dir already exists (another process completed first)", .{name}); + Output.flush(); + } break :brk try bun.openDir(cache_dir, folder_name); } else |_| {} log.addErrorFmt( @@ -697,6 +730,12 @@ pub const Repository = extern struct { } moved = true; + if (PackageManager.verbose_install) { + const elapsed = bun.getRoughTickCount().ns() - time_started_for_verbose_logs; + Output.prettyErrorln("[{s}] Checked out to cache ({})", .{ name, std.fmt.fmtDuration(elapsed) }); + Output.flush(); + } + break :brk try bun.openDir(cache_dir, folder_name); }; defer package_dir.close(); @@ -760,6 +799,7 @@ const PackageManager = Install.PackageManager; const bun = @import("bun"); const OOM = bun.OOM; +const Output = bun.Output; const Path = bun.path; const logger = bun.logger; const strings = bun.strings; From bc6cddacbee414f35f8dc36cf487c4dec06308e9 Mon Sep 17 00:00:00 2001 From: Claude Bot Date: Mon, 13 Oct 2025 17:23:44 +0000 Subject: [PATCH 4/6] Address CodeRabbit nitpicks: improve logging clarity MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - 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 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude --- src/install/repository.zig | 14 ++++++++++---- 1 file changed, 10 insertions(+), 4 deletions(-) diff --git a/src/install/repository.zig b/src/install/repository.zig index 97496a4af56daa..3cae7145c22c06 100644 --- a/src/install/repository.zig +++ b/src/install/repository.zig @@ -524,7 +524,7 @@ pub const Repository = extern struct { errdefer if (!moved) temp_dir.deleteTree(tmpname) catch {}; if (PackageManager.verbose_install) { - Output.prettyErrorln("[{s}] Cloning git repository (bare) to {s}", .{ name, tmpname }); + Output.prettyErrorln("[{s}] Cloning git repository (bare) to {s}", .{ name, temp_target_full }); Output.flush(); } @@ -561,18 +561,21 @@ pub const Repository = extern struct { if (cache_dir.openDirZ(folder_name, .{})) |dir2| { // Best-effort cleanup of our temp path temp_dir.deleteTree(tmpname) catch {}; + moved = true; // cancel errdefer; already cleaned up if (PackageManager.verbose_install) { Output.prettyErrorln("[{s}] Cache dir already exists (another process completed first)", .{name}); Output.flush(); } break :clone dir2; } else |_| {} + const cache_target = try bun.getFdPath(.fromStdDir(cache_dir), &final_path_buf); + const dest_full = Path.joinAbsString(cache_target, &.{folder_name}, .auto); log.addErrorFmt( null, logger.Loc.Empty, allocator, "moving \"{s}\" to cache dir failed: {}\n From: {s}\n To: {s}", - .{ name, err, temp_target_full, folder_name }, + .{ name, err, temp_target_full, dest_full }, ) catch unreachable; return error.InstallFailed; } @@ -650,7 +653,7 @@ pub const Repository = extern struct { errdefer if (!moved) temp_dir.deleteTree(tmpname) catch {}; if (PackageManager.verbose_install) { - Output.prettyErrorln("[{s}] Cloning git repository to {s}", .{ name, tmpname }); + Output.prettyErrorln("[{s}] Cloning git repository to {s}", .{ name, temp_target_full }); Output.flush(); } @@ -713,18 +716,21 @@ pub const Repository = extern struct { if (bun.openDir(cache_dir, folder_name)) |_| { // Best-effort cleanup of our temp path temp_dir.deleteTree(tmpname) catch {}; + moved = true; // cancel errdefer; already cleaned up if (PackageManager.verbose_install) { Output.prettyErrorln("[{s}] Cache dir already exists (another process completed first)", .{name}); Output.flush(); } break :brk try bun.openDir(cache_dir, folder_name); } else |_| {} + const cache_target = try bun.getFdPath(.fromStdDir(cache_dir), &final_path_buf); + const dest_full = Path.joinAbsString(cache_target, &.{folder_name}, .auto); log.addErrorFmt( null, logger.Loc.Empty, allocator, "moving \"{s}\" to cache dir failed: {}\n From: {s}\n To: {s}", - .{ name, err, temp_target_full, folder_name }, + .{ name, err, temp_target_full, dest_full }, ) catch unreachable; return error.InstallFailed; } From c7a2c79c512656bbbd813ead376b6e698be0b9d4 Mon Sep 17 00:00:00 2001 From: Claude Bot Date: Mon, 13 Oct 2025 18:21:33 +0000 Subject: [PATCH 5/6] Update ban-limits.json for std.fs.Dir usage MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 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 --- test/internal/ban-limits.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test/internal/ban-limits.json b/test/internal/ban-limits.json index e513d7f719e31b..ff1b1cde2d0983 100644 --- a/test/internal/ban-limits.json +++ b/test/internal/ban-limits.json @@ -33,7 +33,7 @@ "std.debug.dumpStackTrace": 0, "std.debug.print": 0, "std.enums.tagName(": 2, - "std.fs.Dir": 165, + "std.fs.Dir": 167, "std.fs.File": 62, "std.fs.cwd": 103, "std.log": 1, From 009adfd2323dea7b696ed8b5f7e75c53d5c6e66f Mon Sep 17 00:00:00 2001 From: Dylan Conway Date: Mon, 20 Oct 2025 19:56:58 -0700 Subject: [PATCH 6/6] update --- src/install/repository.zig | 22 ++++++++++++++-------- 1 file changed, 14 insertions(+), 8 deletions(-) diff --git a/src/install/repository.zig b/src/install/repository.zig index 3cae7145c22c06..ddfd7acb885918 100644 --- a/src/install/repository.zig +++ b/src/install/repository.zig @@ -2,7 +2,6 @@ threadlocal var final_path_buf: bun.PathBuffer = undefined; threadlocal var ssh_path_buf: bun.PathBuffer = undefined; threadlocal var folder_name_buf: bun.PathBuffer = undefined; threadlocal var json_path_buf: bun.PathBuffer = undefined; -threadlocal var temp_path_buf: bun.PathBuffer = undefined; const SloppyGlobalGitConfig = struct { has_askpass: bool = false, @@ -515,10 +514,14 @@ pub const Repository = extern struct { const time_started_for_verbose_logs: u64 = if (PackageManager.verbose_install) bun.getRoughTickCount().ns() else 0; + const temp_path_buf = bun.path_buffer_pool.get(); + defer bun.path_buffer_pool.put(temp_path_buf); + const tmpname_buf = bun.path_buffer_pool.get(); + defer bun.path_buffer_pool.put(tmpname_buf); + // 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 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 {}; @@ -644,10 +647,13 @@ pub const Repository = extern struct { const time_started_for_verbose_logs: u64 = if (PackageManager.verbose_install) bun.getRoughTickCount().ns() else 0; - // 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_path_buf = bun.path_buffer_pool.get(); + defer bun.path_buffer_pool.put(temp_path_buf); + const tmpname_buf = bun.path_buffer_pool.get(); + defer bun.path_buffer_pool.put(tmpname_buf); + + 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 {};