From 187a245aea3271856643518ff8e1dd0f2e1ea7cc Mon Sep 17 00:00:00 2001 From: Juan Gomez <1766933+judavi@users.noreply.github.com> Date: Thu, 26 Mar 2026 08:06:50 +0000 Subject: [PATCH] system-reinstall-bootc: add progress indication and fix implicit pull bug Add a spinner during the capability check step and a status message before the long-running installation, addressing issue #1270. Also fix a latent bug: `bootc_has_clean` was called inside `reinstall_command`, which runs `podman run ` and would implicitly pull the image if not already cached locally. The flow is now three clearly distinct phases: 1. Pull phase: `pull_if_not_present` explicitly pulls the image first, with a message so the user knows what is happening. 2. Capability check phase: `bootc_has_clean` is called after the image is guaranteed to be local, with a spinner labeled "Checking image capabilities...". 3. Install phase: `reinstall_command` is now a pure command builder that takes `has_clean: bool` as a parameter with no I/O side effects. Fixes: https://github.com/bootc-dev/bootc/issues/1270 Signed-off-by: Juan Gomez <1766933+judavi@users.noreply.github.com> --- Cargo.lock | 1 + crates/system-reinstall-bootc/Cargo.toml | 3 +++ crates/system-reinstall-bootc/src/main.rs | 22 +++++++++++++++++++-- crates/system-reinstall-bootc/src/podman.rs | 10 +++++++--- 4 files changed, 31 insertions(+), 5 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 140a9e70a..5a745da2b 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -2942,6 +2942,7 @@ dependencies = [ "crossterm", "dialoguer", "fn-error-context", + "indicatif 0.18.3", "indoc", "log", "openssh-keys", diff --git a/crates/system-reinstall-bootc/Cargo.toml b/crates/system-reinstall-bootc/Cargo.toml index 921698e54..eef813543 100644 --- a/crates/system-reinstall-bootc/Cargo.toml +++ b/crates/system-reinstall-bootc/Cargo.toml @@ -32,6 +32,9 @@ tempfile = { workspace = true } tracing = { workspace = true } uzers = { workspace = true } +# Workspace dependencies (additional) +indicatif = { workspace = true } + # Crate-specific dependencies crossterm = "0.29.0" dialoguer = "0.12.0" diff --git a/crates/system-reinstall-bootc/src/main.rs b/crates/system-reinstall-bootc/src/main.rs index 3edf8cd49..d4626456c 100644 --- a/crates/system-reinstall-bootc/src/main.rs +++ b/crates/system-reinstall-bootc/src/main.rs @@ -5,6 +5,7 @@ use bootc_utils::CommandRunExt; use clap::Parser; use fn_error_context::context; use rustix::process::getuid; +use std::time::Duration; mod btrfs; mod config; @@ -54,11 +55,24 @@ fn run() -> Result<()> { podman::ensure_podman_installed()?; - //pull image early so it can be inspected, e.g. to check for cloud-init + // Pull phase: explicitly pull the image before any other operations that use it. + // This ensures no implicit pulls happen in later steps (e.g. capability check). podman::pull_if_not_present(&opts.image)?; println!(); + // Capability check phase: run after the image is guaranteed to be present locally. + let spinner = indicatif::ProgressBar::new_spinner(); + spinner.set_style( + indicatif::ProgressStyle::default_spinner() + .template("{spinner} {msg}") + .expect("Failed to parse spinner template"), + ); + spinner.set_message("Checking image capabilities..."); + spinner.enable_steady_tick(Duration::from_millis(150)); + let has_clean = podman::bootc_has_clean(&opts.image)?; + spinner.finish_and_clear(); + let ssh_key_file = tempfile::NamedTempFile::new()?; let ssh_key_file_path = ssh_key_file .path() @@ -71,7 +85,8 @@ fn run() -> Result<()> { prompt::mount_warning()?; - let mut reinstall_podman_command = podman::reinstall_command(&opts, ssh_key_file_path)?; + let mut reinstall_podman_command = + podman::reinstall_command(&opts, ssh_key_file_path, has_clean)?; println!(); println!("Going to run command:"); @@ -85,6 +100,9 @@ fn run() -> Result<()> { prompt::temporary_developer_protection_prompt()?; + println!("Starting bootc installation. This may take several minutes..."); + println!(); + reinstall_podman_command .run_inherited_with_cmd_context() .context("running reinstall command")?; diff --git a/crates/system-reinstall-bootc/src/podman.rs b/crates/system-reinstall-bootc/src/podman.rs index 60dfefb52..7a9e104f8 100644 --- a/crates/system-reinstall-bootc/src/podman.rs +++ b/crates/system-reinstall-bootc/src/podman.rs @@ -8,7 +8,7 @@ use std::process::Command; use which::which; #[context("bootc_has_clean")] -fn bootc_has_clean(image: &str) -> Result { +pub(crate) fn bootc_has_clean(image: &str) -> Result { let output = Command::new("podman") .args([ "run", @@ -25,7 +25,11 @@ fn bootc_has_clean(image: &str) -> Result { } #[context("reinstall_command")] -pub(crate) fn reinstall_command(opts: &ReinstallOpts, ssh_key_file: &str) -> Result { +pub(crate) fn reinstall_command( + opts: &ReinstallOpts, + ssh_key_file: &str, + has_clean: bool, +) -> Result { let mut podman_command_and_args = [ // We use podman to run the bootc container. This might change in the future to remove the // podman dependency. @@ -80,7 +84,7 @@ pub(crate) fn reinstall_command(opts: &ReinstallOpts, ssh_key_file: &str) -> Res // bootc system for the first time. // This only happens if the bootc version in the image >= 1.1.8 (this is when the cleanup // feature was introduced) - if bootc_has_clean(&opts.image)? { + if has_clean { bootc_command_and_args.push("--cleanup".to_string()); }