Skip to content

Add onctl tui: the VM list as a k9s-style live view - #1210

Merged
cdalar merged 4 commits into
mainfrom
tui
Oct 21, 2026
Merged

cdalar merged 4 commits into
mainfrom
tui

Conversation

@cdalar

@cdalar cdalar commented Oct 21, 2026

Copy link
Copy Markdown
Owner

What

A port of boxctl's tui (boxctl-io/boxctl-cli#130, #131) onto onctl's provider interface. Same stance as there: every action is what the subcommand does, nothing else in the CLI changes.

  • Header: info block (provider, VMs by state, what's showing, last refresh with a spinner while busy, rev), the current view's hotkeys as <key> label columns, and an onctl logo on the right. Hidden on terminals under 18 lines.
  • Views via the : prompt: :vms (default), :images, :sizes, plus :create, :ssh <vm>, :help, :q. :vms web opens filtered.
  • Table: one bordered box titled VMs(all)</filter>[count], sorted column marked ↑/↓, cursor row highlighted, coloured STATE column, 0/1/2 for all/running/paused (ls's two sections). shift+letter sorts by that column.
  • Pages: enter/d describe (a kubectl describe layout: IP, type, image, location, SSH port, created, cost; follows the VM on refresh), y the VM as JSON, ? help. Esc pops; crumbs at the bottom show the stack.
  • Actions: s runs onctl ssh <vm> as a child process (the alternate screen is left for it) with the resolved --provider and any root flags passed along; p is provider.Pause by name; r is CreateSSHKey + Resume as resume.go does it; ctrl-d is provider.Destroy after destroy's question. Pause is refused on a paused VM, resume on a live one, and nothing runs twice while an action is in flight.
  • Create (c): a form with name, image and type. Each is typed, and ←/→ walks image/type through what the provider's images/sizes list when it lists them; empty fields leave the flag off so onctl create's own defaults apply. Enter runs onctl create in the foreground with those flags, so create's output and waits show as usual. c from :images or :sizes fills in that row.
  • The list refreshes every 5s; an action's completion refreshes at once.

Also: ls's running/paused partition moves into listVMs so ls and the tui agree on which VMs count as paused. Provider log lines are kept off the screen while the tui runs (ONCTL_LOG sends them to a file whose name is printed on exit).

boxctl-only views and keys (urls/expose, backups/restore, keep, goal logs) have no onctl equivalent and are left out.

Dependencies

Adds charmbracelet/bubbletea, bubbles (textinput, spinner) and lipgloss. spf13/pflag becomes a direct dependency (it was already there via cobra).

Testing

  • cmd/tui_test.go drives the model against a fake provider: rows/sort/filter/state filter, keys arriving batched, refusals on paused and pending VMs, r → CreateSSHKey then Resume, the destroy dialog → Destroy, describe/json pages and VM keys from them, :images/:sizes/:vms web, unknown commands, :q, partial listing errors, the child-process exit message, the child args (provider + changed root flags), the create form (catalog walking, existing-name refusal, the onctl create command line, preselection from :sizes).
  • Rendered the real binary in a pseudo-terminal against the local ac provider at 100x26 and 70x16: list, help, :sizes, :images, the create form, flash messages. (The ac listing itself fails on this machine with "unexpected output from the container CLI", on main too; the tui shows it as a flash.)
  • go vet, gofmt, golangci-lint, Linux and Windows cross-builds: clean. Full cmd test package passes.

🤖 Generated with Claude Code

A port of boxctl's tui (boxctl-io/boxctl-cli#130, #131) onto onctl's provider
interface. A header with an info block, the view's hotkeys and a logo;
one bordered table per view (:vms, :images, :sizes), switched with a `:`
command and filtered with `/`; describe (d) and json (y) pages; a y/N
dialog for destroy; flash messages on the crumbs line. The list
refreshes itself every 5s.

Every action is what the subcommand does, nothing more:
- s runs `onctl ssh <vm>` as a child process (the alternate screen is
  left for it), with the resolved --provider and any root flags passed
  along; p is provider.Pause by name, r is CreateSSHKey + Resume as
  resume.go does it, ctrl-d is provider.Destroy after destroy's question
- c is a form (name, image, type -- typed, or walked through what the
  provider's images/sizes list) that runs `onctl create` in the
  foreground with those flags, so create's own output and waits show
  as usual
- pause is refused on a paused VM, resume on a live one, and nothing
  runs twice while an action is in flight

ls's running/paused partition moves into listVMs so the tui and ls
agree on which VMs count as paused. The provider code's log lines are
kept off the screen while the tui runs (ONCTL_LOG sends them to a file
whose name is printed on exit).

boxctl-only views and keys (urls/expose, backups/restore, keep, goal
logs) have no onctl equivalent and are left out.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
@socket-security

socket-security Bot commented Oct 21, 2026 •

Copy link
Copy Markdown

Review the following changes in direct dependencies. Learn more about Socket for GitHub.

Diff Package Supply Chain
Security
Vulnerability Quality Maintenance License
Addedgolang/​github.com/​charmbracelet/​bubbletea@​v1.3.1099100100100100
Addedgolang/​github.com/​charmbracelet/​bubbles@​v1.0.0100100100100100
Addedgolang/​github.com/​charmbracelet/​lipgloss@​v1.1.0100100100100100

View full report

@cdalar
cdalar enabled auto-merge October 21, 2026 15:05
Both jobs run under a 5-minute cap on the self-hosted runner. On main
the build already takes 4-5 minutes with a warm module cache, and a
pull request that changes go.sum starts cold: ~90s more for vet to
download and compile the modules, which is over the cap, so the job is
cancelled before its post step can save the cache, and every rerun is
cold again. Lint finished in 2 minutes but was cancelled while its
post step was still uploading the 380 MB module cache.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_012kqmyZGSj54mkMfVDcWhUU
… say so

- The destroy dialog took enter as yes. `onctl destroy`'s own prompt
  is [y/N], so a reflexive enter no longer destroys a VM: y alone does.
- The 5s tick re-listed the images/sizes catalog whenever that view was
  open (a few API calls each on some providers). They are listed when
  the view opens and on R; the tick lists the VMs only.
- A catalog whose listing failed showed "Loading…" for ever, with the
  error flashed again every tick. The table now says why it is empty
  and that R retries.
- R was both refresh and the sizes view's ARCH sort letter, and refresh
  won, so ARCH could not be sorted; it sorts on A now.
- `onctl ssh` exits with the remote shell's status, so a session that
  ended with `exit 1` was flashed as a failed ssh. A non-zero exit with
  nothing on stderr is left alone.
- The "Refreshed" line listed in-flight actions in map order, which
  reshuffled them on every spinner frame; sorted.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_012kqmyZGSj54mkMfVDcWhUU
The onctl-2 pool has been provisioning VMs 8-14 minutes late since
about 14:20 UTC today and losing runners mid-job (the edge build on
main, then this PR's Build twice), so lint, build, vuln, the docs test
deployment, Dependabot auto-merge and Snyk go to ubuntu-latest until
the pool is looked at. The release and docs-deploy workflows are left
on onctl-2.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_012kqmyZGSj54mkMfVDcWhUU
@codecov

codecov Bot commented Oct 21, 2026

Copy link
Copy Markdown

Codecov Report

❌ Patch coverage is 78.55015% with 216 lines in your changes missing coverage. Please review.
✅ Project coverage is 45.70%. Comparing base (cb8c258) to head (53d6b96).

Files with missing lines Patch % Lines
cmd/tuiactions.go 63.42% 94 Missing ⚠️
cmd/tui.go 76.23% 52 Missing and 1 partial ⚠️
cmd/tuirender.go 87.50% 33 Missing ⚠️
cmd/tuicreate.go 82.94% 22 Missing ⚠️
cmd/tuiviews.go 93.27% 8 Missing ⚠️
cmd/list.go 60.00% 6 Missing ⚠️
Additional details and impacted files
@@            Coverage Diff             @@
##             main    #1210      +/-   ##
==========================================
+ Coverage   41.25%   45.70%   +4.45%     
==========================================
  Files          69       74       +5     
  Lines        7560     8554     +994     
==========================================
+ Hits         3119     3910     +791     
- Misses       4441     4643     +202     
- Partials        0        1       +1     

☔ View full report in Codecov by Harness.
📢 Have feedback on the report? Share it here.

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.

@cdalar
cdalar merged commit ceb54e4 into main Oct 21, 2026
11 checks passed
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

⌂ 换网址