feat(vm): add vm exec command with workspace dirty detection

Introduces three interconnected features for persistent VM workflows:

1. `banger vm exec <vm> -- <cmd>`: runs a command in the prepared
   workspace, automatically cd-ing into the guest path and wrapping
   via `mise exec --` so mise-managed tools are on PATH. Falls back
   to a plain exec when mise isn't available. Exit code propagates
   verbatim.

2. Workspace persistence: workspace.prepare now stores the guest path,
   host source path, and HEAD commit into a new `workspace_json` column
   on the vms table (migration 3). This state survives daemon restarts
   and informs both dirty-checking and auto-prepare.

3. Dirty detection: `vm exec` compares the stored HEAD commit against
   the current host repo HEAD. When stale it warns and, with
   --auto-prepare, re-syncs the workspace before running.

Also:
- WORKSPACE column added to `banger ps` / `vm list`
- `banger vm` quick reference updated with `vm exec` entry
This commit is contained in:
Thales Maciel 2026-04-26 23:53:45 -03:00
parent c8637b0fe4
commit d59425adb9
No known key found for this signature in database
GPG key ID: 33112E6833C34679
8 changed files with 260 additions and 13 deletions

View file

@ -39,6 +39,7 @@ Quick reference:
banger vm run ./repo -- make test ship a repo, run a command, exit
banger vm create --name dev persistent VM; pair with 'vm ssh'
banger vm ssh <name> open a shell in a running VM
banger vm exec <name> -- make test run a command in the workspace with mise toolchain
banger vm stop <name> | vm restart graceful lifecycle
banger vm kill <name> force-kill if stop hangs
banger vm delete <name> stop + remove disks
@ -49,7 +50,7 @@ Quick reference:
Example: strings.TrimSpace(`
banger vm run -- uname -a
banger vm run ./project -- npm test
banger vm create --name agent && banger vm ssh agent
banger vm create --name dev && banger vm workspace prepare dev . && banger vm exec dev -- make test
`),
RunE: helpNoArgs,
}
@ -66,6 +67,7 @@ Quick reference:
d.newVMPruneCommand(),
d.newVMSetCommand(),
d.newVMSSHCommand(),
d.newVMExecCommand(),
d.newVMWorkspaceCommand(),
d.newVMLogsCommand(),
d.newVMStatsCommand(),