Skip to content

0.3.0: non-interactive apt upgrades; Omarchy's updater as a hand-off (#21) - #22

Merged
edimarlnx merged 1 commit into
mainfrom
fix/21
Sep 26, 2026
Merged

edimarlnx merged 1 commit into
mainfrom
fix/21

Conversation

@edimarlnx

Copy link
Copy Markdown
Contributor

Closes #21

Release notes (0.3.0)

tui-kit v0.4.4 runs every command in a session with no terminal, so a program that asks a question fails at once instead of hanging behind the screen. The two upgrades that could ask are now handled.

  • apt upgrades ask nothing. The upgrade runs as sudo -n env DEBIAN_FRONTEND=noninteractive NEEDRESTART_MODE=a apt-get -y -o Dpkg::Options::=--force-confdef -o Dpkg::Options::=--force-confold upgrade (or dist-upgrade). debconf takes its defaults, needrestart restarts the services left on replaced libraries, and a config file changed on the machine keeps the local version (the package's version is left beside it as .dpkg-dist). The confirm dialog shows the full command line and explains all three parts.
  • Omarchy Server's updater gets the terminal. omarchy-server-update run --no-reboot is interactive by design, so it no longer runs as a background step. After the same preview and confirm, tui-update hands the terminal over to it (tea.Exec). When it exits, the screen comes back, the sequence continues and the machine is read again. It is previewed and run with sudo rather than sudo -n, because it has a terminal where sudo can ask for a password.
  • --demo-machine ubuntu|omarchy runs both paths on a sample machine (the default is fedora). On the Omarchy sample machine, the hand-off shows on the real terminal what would run there and waits for Enter.
  • README: new section "Nothing asks behind the screen", two new screenshots (confirm-apt, confirm-omarchy), and the install snippet now points at 0.3.0.

How

  • pkgmgr.BuildUpgrade (apt) now has Env: kitpkg.APTEnv() and the dpkg options, set before the verb. Its description states the config-file policy.
  • pkgmgr.IsHandOff marks the Omarchy updater. assemblePlan records it in the new Plan.HandOff (index into Commands) and adds the explanation sentences to the new Plan.Explain, which the confirm body shows.
  • New Backend.HandOff(cmd) (updates.Process, error). The real backend builds the process in internal/pkgmgr/handoff.go (still the only exec site; check-exec is clean) from the same resolved binary and escalation prefix minus -n. Real.Preview renders a hand-off the same way, so the preview still matches what runs. The fake prints a line, waits for Enter and applies the upgrade to the sample machine.
  • The app's runStep sends a hand-off step to tea.Exec and runner steps to the background, as before. Both report back as a stepMsg, so failure handling, the post snapshot and the final reload are unchanged.

Tests

  • internal/pkgmgr/handoff_test.go: exact apt argv and env for both verbs, the escalated preview (sudo -n env ...), the Omarchy plan's single hand-off at the right index with the snapshot on and off, the hand-off argv and preview (prefix without -n, env for variables, direct when running as root), refusal to hand off a runner step, the fake hand-off running and applying, and all sample machines.
  • cmd/tui-update/handoff_test.go: the apt confirm dialog (command line and explanation), a full Omarchy sequence where the updater step is a tea.Exec (not a runner step, and nothing runs before the hand-off) followed by the post snapshot and a reload, a failed hand-off stopping the sequence, and the --demo-machine flag.
  • The argv table in command_test.go pins the new apt command lines.
  • make check (gofmt, vet, exec boundary, golangci-lint 0 issues, tests) and make manifest pass.

Lab evidence (lab host, 0.3.0-rc build, guests snapshotted first and restored after)

Ubuntu 24.04: an upgrade that would have prompted

Setup: needrestart 3.6-7ubuntu4.5 installed. chrony pinned back to 4.5-1ubuntu4 (noble-updates has 4.5-1ubuntu4.2, which ships a different /etc/apparmor.d/usr.sbin.chronyd), and that conffile edited locally, so dpkg has a config-file question to ask. The upgrade was run from the TUI in tmux (p, U, y).

Confirm dialog:

  │  apt asks nothing: debconf takes its defaults, needrestart restarts the services left on replaced libraries, and a config  │
  │  file changed on this machine keeps the local version, and the package's new one is left beside it as .dpkg-dist.          │
  │  tui-update never reboots by itself.                                                                                       │
  │  Command to run:                                                                                                           │
  │  $ /usr/bin/sudo -n apt-get update                                                                                         │
  │  $ /usr/bin/sudo -n env DEBIAN_FRONTEND=noninteractive NEEDRESTART_MODE=a apt-get -y -o Dpkg::Options::=--force-confdef    │
  │    -o Dpkg::Options::=--force-confold upgrade                                                                              │

End of the apply pane (the refresh brought in 43 upgrades; the run took 32 s, from 01:22:10 to 01:22:42, and nothing hung):

 Restarting services...
  systemctl restart cron.service packagekit.service ... ssh.service systemd-journald.service systemd-networkd.se…
 ...
 ✓ done

 The sequence finished. Nothing running has to be restarted afterwards.
 upgrade finished

dpkg's own record (/var/log/apt/term.log) and the resulting files:

Setting up chrony (4.5-1ubuntu4.2) ...
Configuration file '/etc/apparmor.d/usr.sbin.chronyd'
 ==> Modified (by you or by a script) since installation.
 ==> Package distributor has shipped an updated version.
 ==> Keeping old config file as default.

-rw-r--r-- 1 root root 2900 Sep 26 01:20 /etc/apparmor.d/usr.sbin.chronyd
-rw-r--r-- 1 root root 2838 Jul  2  2024 /etc/apparmor.d/usr.sbin.chronyd.dpkg-dist
# local change made on this machine (tui-update lab)      <- last line of the kept file

dpkg -l chrony: ii chrony 4.5-1ubuntu4.2. dpkg --audit: clean.

Omarchy Server: the updater, handed the terminal

47 pending packages, snapper root configuration present. Run from the TUI in tmux (p, U, y).

Confirm dialog:

  │  The terminal is handed over to omarchy-server-update for its step: answer it there, and tui-update comes back when it     │
  │  exits.                                                                                                                    │
  │  Command to run:                                                                                                           │
  │  $ /usr/bin/sudo -n snapper create -c root -t pre -d 'tui-update: before the pacman upgrade' --print-number                │
  │  $ /usr/bin/sudo omarchy-server-update run --no-reboot                                                                     │
  │  $ /usr/bin/sudo -n snapper create -c root -t post -d 'tui-update: after the pacman upgrade' --print-number                │

While the updater has the terminal, the pane shows the updater's own output, not the TUI, and it runs as a child of tui-update on the TUI's terminal:

Total Download Size:   626.79 MiB
:: Proceed with installation? [Y/n]
:: Retrieving packages...
 Total ( 0/68)                                      60.6 MiB  33.7 MiB/s 00:16 [---c o  o  o ...]   9%

lab   1388  1383  pts/0  tui-update
root  2744  1388  pts/0  /usr/bin/sudo /usr/bin/omarchy-server-update run --no-reboot
root  2763  2758  pts/2  /bin/bash /usr/bin/omarchy-update -y

After it exits (COMMAND_EXIT_CODE="0", "Reboot? (not rebooting: unattended update)"), the screen is restored and the machine is read again (updates: 0 pending):

 $ /usr/bin/sudo omarchy-server-update run --no-reboot
   (the terminal is handed over to this step; tui-update comes back when it exits)
 ✓ done (its output was on the terminal)

No reboot (up 14 min afterwards), and tui-update was still running.

Found along the way, not changed here (pre-existing): the post snapshot then fails: snapper create -t post needs --pre-number <n> ("Missing or invalid pre-number"). The pre/post pair has never been able to complete on a snapper machine. That is a separate issue with a design question (the pre number is only known at run time, after the preview), so this PR leaves it alone.

🤖 Generated with Claude Code

https://claude.ai/code/session_01MbCfX3T8khyd9hsrN5kMo3

tui-kit v0.4.4 starts every runner child in its own session with no
controlling terminal, so a program that asks a question now fails fast
instead of hanging behind the TUI. Two upgrade paths could ask:

- apt: the upgrade now carries the kit's APTEnv (DEBIAN_FRONTEND=
  noninteractive NEEDRESTART_MODE=a) and -o Dpkg::Options::=--force-confdef
  -o Dpkg::Options::=--force-confold, so debconf takes its defaults,
  needrestart restarts services instead of asking, and a locally changed
  config file keeps the local version (the package's is left as
  .dpkg-dist). The preview shows `sudo -n env ...` and the confirm dialog
  says what each part does.
- Omarchy Server: omarchy-server-update is interactive by design, so its
  step is no longer a runner step. The plan marks it as a hand-off; after
  the same preview and confirm, Backend.HandOff prepares the process and
  tea.Exec gives it the terminal, restores the screen when it exits, and
  the sequence goes on and re-reads the machine. It is previewed and run
  with the prefix minus -n, since sudo may ask on the terminal it has.

--demo-machine ubuntu|omarchy drives the two new paths without such a
machine; two README screenshots show their confirm dialogs.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01MbCfX3T8khyd9hsrN5kMo3
@edimarlnx
edimarlnx merged commit 9e2a37e into main Sep 26, 2026
8 checks passed
@edimarlnx
edimarlnx deleted the fix/21 branch September 26, 2026 01:35
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.

Updates run without a terminal since tui-kit v0.4.4: apt needs APTEnv, Omarchy's updater needs a hand-off

1 participant