Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
114 changes: 74 additions & 40 deletions src/install/PackageManager/runTasks.rs
Original file line number Diff line number Diff line change
Expand Up @@ -542,16 +542,7 @@ fn run_tasks_erased(
);
}

if manager.subcommand != Subcommand::Remove {
for request in manager.update_requests.iter_mut() {
if strings::eql(request.name, name) {
request.failed = true;
manager.options.do_.remove(Do::SAVE_LOCKFILE);
manager.options.do_.remove(Do::SAVE_YARN_LOCK);
manager.options.do_.remove(Do::INSTALL_PACKAGES);
}
}
}
fail_update_requests(manager, task.task_id, name, None);
}

continue;
Expand Down Expand Up @@ -599,16 +590,7 @@ fn run_tasks_erased(
response.status_code,
);
}
if manager.subcommand != Subcommand::Remove {
for request in manager.update_requests.iter_mut() {
if strings::eql(request.name, name) {
request.failed = true;
manager.options.do_.remove(Do::SAVE_LOCKFILE);
manager.options.do_.remove(Do::SAVE_YARN_LOCK);
manager.options.do_.remove(Do::INSTALL_PACKAGES);
}
}
}
fail_update_requests(manager, task.task_id, name, None);

continue;
}
Expand Down Expand Up @@ -853,16 +835,12 @@ fn run_tasks_erased(
.fmt(&manager.lockfile.buffers.string_bytes, PathSep::Auto,),
);
}
if manager.subcommand != Subcommand::Remove {
for request in manager.update_requests.iter_mut() {
if strings::eql(request.name, extract.name.slice()) {
request.failed = true;
manager.options.do_.remove(Do::SAVE_LOCKFILE);
manager.options.do_.remove(Do::SAVE_YARN_LOCK);
manager.options.do_.remove(Do::INSTALL_PACKAGES);
}
}
}
fail_update_requests(
manager,
task.task_id,
extract.name.slice(),
Some(extract.dependency_id),
);

if let Some(removed) = manager.task_queue.remove(&task.task_id) {
drop(removed);
Expand Down Expand Up @@ -936,16 +914,12 @@ fn run_tasks_erased(
response.status_code,
);
}
if manager.subcommand != Subcommand::Remove {
for request in manager.update_requests.iter_mut() {
if strings::eql(request.name, extract.name.slice()) {
request.failed = true;
manager.options.do_.remove(Do::SAVE_LOCKFILE);
manager.options.do_.remove(Do::SAVE_YARN_LOCK);
manager.options.do_.remove(Do::INSTALL_PACKAGES);
}
}
}
fail_update_requests(
manager,
task.task_id,
extract.name.slice(),
Some(extract.dependency_id),
);

if let Some(removed) = manager.task_queue.remove(&task.task_id) {
drop(removed);
Expand Down Expand Up @@ -1946,6 +1920,66 @@ pub(crate) fn network_task_has_failed(this: &PackageManager, task_id: Task::Id)
.is_some_and(|e| e.failed)
}

/// `bun add` / `bun update <name>` exits 1 and saves nothing when a download for the request fails.
fn fail_update_requests(
this: &mut PackageManager,
task_id: Task::Id,
package_name: &[u8],
tarball_dependency_id: Option<DependencyID>,
) {
if this.subcommand == Subcommand::Remove {
return;
}
let lockfile = &*this.lockfile;
let string_buf = lockfile.buffers.string_bytes.as_slice();
let dependencies = lockfile.buffers.dependencies.as_slice();
let resolutions = lockfile.buffers.resolutions.as_slice();
let waiters = this.task_queue.get(&task_id).map_or(&[][..], Vec::as_slice);
Comment thread
robobun marked this conversation as resolved.
// An npm tarball task has no waiters: the dependencies it was for already resolved to its package.
let package_id = tarball_dependency_id
.and_then(|id| resolutions.get(id as usize).copied())
.filter(|&package_id| package_id != INVALID_PACKAGE_ID);
let was_for = |id: DependencyID| {
package_id.is_some_and(|package_id| resolutions.get(id as usize) == Some(&package_id))
|| waiters.iter().any(|waiter| {
matches!(
waiter,
bun_install::TaskCallbackContext::Dependency(waiting)
| bun_install::TaskCallbackContext::RootDependency(waiting) if *waiting == id
)
})
};
let pending = this.pending_filtered_write.as_deref();

let mut any_failed = false;
for request in this.update_requests.iter_mut() {
// A request names a package.json key, which an `npm:` alias or an override spells differently from `package_name`.
let names_its_dependency = lockfile
.workspaces_of_update_request(pending, this.workspace_name_hash, request)
.into_iter()
.any(|workspace_id| {
let lists = lockfile.packages.items_dependencies();
lists.get(workspace_id as usize).is_some_and(|list| {
(list.off..list.off + list.len).any(|id| {
dependencies
.get(id as usize)
.is_some_and(|dependency| request.matches(dependency, string_buf))
&& was_for(id)
})
})
});
if names_its_dependency || strings::eql(request.name, package_name) {
Comment thread
coderabbitai[bot] marked this conversation as resolved.
request.failed = true;
any_failed = true;
}
}
if any_failed {
this.options
.do_
.remove(Do::SAVE_LOCKFILE | Do::SAVE_YARN_LOCK | Do::INSTALL_PACKAGES);
Comment thread
robobun marked this conversation as resolved.
}
}

/// The first failed download in a `run_tasks` pass halves the number of
/// concurrent requests (down to the configured minimum).
fn throttle_after_network_error(manager: &PackageManager, has_network_error: &mut bool) {
Expand Down
26 changes: 16 additions & 10 deletions src/install/lockfile.rs
Original file line number Diff line number Diff line change
Expand Up @@ -950,6 +950,19 @@ impl Lockfile {
}
}

/// The workspaces whose dependency lists `request` names: the ones that received it under `--filter` / `-r`, else the cwd's.
pub(crate) fn workspaces_of_update_request(
&self,
pending: Option<&crate::package_manager_real::add_remove_with_filter::PendingWrite>,
workspace_name_hash: Option<PackageNameHash>,
request: &UpdateRequest,
) -> Vec<PackageID> {
match pending {
Some(pending) => pending.workspace_ids_receiving(self, request.name_hash),
None => vec![self.get_workspace_package_id(workspace_name_hash)],
}
}

/// Re-runnable: package_json_write_back binds again after re-deriving the declared columns.
#[cold]
#[inline(never)]
Expand All @@ -963,19 +976,12 @@ impl Lockfile {
let string_buf = self.buffers.string_bytes.as_slice();
let string_buf_ptr = bun_ptr::RawSlice::new(string_buf);
let slice = self.packages.slice();
let cwd_workspace = [self.get_workspace_package_id(workspace_name_hash)];

'request_updated: for update in updates.iter_mut() {
update.e_string = None;
let filtered: Vec<PackageID>;
let workspace_ids: &[PackageID] = match pending {
Some(pending) => {
filtered = pending.workspace_ids_receiving(self, update.name_hash);
&filtered
}
None => &cwd_workspace,
};
for &workspace_package_id in workspace_ids {
let workspace_ids =
self.workspaces_of_update_request(pending, workspace_name_hash, update);
for &workspace_package_id in &workspace_ids {
let dep_list = slice.items_dependencies()[workspace_package_id as usize];
let res_list = slice.items_resolutions()[workspace_package_id as usize];
let workspace_deps: &[Dependency] =
Expand Down
Loading
Loading