Skip to content

feat: vm stop/delete --all for bulk cleanup#12

Open
jasonhernandez wants to merge 1 commit into
aljoscha:mainfrom
jasonhernandez:feat/vm-delete-all
Open

feat: vm stop/delete --all for bulk cleanup#12
jasonhernandez wants to merge 1 commit into
aljoscha:mainfrom
jasonhernandez:feat/vm-delete-all

Conversation

@jasonhernandez
Copy link
Copy Markdown
Collaborator

Summary

Add --all flag to ember vm stop and ember vm delete for bulk operations.

  • ember vm stop --all — stops all running VMs
  • ember vm delete --all — deletes all VMs (requires --force if any are running)
  • name becomes optional when --all is used (required_unless_present)
  • Running VMs are stopped before deletion when --force is used

Files changed

File What
src/cli/vm.rs --all flag on StopArgs/DeleteArgs, stop_all()/delete_all() functions

Test plan

  • 26 unit tests pass, cargo build clean, cargo clippy clean
  • Manual: ember vm stop --all stops all running VMs
  • Manual: ember vm delete --all --force cleans up everything

🤖 Generated with Claude Code

ember vm stop --all          # stop all running VMs
ember vm stop --all --force  # SIGKILL all running VMs
ember vm delete --all --force # stop + delete every VM

Useful for cleanup and for ending all VMs (including non-pool
control agent VMs that pool destroy doesn't touch).

Co-Authored-By: Claude <noreply@anthropic.com>
@jasonhernandez jasonhernandez marked this pull request as ready for review April 15, 2026 00:03
@jasonhernandez jasonhernandez requested a review from aljoscha April 15, 2026 00:03
Copy link
Copy Markdown
Owner

@aljoscha aljoscha left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Nice, clean feature — reuses existing logic well and the CLI ergonomics are solid. A few things to address before merge, mostly around project conventions and edge-case UX.

Also: the PR mentions 26 unit tests pass, but there's no test coverage for the new --all paths. Please add an integration test that exercises the bulk stop/delete flow end-to-end (create multiple VMs, stop --all, verify states, delete --all, verify cleanup).

Comment thread src/cli/vm.rs
return stop_all(args.force, state_dir);
}

let name = args.name.as_deref().unwrap();
Copy link
Copy Markdown
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Project convention (CLAUDE.md): "Prefer explicit error handling. Use ? for propagation, not .unwrap()." Clap's required_unless_present prevents this from panicking in practice, but an explicit error is more defensive and consistent:

let name = args.name.as_deref()
    .context("VM name is required when --all is not used")?;

Same applies to the identical .unwrap() in delete() below.

Comment thread src/cli/vm.rs

println!("Stopping {} VMs...", targets.len());
for metadata in &targets {
let stop_args = StopArgs {
Copy link
Copy Markdown
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Constructing a StopArgs just to call back into stop() is a bit roundabout. Consider extracting the single-VM logic into a helper like stop_one(name: &str, force: bool, state_dir: &Path) that both stop() and stop_all() can call directly. Same pattern applies to delete_all below.

Not blocking, but it would simplify both call sites and avoid synthesizing args structs.

Comment thread src/cli/vm.rs
}
}

Ok(())
Copy link
Copy Markdown
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

No summary line after the loop completes. When some VMs fail (warnings printed to stderr), the user has no way to tell how many succeeded at a glance. Consider tracking success/failure counts and printing a final line:

println!("Stopped {ok} of {} VMs.", targets.len());

Same applies to delete_all.

Comment thread src/cli/vm.rs

println!("Deleting {} VMs...", vms.len());
for metadata in &vms {
let delete_args = DeleteArgs {
Copy link
Copy Markdown
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ordering issue with fork hierarchies: vm::list() returns VMs in arbitrary order. If a parent VM is visited before its fork child, delete() with --force will cascade-delete the child via force_delete_vm. When the loop later reaches that already-deleted child, vm::load fails and the user sees a spurious "warning: failed to delete 'child'" message.

This works correctly (no data loss), but the warnings are confusing. Two options:

  1. Sort VMs children-first (leaves before parents) so children are deleted before their parents.
  2. Collect successfully-deleted names in a HashSet and skip VMs that were already cascade-deleted.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants