fix: keep passthrough help local

This commit is contained in:
Peter Steinberger 2026-05-04 23:32:22 +01:00
parent a0585156af
commit 1d22208087
No known key found for this signature in database
3 changed files with 82 additions and 0 deletions

View File

@ -27,6 +27,7 @@
- Fixed WebVNC portal login redirects by canonicalizing broker origins before starting the browser login flow.
- Fixed AWS desktop provisioning and Windows SSH bootstrap issues that could leave managed desktop leases unreachable.
- Fixed passthrough command help such as `crabbox run --help` so it prints local usage instead of provisioning a remote lease.
- Fixed `crabbox desktop launch --browser` on freshly warmed desktop leases by creating the remote workdir before launching the app.
- Fixed failed Blacksmith Testbox warmups so printed, newly listed, or delayed `tbx_...` boxes are stopped instead of being left queued after an upstream workflow error.
- Fixed `crabbox run --junit` so all-passing JUnit files record results instead of leaving the coordinator run stuck when the failure list is empty.

View File

@ -36,10 +36,74 @@ func (a App) Run(ctx context.Context, args []string) error {
a.printHelp()
return nil
}
if help, ok := a.directCommandHelp(ctx, args); ok {
return help
}
return a.runKong(ctx, args)
}
func (a App) directCommandHelp(ctx context.Context, args []string) (error, bool) {
if len(args) < 2 || !isHelpArg(args[1]) || isKongCommandGroup(args[0]) {
return nil, false
}
helpArgs := []string{"--help"}
switch args[0] {
case "init":
return a.initProject(ctx, helpArgs), true
case "login":
return a.login(ctx, helpArgs), true
case "logout":
return a.logout(ctx, helpArgs), true
case "whoami":
return a.whoami(ctx, helpArgs), true
case "doctor":
return a.doctor(ctx, helpArgs), true
case "warmup":
return a.warmup(ctx, helpArgs), true
case "run":
return a.runCommand(ctx, helpArgs), true
case "sync-plan":
return a.syncPlan(ctx, helpArgs), true
case "history":
return a.history(ctx, helpArgs), true
case "logs":
return a.logs(ctx, helpArgs), true
case "events":
return a.events(ctx, helpArgs), true
case "attach":
return a.attach(ctx, helpArgs), true
case "results":
return a.results(ctx, helpArgs), true
case "status":
return a.status(ctx, helpArgs), true
case "list":
return a.list(ctx, helpArgs), true
case "usage":
return a.usage(ctx, helpArgs), true
case "ssh":
return a.ssh(ctx, helpArgs), true
case "vnc":
return a.vnc(ctx, helpArgs), true
case "webvnc":
return a.webvnc(ctx, helpArgs), true
case "screenshot":
return a.screenshot(ctx, helpArgs), true
case "inspect":
return a.inspect(ctx, helpArgs), true
case "stop", "release":
return a.stop(ctx, helpArgs), true
case "cleanup":
return a.cleanup(ctx, helpArgs), true
default:
return nil, false
}
}
func isHelpArg(arg string) bool {
return arg == "-h" || arg == "--help" || arg == "help"
}
func (a App) printHelp() {
fmt.Fprintln(a.Stdout, `Crabbox leases remote test boxes, syncs your dirty checkout, runs commands, and cleans up.

View File

@ -81,6 +81,23 @@ func TestSubcommandHelpExitsZero(t *testing.T) {
}
}
func TestPassthroughCommandHelpExitsBeforeExecution(t *testing.T) {
for _, command := range []string{"warmup", "run", "status", "ssh", "vnc", "webvnc", "screenshot", "inspect", "stop"} {
t.Run(command, func(t *testing.T) {
var stderr bytes.Buffer
app := App{Stdout: &bytes.Buffer{}, Stderr: &stderr}
err := app.Run(context.Background(), []string{command, "--help"})
var exitErr ExitError
if !AsExitError(err, &exitErr) || exitErr.Code != 0 {
t.Fatalf("%s --help error=%v, want exit 0", command, err)
}
if !strings.Contains(stderr.String(), "Usage") {
t.Fatalf("%s --help output missing usage: %s", command, stderr.String())
}
})
}
}
func TestGroupedCommandHelpExitsZero(t *testing.T) {
for _, command := range []string{"actions", "admin", "cache", "config", "desktop", "pool", "machine"} {
t.Run(command, func(t *testing.T) {