chore: update bundled packages only after cluster launch (#849)

This commit is contained in:
LynithDev 2026-09-18 00:09:53 +02:00 • committed by GitHub
parent 43108bc8bc
commit c7f3fdc52b
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
6 changed files with 6 additions and 180 deletions

View file

@ -1373,13 +1373,9 @@ impl Actions {
spawn_forever(async move {
let synced = match state.bundles.sync(&state.services.content()).await {
Ok(_) => true,
Err(err) => {
tracing::error!("bundle catalog sync failed: {err:#}");
false
}
};
if let Err(err) = state.bundles.sync(&state.services.content()).await {
tracing::error!("bundle catalog sync failed: {err:#}");
}
if let Err(err) = oneclient_core::clusters::apply_remote_migrations(&state).await {
tracing::error!("cluster migrations failed: {err:#}");
}
@ -1387,37 +1383,6 @@ impl Actions {
tracing::error!("bundle cluster provisioning failed: {err:#}");
}
// One grouped session for the batch so all downloads surface as a
// single notification `detach` lets it be converted in place
let session = oneclient_events::GroupedProgressSession::start(
&state.services.events,
"Updating mods",
);
let changed = oneclient_content::bundles::sync_all_cluster_bundles(
state.bundles.as_ref(),
&state.services.content(),
Some(&session),
)
.await;
let session_id = session.detach();
if synced
&& let Ok(clusters) = state.clusters.list().await
{
let unsettled: std::collections::HashSet<i64> = changed
.iter()
.filter(|(_, result)| !result.settled())
.map(|(cluster_id, _)| *cluster_id)
.collect();
actions.record_bundle_checks(
clusters
.iter()
.map(|cluster| cluster.id)
.filter(|cluster_id| !unsettled.contains(cluster_id)),
);
}
actions
.station
.clone()
@ -1425,12 +1390,6 @@ impl Actions {
.launcher
.syncing_bundles = false;
super::invalidate_cluster_queries().await;
let spec = crate::install::combined_cluster_update_spec(&changed, &state.services).await;
actions.with_engine(|app| {
app.notifications
.finish_grouped_as_actions(&mut app.inbox, session_id, spec);
});
});
}
@ -1451,8 +1410,6 @@ impl Actions {
return;
}
let mode = self.cluster_update_mode(state, cluster_id).await;
self.station
.clone()
.write_channel(AppChannel::Launcher)
@ -1486,15 +1443,8 @@ impl Actions {
.launcher
.syncing_bundles = false;
// No prompt for bundles the package manager's markers carry them instead
if !matches!(mode, PackageUpdateMode::Automatic) {
tracing::debug!(cluster_id, "bundle updates left for the user to apply");
if synced {
self.record_bundle_checks([cluster_id]);
}
return;
}
// Bundle content is forced, so the update mode never gates it and the
// modal never lists it
let result = match oneclient_core::apply_bundle_updates(
cluster_id,
state.bundles.as_ref(),
@ -1533,12 +1483,6 @@ impl Actions {
}
super::invalidate_cluster_queries().await;
if let Some(spec) =
crate::install::cluster_update_notification(cluster_id, &result, &state.services).await
{
self.push_notification(spec);
}
}
/// The check runs and the cache is written whatever the mode says keeping

View file

@ -168,60 +168,6 @@ pub async fn cluster_update_notification(
})
}
/// All clusters share one "View changes" action so the notification keeps exactly two buttons
pub async fn combined_cluster_update_spec(
changed: &[(i64, oneclient_core::ApplyBundleUpdatesResult)],
services: &oneclient_core::LauncherServices,
) -> Option<NotificationSpec> {
let mut summaries = Vec::new();
let mut total_changes = 0usize;
for (cluster_id, result) in changed {
if let Some(summary) = cluster_update_summary(*cluster_id, result, services).await {
total_changes += summary.total();
summaries.push(summary);
}
}
if summaries.is_empty() {
return None;
}
let cluster_count = summaries.len();
let (title, body) = if total_changes == 0 {
let offers: usize = summaries.iter().map(|s| s.optional.len()).sum();
(
"Optional mods available",
format!(
"{offers} optional mod{} across {cluster_count} cluster{}",
if offers == 1 { "" } else { "s" },
if cluster_count == 1 { "" } else { "s" }
),
)
} else {
(
"Mods updated",
format!(
"{total_changes} package{} updated across {cluster_count} cluster{}",
if total_changes == 1 { "" } else { "s" },
if cluster_count == 1 { "" } else { "s" }
),
)
};
Some(NotificationSpec {
title: title.to_string(),
body,
level: Level::Info,
icon: Some(IconType::DownloadCloud02),
progress: None,
actions: vec![NotificationAction {
label: "View changes".to_string(),
kind: NotificationActionKind::OpenClusterUpdate(summaries),
}],
})
}
pub async fn pending_optional_group(
cluster_id: i64,
pending: &[oneclient_core::PendingOptionalMod],

View file

@ -5,7 +5,6 @@ mod manifest;
mod optional;
mod overrides;
mod polymrpack;
mod runtime;
mod types;
mod updates;
@ -25,7 +24,6 @@ pub use optional::{
PendingOptionalMod, pending_optional_mods, resolve_optional_mods, skip_optional_mods,
};
pub use manifest::BundleManifest as RemoteBundleManifest;
pub use runtime::{is_bundle_syncing, sync_all_cluster_bundles};
pub use types::{
ApplyBundleUpdatesResult, BundleArchive, BundleFile, BundleFileKind, BundleOptionalPackage,
BundleManifest, BundlePackageAddition, BundlePackageRemoval, BundlePackageUpdate,

View file

@ -1,34 +0,0 @@
use std::sync::atomic::{AtomicBool, Ordering};
use crate::bundles::manager::BundlesManager;
use crate::bundles::ApplyBundleUpdatesResult;
use crate::bundles::updates::apply_bundle_updates_for_all_clusters;
use oneclient_events::GroupedProgressSession;
use crate::ctx::ContentCtx;
static BUNDLE_SYNCING: AtomicBool = AtomicBool::new(false);
pub fn is_bundle_syncing() -> bool {
BUNDLE_SYNCING.load(Ordering::Relaxed)
}
/// `session` when given groups every cluster's downloads into one notification
#[tracing::instrument(skip_all)]
pub async fn sync_all_cluster_bundles(
bundles: &BundlesManager,
ctx: &ContentCtx,
session: Option<&GroupedProgressSession>,
) -> Vec<(i64, ApplyBundleUpdatesResult)> {
tracing::info!("syncing all cluster bundles");
BUNDLE_SYNCING.store(true, Ordering::Relaxed);
let changed = match apply_bundle_updates_for_all_clusters(bundles, ctx, session).await {
Ok(changed) => changed,
Err(err) => {
tracing::error!("failed to apply bundle updates for clusters: {err:#}");
Vec::new()
}
};
BUNDLE_SYNCING.store(false, Ordering::Relaxed);
changed
}

View file

@ -902,34 +902,6 @@ pub async fn get_bundles_with_update_status(
Ok(results)
}
#[tracing::instrument(level = "debug", skip_all)]
pub async fn apply_bundle_updates_for_all_clusters(
bundles: &BundlesManager,
ctx: &ContentCtx,
session: Option<&oneclient_events::GroupedProgressSession>,
) -> ContentResult<Vec<(i64, ApplyBundleUpdatesResult)>> {
let mut changed = Vec::new();
for cluster in cluster_dao::list_all(&ctx.db).await? {
match apply_bundle_updates_with(cluster.id, bundles, ctx, session, None).await {
Ok(result) => {
if !result.updates_applied.is_empty()
|| !result.additions_applied.is_empty()
|| !result.removals_applied.is_empty()
|| !result.optional_available.is_empty()
{
changed.push((cluster.id, result));
}
}
Err(err) => tracing::warn!(
cluster_id = cluster.id,
error = %err,
"bundle update apply failed for cluster"
),
}
}
Ok(changed)
}
fn bundle_package_key(
bundle_pkg: &BundleTrackedArtifactRow,
linked: &HashMap<String, &LinkedArtifactInfo>,

View file

@ -27,7 +27,7 @@ pub use oneclient_content::bundles::{
cluster_has_bundle_content, effective_enabled,
install_bundle,
install_cluster_bundles,
install_package_from_bundle, is_bundle_syncing, list_cluster_bundle_overrides,
install_package_from_bundle, list_cluster_bundle_overrides,
pending_optional_mods, resolve_optional_mods, skip_optional_mods, PendingOptionalMod,
set_bundle_package_enabled, set_bundle_package_opt_in, set_bundle_package_override,
set_bundle_package_overrides,