-
Notifications
You must be signed in to change notification settings - Fork 0
feat(update): add overall progress bar with per-task spinners #11
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from 2 commits
1499f08
1f8cb40
c3a0eb1
354b349
5ef3c18
54a3297
7256a30
792d821
258a85e
9c983f0
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -207,12 +207,25 @@ async fn do_update(workspace_root: &Path) -> miette::Result<()> { | |
| return Ok(()); | ||
| } | ||
|
|
||
| let total = projects.len() as u64; | ||
| let mp = MultiProgress::new(); | ||
| let style = ProgressStyle::default_spinner() | ||
| .template("{spinner:.green} {msg}") | ||
|
|
||
| // Top-level progress bar showing overall completion | ||
| let overall_style = ProgressStyle::default_bar() | ||
| .template("[{bar:30.cyan/dim}] {pos}/{len} {msg}") | ||
| .expect("valid template") | ||
| .progress_chars("##-"); | ||
| let overall = mp.add(ProgressBar::new(total)); | ||
| overall.set_style(overall_style); | ||
| overall.set_message("updating..."); | ||
|
|
||
| // Style for per-task spinners (inserted below the overall bar) | ||
| let spinner_style = ProgressStyle::default_spinner() | ||
| .template(" {spinner:.green} {msg}") | ||
| .expect("valid template"); | ||
|
|
||
| let semaphore = std::sync::Arc::new(Semaphore::new(MAX_CONCURRENT_GIT)); | ||
| let overall = std::sync::Arc::new(overall); | ||
| let mut handles = Vec::new(); | ||
|
|
||
| for project in &projects { | ||
|
|
@@ -221,8 +234,9 @@ async fn do_update(workspace_root: &Path) -> miette::Result<()> { | |
| let clone_url = manifest.project_clone_url(project).ok(); | ||
| let project_name = project.name.clone(); | ||
| let sem = semaphore.clone(); | ||
| let pb = mp.add(ProgressBar::new_spinner()); | ||
| pb.set_style(style.clone()); | ||
| let overall = overall.clone(); | ||
| let pb = mp.insert_after(&overall, ProgressBar::new_spinner()); | ||
| pb.set_style(spinner_style.clone()); | ||
|
|
||
| let handle = tokio::spawn(async move { | ||
| let _permit = sem.acquire().await.expect("semaphore closed"); | ||
|
|
@@ -249,9 +263,16 @@ async fn do_update(workspace_root: &Path) -> miette::Result<()> { | |
| pb.set_message(format!("{project_name}: initializing...")); | ||
| Git::init_and_fetch(url, &project_path, revision.as_deref()).await | ||
| } else { | ||
| // Clone | ||
| // Clone — fallback to init+fetch if the directory was | ||
| // created by a concurrent sibling clone in the meantime. | ||
| pb.set_message(format!("{project_name}: cloning...")); | ||
| Git::clone(url, &project_path, revision.as_deref()).await | ||
| let clone_result = Git::clone(url, &project_path, revision.as_deref()).await; | ||
| if clone_result.is_err() && project_path.exists() { | ||
| pb.set_message(format!("{project_name}: initializing (fallback)...")); | ||
| Git::init_and_fetch(url, &project_path, revision.as_deref()).await | ||
| } else { | ||
| clone_result | ||
| } | ||
|
Comment on lines
+320
to
+334
|
||
| } | ||
| } else { | ||
| Err(east_vcs::error::VcsError::GitFailed { | ||
|
|
@@ -260,10 +281,12 @@ async fn do_update(workspace_root: &Path) -> miette::Result<()> { | |
| }) | ||
| }; | ||
|
|
||
| // Update UI: remove spinner on success, keep failure visible | ||
| match &result { | ||
| Ok(()) => pb.finish_with_message(format!("{project_name}: done")), | ||
| Ok(()) => pb.finish_and_clear(), | ||
| Err(e) => pb.finish_with_message(format!("{project_name}: FAILED ({e})")), | ||
| } | ||
| overall.inc(1); | ||
| result | ||
| }); | ||
| handles.push((project.name.clone(), handle)); | ||
|
|
@@ -277,6 +300,7 @@ async fn do_update(workspace_root: &Path) -> miette::Result<()> { | |
| Err(e) => errors.push(format!("{name}: task panicked: {e}")), | ||
| } | ||
| } | ||
| overall.finish_and_clear(); | ||
|
|
||
| if errors.is_empty() { | ||
| println!("updated {} projects", projects.len()); | ||
|
|
||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Per-task
ProgressBar::new_spinner()never callstick()orenable_steady_tick(...), so the spinner glyph will stay static (and may not render as intended) until some other redraw happens. Consider enabling a steady tick for these spinners (bounded byMAX_CONCURRENT_GIT), or explicitly ticking on state updates so the UI matches the intended “spinner” behavior.