vm run: add -d/--detach + transparent tooling bootstrap
The mise tooling bootstrap was failing silently when --nat wasn't set: the VM came up, the user landed in ssh, and tools were missing with no obvious cause. Two coupled fixes: * `-d`/`--detach`: create + prep + bootstrap, exit without attaching to ssh. Reconnect later with `banger vm ssh <name>`. Rejects the ambiguous combos `-d --rm` and `-d -- <cmd>`. * NAT precondition: when the workspace has a .mise.toml or .tool-versions, vm run now refuses before VM creation if --nat isn't set. Error message points at --nat or --no-bootstrap. * `--no-bootstrap`: explicit opt-out for users who want a vanilla VM with their workspace and no tooling install. Detached bootstrap runs synchronously (foreground tee'd to the log file) so the CLI only returns once installs finish. Interactive mode keeps today's nohup'd background behaviour so the ssh session starts promptly. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
parent
9b5cbed32d
commit
aaf49fc1b1
5 changed files with 394 additions and 13 deletions
|
|
@ -91,6 +91,8 @@ func (d *deps) newVMRunCommand() *cobra.Command {
|
|||
removeOnExit bool
|
||||
includeUntracked bool
|
||||
dryRun bool
|
||||
detach bool
|
||||
skipBootstrap bool
|
||||
)
|
||||
cmd := &cobra.Command{
|
||||
Use: "run [path] [-- command args...]",
|
||||
|
|
@ -98,16 +100,24 @@ func (d *deps) newVMRunCommand() *cobra.Command {
|
|||
Long: strings.TrimSpace(`
|
||||
Create a sandbox VM and either drop into an interactive shell or run a command.
|
||||
|
||||
Three modes:
|
||||
Modes:
|
||||
banger vm run bare sandbox, drops into ssh
|
||||
banger vm run ./repo workspace sandbox, drops into ssh at /root/repo
|
||||
banger vm run ./repo -- make test workspace, runs command, exits with its status
|
||||
banger vm run -d ./repo workspace + bootstrap, exit (no ssh attach)
|
||||
|
||||
Tooling bootstrap (workspace mode):
|
||||
When the workspace contains a .mise.toml or .tool-versions, vm run
|
||||
installs the listed tools via mise on first boot. The bootstrap
|
||||
needs internet, so --nat must be set. Pass --no-bootstrap to skip
|
||||
it entirely (no NAT requirement).
|
||||
`),
|
||||
Args: cobra.ArbitraryArgs,
|
||||
Example: strings.TrimSpace(`
|
||||
banger vm run
|
||||
banger vm run ../repo --name agent-box --branch feature/demo
|
||||
banger vm run ../repo -- make test
|
||||
banger vm run -d ../repo --nat
|
||||
banger vm run -- uname -a
|
||||
`),
|
||||
RunE: func(cmd *cobra.Command, args []string) error {
|
||||
|
|
@ -129,6 +139,12 @@ Three modes:
|
|||
if sourcePath == "" && strings.TrimSpace(branchName) != "" {
|
||||
return errors.New("--branch requires a path argument")
|
||||
}
|
||||
if detach && removeOnExit {
|
||||
return errors.New("cannot combine --detach with --rm")
|
||||
}
|
||||
if detach && len(commandArgs) > 0 {
|
||||
return errors.New("cannot combine --detach with a guest command")
|
||||
}
|
||||
|
||||
var repoPtr *vmRunRepo
|
||||
if sourcePath != "" {
|
||||
|
|
@ -174,7 +190,7 @@ Three modes:
|
|||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return d.runVMRun(cmd.Context(), layout.SocketPath, cfg, cmd.InOrStdin(), cmd.OutOrStdout(), cmd.ErrOrStderr(), params, repoPtr, commandArgs, removeOnExit)
|
||||
return d.runVMRun(cmd.Context(), layout.SocketPath, cfg, cmd.InOrStdin(), cmd.OutOrStdout(), cmd.ErrOrStderr(), params, repoPtr, commandArgs, removeOnExit, detach, skipBootstrap)
|
||||
},
|
||||
}
|
||||
cmd.Flags().StringVar(&name, "name", "", "vm name")
|
||||
|
|
@ -189,6 +205,8 @@ Three modes:
|
|||
cmd.Flags().BoolVar(&removeOnExit, "rm", false, "delete the VM after the ssh session / command exits")
|
||||
cmd.Flags().BoolVar(&includeUntracked, "include-untracked", false, "also copy untracked non-ignored files into the guest workspace (default: tracked files only)")
|
||||
cmd.Flags().BoolVar(&dryRun, "dry-run", false, "list the files that would be copied into the guest workspace and exit without creating a VM")
|
||||
cmd.Flags().BoolVarP(&detach, "detach", "d", false, "create the VM, prep workspace + bootstrap, exit without attaching to ssh")
|
||||
cmd.Flags().BoolVar(&skipBootstrap, "no-bootstrap", false, "skip the mise tooling bootstrap (no --nat requirement)")
|
||||
_ = cmd.RegisterFlagCompletionFunc("image", d.completeImageNames)
|
||||
return cmd
|
||||
}
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue