Skip to content
31 changes: 22 additions & 9 deletions src/install/PackageInstall.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2350,6 +2350,12 @@ impl<'a> PackageInstall<'a> {
ZStr::from_buf(&buf[..], subpath_len + 1 + b"package.json".len());
break 'package_json_exists sys::exists_at(self.cache_dir, subpath);
}
resolution::Tag::Git => {
crate::package_manager::directories::is_git_folder_in_cache_at(
self.cache_dir,
self.cache_dir_subpath.as_bytes(),
)
}
Comment thread
robobun marked this conversation as resolved.
_ => sys::directory_exists_at(self.cache_dir, self.cache_dir_subpath)
.unwrap_or(false),
};
Expand All @@ -2364,15 +2370,22 @@ impl<'a> PackageInstall<'a> {
});
let cache_dir_subpath_without_patch_hash =
&self.cache_dir_subpath.as_bytes()[..idx];
// Use a stack PathBuffer (no shared state).
let mut join_buf = PathBuffer::uninit();
join_buf[..cache_dir_subpath_without_patch_hash.len()]
.copy_from_slice(cache_dir_subpath_without_patch_hash);
join_buf[cache_dir_subpath_without_patch_hash.len()] = 0;
// SAFETY: NUL written above.
let subpath =
ZStr::from_buf(&join_buf[..], cache_dir_subpath_without_patch_hash.len());
let exists = sys::directory_exists_at(self.cache_dir, subpath).unwrap_or(false);
let exists = if matches!(resolution_tag, resolution::Tag::Git) {
crate::package_manager::directories::is_git_folder_in_cache_at(
self.cache_dir,
cache_dir_subpath_without_patch_hash,
)
} else {
// Use a stack PathBuffer (no shared state).
let mut join_buf = PathBuffer::uninit();
join_buf[..cache_dir_subpath_without_patch_hash.len()]
.copy_from_slice(cache_dir_subpath_without_patch_hash);
join_buf[cache_dir_subpath_without_patch_hash.len()] = 0;
// SAFETY: NUL written above.
let subpath =
ZStr::from_buf(&join_buf[..], cache_dir_subpath_without_patch_hash.len());
sys::directory_exists_at(self.cache_dir, subpath).unwrap_or(false)
};
if exists {
manager.set_preinstall_state(package_id, crate::PreinstallState::Done);
}
Expand Down
19 changes: 19 additions & 0 deletions src/install/PackageManager/PackageManagerDirectories.rs
Original file line number Diff line number Diff line change
Expand Up @@ -754,6 +754,25 @@ pub fn is_folder_in_cache(this: &mut PackageManager, folder_path: &ZStr) -> bool
sys::directory_exists_at(get_cache_directory(this), folder_path).unwrap_or(false)
}

/// Git checkouts can legitimately lack `package.json`, so their completeness
/// marker is the `.bun-tag` that `Repository::checkout` writes last. A folder
/// without it is a leftover from an interrupted checkout; installing it would
/// produce an empty package.
Comment thread
robobun marked this conversation as resolved.
pub fn is_git_folder_in_cache(this: &mut PackageManager, folder_path: &ZStr) -> bool {
is_git_folder_in_cache_at(get_cache_directory(this), folder_path.as_bytes())
}

/// [`is_git_folder_in_cache`] for call sites that hold the cache dir `Fd` and
/// folder subpath directly (the hoisted and isolated installers).
Comment thread
robobun marked this conversation as resolved.
pub fn is_git_folder_in_cache_at(cache_dir: Fd, folder_subpath: &[u8]) -> bool {
let mut buf = PathBuffer::uninit();
let tag_path = path::resolve_path::join_z_buf::<path::platform::Auto>(
&mut buf.0,
&[folder_subpath, b".bun-tag"],
);
sys::exists_at(cache_dir, tag_path)
}
Comment thread
robobun marked this conversation as resolved.
Comment thread
coderabbitai[bot] marked this conversation as resolved.

// ─────────────────────────── global directories ───────────────────────────────

pub fn setup_global_dir(manager: &mut PackageManager, ctx: &Command::Context) -> Result<(), Error> {
Expand Down
15 changes: 13 additions & 2 deletions src/install/PackageManager/PackageManagerLifecycle.rs
Original file line number Diff line number Diff line change
Expand Up @@ -177,7 +177,13 @@ impl PackageManager {
return PreinstallState::Extract;
}

if directories::is_folder_in_cache(self, folder_path) {
let folder_in_cache =
if matches!(pkg.resolution.tag, ResolutionTag::Git) && patch_hash.is_none() {
directories::is_git_folder_in_cache(self, folder_path)
} else {
directories::is_folder_in_cache(self, folder_path)
};
if folder_in_cache {
self.set_preinstall_state(pkg.meta.id, PreinstallState::Done);
return PreinstallState::Done;
}
Expand All @@ -200,7 +206,12 @@ impl PackageManager {
});
// Owned NUL-terminated copy.
let non_patched_path = ZBox::from_bytes(&folder_path.as_bytes()[..idx]);
if directories::is_folder_in_cache(self, &non_patched_path) {
let base_in_cache = if matches!(pkg.resolution.tag, ResolutionTag::Git) {
directories::is_git_folder_in_cache(self, &non_patched_path)
} else {
directories::is_folder_in_cache(self, &non_patched_path)
};
if base_in_cache {
self.set_preinstall_state(pkg.meta.id, PreinstallState::ApplyPatch);
// yay step 1 is already done for us
return PreinstallState::ApplyPatch;
Expand Down
6 changes: 6 additions & 0 deletions src/install/isolated_install.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2370,6 +2370,12 @@ pub(crate) fn install_isolated_packages(
pkg_cache_dir_subpath.set_length(cache_dir_path_save);
exists
}
ResolutionTag::Git => {
package_manager::directories::is_git_folder_in_cache_at(
cache_dir,
pkg_cache_dir_subpath.slice_z().as_bytes(),
)
}
_ => sys::directory_exists_at(
cache_dir,
pkg_cache_dir_subpath.slice_z(),
Expand Down
Loading
Loading