use core::fmt;
use std::sync::OnceLock;
use bstr::BStr;
use bun_alloc::AllocError;
use bun_core::strings;
use bun_core::{self, Error, Output, err};
use bun_paths::{self as Path, PathBuffer};
use bun_semver::string::Buf as StringBuf;
use crate::dependency as Dependency;
use crate::hosted_git_info;
use crate::install::{self as Install, ExtractData, PackageManager};
struct TlBufs {
final_path_buf: PathBuffer,
ssh_path_buf: PathBuffer,
folder_name_buf: PathBuffer,
json_path_buf: PathBuffer,
}
thread_local! {
static TL_BUFS: core::cell::Cell<*mut TlBufs> =
const { core::cell::Cell::new(core::ptr::null_mut()) };
}
fn tl_bufs() -> *mut TlBufs {
TL_BUFS.with(|c| {
let mut p = c.get();
if p.is_null() {
p = bun_core::heap::into_raw(Box::new(TlBufs {
final_path_buf: PathBuffer::ZEROED,
ssh_path_buf: PathBuffer::ZEROED,
folder_name_buf: PathBuffer::ZEROED,
json_path_buf: PathBuffer::ZEROED,
}));
c.set(p);
}
p
})
}
impl TlBufs {
#[inline]
fn ssh_path_buf() -> &'static mut PathBuffer {
unsafe { &mut (*tl_bufs()).ssh_path_buf }
}
#[inline]
fn final_path_buf() -> &'static mut PathBuffer {
unsafe { &mut (*tl_bufs()).final_path_buf }
}
#[inline]
fn folder_name_buf() -> &'static mut PathBuffer {
unsafe { &mut (*tl_bufs()).folder_name_buf }
}
#[inline]
fn json_path_buf() -> &'static mut PathBuffer {
unsafe { &mut (*tl_bufs()).json_path_buf }
}
}
#[derive(Clone, Copy, Default)]
struct SloppyGlobalGitConfig {
has_askpass: bool,
has_ssh_command: bool,
}
static SLOPPY_HOLDER: OnceLock<SloppyGlobalGitConfig> = OnceLock::new();
impl SloppyGlobalGitConfig {
pub(crate) fn get() -> SloppyGlobalGitConfig {
*SLOPPY_HOLDER.get_or_init(Self::load_and_parse)
}
fn load_and_parse() -> SloppyGlobalGitConfig {
let Some(home_dir) = bun_core::env_var::HOME.get() else {
return SloppyGlobalGitConfig::default();
};
let mut config_file_path_buf = PathBuffer::uninit();
let config_file_path = bun_paths::resolve_path::join_abs_string_buf_z::<
bun_paths::platform::Auto,
>(home_dir, &mut config_file_path_buf, &[b".gitconfig"]);
let Ok(source) = bun_ast::to_source(
config_file_path,
bun_ast::ToSourceOptions { convert_bom: true },
) else {
return SloppyGlobalGitConfig::default();
};
let mut remaining = strings::split(source.contents(), b"\n");
let mut found_askpass = false;
let mut found_ssh_command = false;
let mut in_core = false; while let Some(line_) = remaining.next() {
if found_askpass && found_ssh_command {
break;
}
let line = strings::trim(line_, b"\t \r");
if line.is_empty() {
continue;
}
if line[0] == b'#' {
continue;
}
if line[0] == b'[' {
if let Some(end_bracket) = strings::index_of_char(line, b']') {
if &line[0..end_bracket as usize + 1] == b"[core]" {
in_core = true;
continue;
}
}
in_core = false;
continue;
}
if in_core {
if !found_askpass {
const K: &[u8] = b"askpass";
if line.len() > K.len()
&& strings::eql_case_insensitive_ascii_ignore_length(&line[..K.len()], K)
&& matches!(line[K.len()], b' ' | b'\t' | b'=')
{
found_askpass = true;
continue;
}
}
if !found_ssh_command {
const K: &[u8] = b"sshCommand";
if line.len() > K.len()
&& strings::eql_case_insensitive_ascii_ignore_length(&line[..K.len()], K)
&& matches!(line[K.len()], b' ' | b'\t' | b'=')
{
found_ssh_command = true;
}
}
} else {
if !found_askpass {
const K: &[u8] = b"core.askpass";
if line.len() > K.len()
&& strings::eql_case_insensitive_ascii_ignore_length(&line[..K.len()], K)
&& matches!(line[K.len()], b' ' | b'\t' | b'=')
{
found_askpass = true;
continue;
}
}
if !found_ssh_command {
const K: &[u8] = b"core.sshCommand";
if line.len() > K.len()
&& strings::eql_case_insensitive_ascii_ignore_length(&line[..K.len()], K)
&& matches!(line[K.len()], b' ' | b'\t' | b'=')
{
found_ssh_command = true;
}
}
}
}
SloppyGlobalGitConfig {
has_askpass: found_askpass,
has_ssh_command: found_ssh_command,
}
}
}
pub use bun_install_types::resolver_hooks::Repository;
pub(crate) struct SharedEnv {
env: Option<bun_dotenv::Map>,
}
pub(crate) static SHARED_ENV: bun_core::RacyCell<SharedEnv> =
bun_core::RacyCell::new(SharedEnv { env: None });
impl SharedEnv {
pub(crate) fn get(other: &mut bun_dotenv::Loader) -> &'static bun_dotenv::Map {
unsafe {
let this = &mut *SHARED_ENV.get();
if this.env.is_none() {
let mut cloned = bun_core::handle_oom(other.map.clone_with_allocator());
if cloned.get(b"GIT_ASKPASS").is_none() {
let config = SloppyGlobalGitConfig::get();
if !config.has_askpass {
bun_core::handle_oom(cloned.put(b"GIT_ASKPASS", b"echo"));
}
}
if cloned.get(b"GIT_SSH_COMMAND").is_none() {
let config = SloppyGlobalGitConfig::get();
if !config.has_ssh_command {
bun_core::handle_oom(cloned.put(
b"GIT_SSH_COMMAND",
b"ssh -oStrictHostKeyChecking=accept-new",
));
}
}
this.env = Some(cloned);
}
this.env.as_ref().unwrap()
}
}
}
#[inline]
pub(crate) fn host_tld(host: &[u8]) -> Option<&'static [u8]> {
match host.len() {
6 => match host {
b"github" => Some(b".com"),
b"gitlab" => Some(b".com"),
_ => None,
},
9 if host == b"bitbucket" => Some(b".org"),
_ => None,
}
}
pub(crate) fn is_safe_resolved_tag(resolved: &[u8]) -> bool {
!resolved.is_empty()
&& resolved.len() <= 256
&& resolved[0] != b'-'
&& resolved != b"."
&& resolved != b".."
&& resolved
.iter()
.all(|&b| b.is_ascii_alphanumeric() || matches!(b, b'-' | b'_' | b'.'))
}
pub trait RepositoryExt: Sized {
fn parse_append_git(input: &[u8], buf: &mut StringBuf<'_>) -> Result<Repository, AllocError>;
fn parse_append_github(input: &[u8], buf: &mut StringBuf<'_>)
-> Result<Repository, AllocError>;
fn create_dependency_name_from_version_literal(
repository: &Repository,
string_buf: &[u8],
dep: &Install::Dependency,
) -> Vec<u8>;
fn format_as(&self, label: &str, buf: &[u8], writer: &mut impl fmt::Write) -> fmt::Result;
fn fmt_store_path<'a>(&'a self, label: &'a str, string_buf: &'a [u8])
-> StorePathFormatter<'a>;
fn fmt<'a>(&'a self, label: &'a str, buf: &'a [u8]) -> Formatter<'a>;
fn try_ssh(url: &[u8]) -> Option<&[u8]>;
fn try_https(url: &[u8]) -> Option<&[u8]>;
fn download(
env: &bun_dotenv::Map,
log: &mut bun_ast::Log,
cache_dir: bun_sys::Fd,
task_id: crate::package_manager_task::Id,
name: &[u8],
url: &[u8],
attempt: u8,
) -> Result<bun_sys::Dir, Error>;
fn find_commit(
env: &mut bun_dotenv::Loader,
log: &mut bun_ast::Log,
repo_dir: bun_sys::Fd,
name: &[u8],
committish: &[u8],
task_id: crate::package_manager_task::Id,
) -> Result<Vec<u8>, Error>;
fn checkout(
env: &bun_dotenv::Map,
log: &mut bun_ast::Log,
cache_dir: bun_sys::Fd,
repo_dir: bun_sys::Fd,
name: &[u8],
url: &[u8],
resolved: &[u8],
) -> Result<ExtractData, Error>;
}
fn exec(env: &bun_dotenv::Map, argv: &[&[u8]]) -> Result<Vec<u8>, Error> {
let mut env = bun_core::handle_oom(env.clone_with_allocator());
let std_map = env.std_env_map()?;
let result = bun_spawn::run(bun_spawn::RunOptions {
argv,
env_map: std_map.get(),
})?;
match result.term {
bun_spawn::Term::Exited(sig) => {
if sig == 0 {
return Ok(result.stdout);
} else if
(strings::contains(&result.stderr, b"remote:")
&& strings::contains(&result.stderr, b"not")
&& strings::contains(&result.stderr, b"found"))
|| strings::contains(&result.stderr, b"does not exist")
{
return Err(err!("RepositoryNotFound"));
}
}
_ => {}
}
{
let term = match result.term {
bun_spawn::Term::Exited(code) => format!("exit code {code}"),
bun_spawn::Term::Signal(sig) => format!("signal {sig}"),
bun_spawn::Term::Stopped(sig) => format!("stopped (signal {sig})"),
bun_spawn::Term::Unknown(_) => "unknown status".to_string(),
};
Output::err_generic("{} failed with {}", (BStr::new(argv[0]), term.as_str()));
if !result.stderr.is_empty() {
let ew = Output::error_writer();
let _ = ew.write_all(&result.stderr);
if result.stderr.last() != Some(&b'\n') {
let _ = ew.write_all(b"\n");
}
}
Output::flush();
}
Err(err!("InstallFailed"))
}
struct CacheStaging {
cache_dir: bun_sys::Fd,
tmp_name_buf: [u8; 64],
tmp_name_len: usize,
}
impl CacheStaging {
fn new(cache_dir: bun_sys::Fd) -> Result<Self, Error> {
let mut tmp_name_buf = [0u8; 64];
let tmp_name_len = Path::fs::FileSystem::tmpname(
b"tmp",
&mut tmp_name_buf,
bun_core::fast_random(),
)
.map_err(|_| err!("NoSpaceLeft"))?
.len();
Ok(Self {
cache_dir,
tmp_name_buf,
tmp_name_len,
})
}
fn tmp_name(&self) -> &[u8] {
&self.tmp_name_buf[..self.tmp_name_len]
}
fn tmp_path(&self) -> &'static [u8] {
Path::resolve_path::join_abs_string::<Path::platform::Auto>(
&PackageManager::get().cache_directory_path,
&[self.tmp_name()],
)
}
fn discard(&self) {
let _ = bun_sys::Dir::borrow(&self.cache_dir).delete_tree(self.tmp_name());
}
fn publish(
self,
log: &mut bun_ast::Log,
name: &[u8],
folder_name: &[u8],
) -> Result<bun_sys::Dir, Error> {
let renamed = bun_sys::renameat_concurrently_a(
self.cache_dir,
self.tmp_name(),
self.cache_dir,
folder_name,
bun_sys::RenameatConcurrentlyOptions {
move_fallback: false,
},
);
self.discard();
if let Err(err) = renamed {
log.add_error_fmt(
None,
bun_ast::Loc::EMPTY,
format_args!(
"moving \"{}\" to cache dir failed: {}",
BStr::new(name),
err
),
);
return Err(err!("InstallFailed"));
}
bun_sys::Dir::borrow(&self.cache_dir)
.open_at(folder_name)
.map_err(Error::from)
}
}
impl RepositoryExt for Repository {
fn parse_append_git(input: &[u8], buf: &mut StringBuf<'_>) -> Result<Repository, AllocError> {
let mut remain = input;
if remain.starts_with(b"git+") {
remain = &remain[b"git+".len()..];
}
if let Some(hash) = strings::last_index_of_char(remain, b'#') {
return Ok(Repository {
repo: buf.append(&remain[..hash])?,
committish: buf.append(&remain[hash + 1..])?,
..Default::default()
});
}
Ok(Repository {
repo: buf.append(remain)?,
..Default::default()
})
}
fn parse_append_github(
input: &[u8],
buf: &mut StringBuf<'_>,
) -> Result<Repository, AllocError> {
let mut remain = input;
if remain.starts_with(b"github:") {
remain = &remain[b"github:".len()..];
}
let mut hash: usize = 0;
let mut slash: usize = 0;
for (i, &c) in remain.iter().enumerate() {
match c {
b'/' => slash = i,
b'#' => hash = i,
_ => {}
}
}
let repo = if hash == 0 {
&remain[slash + 1..]
} else {
&remain[slash + 1..hash]
};
let mut result = Repository {
owner: buf.append(&remain[..slash])?,
repo: buf.append(repo)?,
..Default::default()
};
if hash != 0 {
result.committish = buf.append(&remain[hash + 1..])?;
}
Ok(result)
}
fn create_dependency_name_from_version_literal(
repository: &Repository,
string_buf: &[u8],
dep: &Install::Dependency,
) -> Vec<u8> {
let buf = string_buf;
let repo_name = repository.repo;
let repo_name_str = repo_name.slice(buf);
let name = 'brk: {
let mut remain = repo_name_str;
if let Some(hash_index) = strings::index_of_char(remain, b'#') {
remain = &remain[..hash_index as usize];
}
if remain.is_empty() {
break 'brk remain;
}
if let Some(slash_index) = strings::last_index_of_char(remain, b'/') {
remain = &remain[slash_index + 1..];
}
remain
};
if name.is_empty() {
let version_literal = dep.version.literal.slice(buf);
let mut name_buf = [0u8; bun_sha::SHA1::DIGEST];
let mut sha1 = bun_sha::SHA1::init();
sha1.update(version_literal);
sha1.r#final(&mut name_buf);
return name_buf.to_vec();
}
name.to_vec()
}
fn format_as(&self, label: &str, buf: &[u8], writer: &mut impl fmt::Write) -> fmt::Result {
let formatter = Formatter {
label,
repository: self,
buf,
};
write!(writer, "{}", formatter)
}
fn fmt_store_path<'a>(
&'a self,
label: &'a str,
string_buf: &'a [u8],
) -> StorePathFormatter<'a> {
StorePathFormatter {
repo: self,
label,
string_buf,
}
}
fn fmt<'a>(&'a self, label: &'a str, buf: &'a [u8]) -> Formatter<'a> {
Formatter {
repository: self,
buf,
label,
}
}
fn try_ssh(url: &[u8]) -> Option<&[u8]> {
let ssh_path_buf = TlBufs::ssh_path_buf();
if url.starts_with(b"http") {
return None;
}
if url.starts_with(b"git@") {
return Some(url);
}
if url.starts_with(b"ssh://") {
let pair = hosted_git_info::UrlProtocolPair {
url: hosted_git_info::UrlProtocolPairUrl::Unmanaged(url),
protocol: hosted_git_info::UrlProtocol::WellFormed(
hosted_git_info::WellDefinedProtocol::GitPlusSsh,
),
};
let Ok(corrected) = hosted_git_info::correct_url(&pair) else {
return Some(url); };
let corrected_str = corrected.url_slice();
let result = &mut ssh_path_buf[..corrected_str.len()];
result.copy_from_slice(corrected_str);
return Some(&ssh_path_buf[..corrected_str.len()]);
}
if Dependency::is_scp_like_path(url) {
const PREFIX: &[u8] = b"ssh://git@";
ssh_path_buf[..PREFIX.len()].copy_from_slice(PREFIX);
let rest = &mut ssh_path_buf[PREFIX.len()..];
let colon_index = strings::index_of_char(url, b':');
if let Some(colon) = colon_index {
let colon = colon as usize;
if let Some(tld) = host_tld(&url[..colon]) {
rest[..colon].copy_from_slice(&url[..colon]);
rest[colon..colon + tld.len()].copy_from_slice(tld);
rest[colon + tld.len()] = b'/';
rest[colon + tld.len() + 1..colon + tld.len() + 1 + (url.len() - colon - 1)]
.copy_from_slice(&url[colon + 1..]);
let out = &ssh_path_buf[..url.len() + PREFIX.len() + tld.len()];
return Some(out);
}
}
rest[..url.len()].copy_from_slice(url);
if let Some(colon) = colon_index {
rest[colon as usize] = b'/';
}
let final_ = &ssh_path_buf[..url.len() + b"ssh://".len()];
return Some(final_);
}
None
}
fn try_https(url: &[u8]) -> Option<&[u8]> {
let final_path_buf = TlBufs::final_path_buf();
if url.starts_with(b"http") {
return Some(url);
}
if url.starts_with(b"ssh://") {
final_path_buf[..b"https".len()].copy_from_slice(b"https");
let tail = &url[b"ssh".len()..];
final_path_buf[b"https".len()..b"https".len() + tail.len()].copy_from_slice(tail);
let out = &final_path_buf[..url.len() - b"ssh".len() + b"https".len()];
return Some(out);
}
if Dependency::is_scp_like_path(url) {
const PREFIX: &[u8] = b"https://";
final_path_buf[..PREFIX.len()].copy_from_slice(PREFIX);
let rest = &mut final_path_buf[PREFIX.len()..];
let colon_index = strings::index_of_char(url, b':');
if let Some(colon) = colon_index {
let colon = colon as usize;
if let Some(tld) = host_tld(&url[..colon]) {
rest[..colon].copy_from_slice(&url[..colon]);
rest[colon..colon + tld.len()].copy_from_slice(tld);
rest[colon + tld.len()] = b'/';
rest[colon + tld.len() + 1..colon + tld.len() + 1 + (url.len() - colon - 1)]
.copy_from_slice(&url[colon + 1..]);
let out = &final_path_buf[..url.len() + PREFIX.len() + tld.len()];
return Some(out);
}
}
rest[..url.len()].copy_from_slice(url);
if let Some(colon) = colon_index {
rest[colon as usize] = b'/';
}
return Some(&final_path_buf[..url.len() + PREFIX.len()]);
}
None
}
fn download(
env: &bun_dotenv::Map,
log: &mut bun_ast::Log,
cache_dir: bun_sys::Fd,
task_id: crate::package_manager_task::Id,
name: &[u8],
url: &[u8],
attempt: u8,
) -> Result<bun_sys::Dir, Error> {
bun_analytics::features::git_dependencies
.fetch_add(1, std::sync::atomic::Ordering::Relaxed);
let folder_name_buf = TlBufs::folder_name_buf();
let folder_name = {
use std::io::Write;
let total = folder_name_buf.len();
let mut cursor = &mut folder_name_buf[..];
write!(
&mut cursor,
"{}.git\0",
bun_core::fmt::hex_int_lower::<16>(task_id.get())
)
.map_err(|_| err!("NoSpaceLeft"))?;
let written = total - cursor.len() - 1;
bun_core::ZStr::from_buf(&folder_name_buf[..], written)
};
match bun_sys::Dir::borrow(&cache_dir).open_dir_z(folder_name) {
Ok(dir) => {
let path = Path::resolve_path::join_abs_string::<Path::platform::Auto>(
&PackageManager::get().cache_directory_path,
&[folder_name.as_bytes()],
);
if let Err(err) = exec(env, &[b"git", b"-C", path, b"fetch", b"--quiet"]) {
log.add_error_fmt(
None,
bun_ast::Loc::EMPTY,
format_args!("\"git fetch\" for \"{}\" failed", BStr::new(name)),
);
return Err(err);
}
Ok(dir)
}
Err(not_found) => {
if not_found != err!("ENOENT") {
return Err(not_found);
}
let staging = CacheStaging::new(cache_dir)?;
if let Err(err) = exec(
env,
&[
b"git",
b"clone",
b"-c",
b"core.longpaths=true",
b"--quiet",
b"--bare",
url,
staging.tmp_path(),
],
) {
staging.discard();
if err == err!("RepositoryNotFound") || attempt > 1 {
log.add_error_fmt(
None,
bun_ast::Loc::EMPTY,
format_args!("\"git clone\" for \"{}\" failed", BStr::new(name)),
);
}
return Err(err);
}
staging.publish(log, name, folder_name.as_bytes())
}
}
}
fn find_commit(
env: &mut bun_dotenv::Loader,
log: &mut bun_ast::Log,
repo_dir: bun_sys::Fd,
name: &[u8],
committish: &[u8],
task_id: crate::package_manager_task::Id,
) -> Result<Vec<u8>, Error> {
let folder_name_buf = TlBufs::folder_name_buf();
let folder_name = {
use std::io::Write;
let total = folder_name_buf.len();
let mut cursor = &mut folder_name_buf[..];
write!(
&mut cursor,
"{}.git",
bun_core::fmt::hex_int_lower::<16>(task_id.get())
)
.map_err(|_| err!("NoSpaceLeft"))?;
let written = total - cursor.len();
&folder_name_buf[..written]
};
let path = Path::resolve_path::join_abs_string::<Path::platform::Auto>(
&PackageManager::get().cache_directory_path,
&[folder_name],
);
let _ = repo_dir;
let shared = SharedEnv::get(env);
let argv_with: [&[u8]; 8] = [
b"git",
b"-C",
path,
b"log",
b"--format=%H",
b"-1",
b"--end-of-options",
committish,
];
let argv_without: [&[u8]; 6] = [b"git", b"-C", path, b"log", b"--format=%H", b"-1"];
let argv: &[&[u8]] = if !committish.is_empty() {
&argv_with
} else {
&argv_without
};
let out = match exec(shared, argv) {
Ok(v) => v,
Err(err) => {
log.add_error_fmt(
None,
bun_ast::Loc::EMPTY,
format_args!(
"no commit matching \"{}\" found for \"{}\" (but repository exists)",
BStr::new(committish),
BStr::new(name)
),
);
return Err(err);
}
};
Ok(strings::trim(&out, b" \t\r\n").to_vec())
}
fn checkout(
env: &bun_dotenv::Map,
log: &mut bun_ast::Log,
cache_dir: bun_sys::Fd,
repo_dir: bun_sys::Fd,
name: &[u8],
url: &[u8],
resolved: &[u8],
) -> Result<ExtractData, Error> {
bun_analytics::features::git_dependencies
.fetch_add(1, std::sync::atomic::Ordering::Relaxed);
if !is_safe_resolved_tag(resolved) {
log.add_error_fmt(
None,
bun_ast::Loc::EMPTY,
format_args!(
"invalid git commit \"{}\" for \"{}\"",
BStr::new(resolved),
BStr::new(name)
),
);
return Err(err!("InstallFailed"));
}
let folder_name_buf = TlBufs::folder_name_buf();
let folder_name = crate::package_manager_real::cached_git_folder_name_print(
&mut folder_name_buf[..],
resolved,
None,
)
.as_bytes();
let package_dir = 'brk: {
match bun_sys::Dir::borrow(&cache_dir).open_at(folder_name) {
Ok(dir) => {
if bun_sys::exists_at(dir.fd(), bun_core::zstr!(".bun-tag")) {
break 'brk dir;
}
dir.close();
}
Err(err) if err.get_errno() == bun_sys::E::ENOENT => {}
Err(err) => return Err(Error::from(err)),
}
let repo_path = bun_sys::get_fd_path(
repo_dir,
TlBufs::final_path_buf(),
)?;
let staging = CacheStaging::new(cache_dir)?;
if let Err(err) = exec(
env,
&[
b"git",
b"clone",
b"-c",
b"core.longpaths=true",
b"--quiet",
b"--no-checkout",
repo_path,
staging.tmp_path(),
],
) {
staging.discard();
log.add_error_fmt(
None,
bun_ast::Loc::EMPTY,
format_args!("\"git clone\" for \"{}\" failed", BStr::new(name)),
);
return Err(err);
}
if let Err(err) = exec(
env,
&[
b"git",
b"-C",
staging.tmp_path(),
b"checkout",
b"--quiet",
resolved,
],
) {
staging.discard();
log.add_error_fmt(
None,
bun_ast::Loc::EMPTY,
format_args!("\"git checkout\" for \"{}\" failed", BStr::new(name)),
);
return Err(err);
}
{
let dir = match bun_sys::Dir::borrow(&cache_dir).open_at(staging.tmp_name()) {
Ok(dir) => dir,
Err(err) => {
staging.discard();
return Err(Error::from(err));
}
};
let _ = dir.delete_tree(b".git");
let _ = dir.delete_file_z(bun_core::zstr!("node_modules"));
let _ = dir.delete_tree(b".bun-tag");
let tagged = bun_sys::File::openat(
dir.fd(),
bun_core::zstr!(".bun-tag"),
bun_sys::O::WRONLY
| bun_sys::O::CREAT
| bun_sys::O::EXCL
| if cfg!(windows) {
0
} else {
bun_sys::O::NOFOLLOW
},
0o664,
)
.and_then(|f| f.write_all(resolved));
dir.close();
if let Err(err) = tagged {
staging.discard();
log.add_error_fmt(
None,
bun_ast::Loc::EMPTY,
format_args!(
"writing \".bun-tag\" for \"{}\" failed: {}",
BStr::new(name),
BStr::new(err.name())
),
);
return Err(err!("InstallFailed"));
}
}
staging.publish(log, name, folder_name)?
};
let (json_file, json_buf) =
match bun_sys::File::read_file_from(package_dir.fd(), b"package.json") {
Ok(v) => v,
Err(err) => {
if err == err!("ENOENT") {
package_dir.close();
return Ok(ExtractData {
url: url.into(),
resolved: resolved.into(),
..Default::default()
});
}
log.add_error_fmt(
None,
bun_ast::Loc::EMPTY,
format_args!(
"\"package.json\" for \"{}\" failed to open: {}",
BStr::new(name),
err.name()
),
);
package_dir.close();
return Err(err!("InstallFailed"));
}
};
let json_path = match json_file.get_path(TlBufs::json_path_buf()) {
Ok(p) => p,
Err(err) => {
log.add_error_fmt(
None,
bun_ast::Loc::EMPTY,
format_args!(
"\"package.json\" for \"{}\" failed to resolve: {}",
BStr::new(name),
err.name()
),
);
let _ = json_file.close(); package_dir.close();
return Err(err!("InstallFailed"));
}
};
let _ = json_file.close(); package_dir.close();
let ret_json_path = bun_resolver::fs::FileSystem::instance()
.dirname_store()
.append(json_path)?;
Ok(ExtractData {
url: url.into(),
resolved: resolved.into(),
json: Some(Install::ExtractDataJson {
path: ret_json_path.into(),
buf: json_buf,
}),
..Default::default()
})
}
}
pub struct StorePathFormatter<'a> {
repo: &'a Repository,
label: &'a str,
string_buf: &'a [u8],
}
impl<'a> fmt::Display for StorePathFormatter<'a> {
fn fmt(&self, writer: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(writer, "{}", Install::fmt_store_path(self.label.as_bytes()))?;
if !self.repo.owner.is_empty() {
write!(
writer,
"{}",
self.repo.owner.fmt_store_path(self.string_buf)
)?;
writer.write_str("+")?;
} else if Dependency::is_scp_like_path(self.repo.repo.slice(self.string_buf)) {
writer.write_str("ssh++")?;
}
write!(writer, "{}", self.repo.repo.fmt_store_path(self.string_buf))?;
if !self.repo.resolved.is_empty() {
writer.write_str("+")?; let mut resolved = self.repo.resolved.slice(self.string_buf);
if let Some(i) = strings::last_index_of_char(resolved, b'-') {
resolved = &resolved[i + 1..];
}
write!(writer, "{}", Install::fmt_store_path(resolved))?;
} else if !self.repo.committish.is_empty() {
writer.write_str("+")?; write!(
writer,
"{}",
self.repo.committish.fmt_store_path(self.string_buf)
)?;
}
Ok(())
}
}
pub struct Formatter<'a> {
label: &'a str,
buf: &'a [u8],
repository: &'a Repository,
}
impl<'a> fmt::Display for Formatter<'a> {
fn fmt(&self, writer: &mut fmt::Formatter<'_>) -> fmt::Result {
#[cfg(debug_assertions)]
debug_assert!(!self.label.is_empty());
writer.write_str(self.label)?;
let repo = self.repository.repo.slice(self.buf);
if !self.repository.owner.is_empty() {
write!(
writer,
"{}",
BStr::new(self.repository.owner.slice(self.buf))
)?;
writer.write_str("/")?;
} else if Dependency::is_scp_like_path(repo) {
writer.write_str("ssh://")?;
}
write!(writer, "{}", BStr::new(repo))?;
if !self.repository.resolved.is_empty() {
writer.write_str("#")?;
let mut resolved = self.repository.resolved.slice(self.buf);
if let Some(i) = strings::last_index_of_char(resolved, b'-') {
resolved = &resolved[i + 1..];
}
write!(writer, "{}", BStr::new(resolved))?;
} else if !self.repository.committish.is_empty() {
writer.write_str("#")?;
write!(
writer,
"{}",
BStr::new(self.repository.committish.slice(self.buf))
)?;
}
Ok(())
}
}