Files
Archduino/docs/superpowers/specs/2026-04-13-installer-architecture-design.md
MDietermann edbbf47e9f docs: add installer architecture redesign spec
Design for Go rewrite of the Bash installer: single static binary with
Bubble Tea + Huh TUI, four-phase module contract, DAG execution engine,
embedded config + optional overlay, and a minimal Bash pre-install shim.
Scoped to installer architecture only; toolset overhaul, CLI browser,
and TTY Nerd Font support are queued as follow-up specs.
2026-04-13 18:00:29 +02:00

11 KiB

Installer Architecture Redesign

Status: Design approved, pending implementation plan Date: 2026-04-13 Scope: Installer architecture only. Toolset overhaul, CLI browser, and TTY-font story are queued as follow-up specs.

Context

The current Archduino installer is a Bash entry point (install.sh) that sources 13 files under lib/. It has grown beyond what Bash handles comfortably:

  • Monolithic / ordering-fragile. install.sh sources lib files in a fixed order at top level; hard to run a single step and hard to re-run safely.
  • Not idempotent / no resume. Re-running either duplicates work or silently skips things.
  • Mixed concerns per file. Detection, templating, package install, and theme application are intertwined in every lib.
  • Thin UX. Only choices are theme + verbose; no dry-run, no module selection, no preview.
  • Platform coupling. Arch vs Ubuntu-WSL branches are scattered throughout.

The goal is a principled rebuild across all five axes.

Decision summary

Area Choice
Language / runtime Go, single static binary
UI Bubble Tea + Huh (wizard for input, dashboard for execution)
Pre-install shim Minimal Bash script: ensures sudo, git, curl, ca-certificates, fetches binary
Module contract Four-phase: Detect → Plan → Apply → Verify
Configuration Embedded defaults + optional ~/.config/archduino/overlay.yaml
Privilege model User-mode binary; sudo per privileged command; upfront sudo -v + keepalive goroutine
State / logging Detect() is authoritative; run log is advisory UX only
Failure policy DAG execution; failed node fails its descendants; siblings proceed; --halt-on-error flag available
Subcommands One binary: install, theme, new, doctor
Platform scope (v1) Arch-only native + WSL2; PackageManager interface ready for Debian/Fedora later

Architecture

Module contract

Every installable unit implements:

type Module interface {
    Meta() ModuleMeta                       // id, human name, description, deps []string
    Detect(ctx) (State, error)              // read current system
    Plan(ctx, State) (Plan, error)          // diff vs desired; []Action with descriptions
    Apply(ctx, Plan, *Reporter) error       // execute; stream progress to Reporter
    Verify(ctx) error                       // confirm desired state reached
}
  • State is module-defined.
  • Plan is a flat list of Action{Description, Kind, Exec} so the engine can render plans without knowing modules.
  • Reporter decouples modules from the TUI.
  • Detect() is authoritative. No cached state overrides what the system reports.

Initial module set (1:1 with today's lib/*.sh): packages, fonts, zsh, starship, zellij, neovim, yazi, arduino-cli, renode, git (lazygit), scaffold, shell-config, themes.

Execution engine

  • DAG-scheduled. Modules declare deps []string. Engine topologically sorts and runs independent branches concurrently, default parallelism 4.
  • Failure policy. Failed node marks its descendants skipped-due-to-dep; siblings proceed. End-of-run summary: X succeeded, Y failed, Z skipped.
  • Dry-run (--dry-run, or wizard "Review plan" step): runs Detect + Plan for every selected module, renders the combined plan, exits.
  • Halt mode (--halt-on-error): opts into fail-fast for scripted use.
  • Resume hint. If a prior run log exists, TUI offers "last run: starship failed — resume? / start over? / pick modules?" Hint only; Detect decides what actually runs.

Configuration

  • Embedded defaults via //go:embed: package lists, theme files, Zellij layouts, config templates.
  • Optional overlay at ~/.config/archduino/overlay.yaml:
    version: 1
    extra_packages: [htop, ncdu]
    disabled_modules: [renode]
    theme_overrides:
      starship: ~/my-starship.toml
    
  • Overlay is merged at load time. Schema is versioned; a migration path exists when it grows.

Privilege model

  • Binary runs as the invoking user.
  • On install start, one sudo -v call; a keepalive goroutine refreshes the timestamp every 50 seconds while the installer is live.
  • Privileged operations shell out to sudo pacman …, sudo usermod …. If sudo needs to re-prompt (timestamp expired), the TUI is suspended via tea.ExecProcess.
  • Config files, git clones, and anything under $HOME run as the user — no root-owned files in home.

Platform abstraction

type PackageManager interface {
    Install(pkgs []string) error
    IsInstalled(pkg string) (bool, error)
    Update() error
    Name() string
}
  • V1 ships Pacman only. Debian/Ubuntu/Fedora impls are stubbed; wiring them is a later spec.
  • Platform{Distro, IsWSL, Arch} detection is centralized. WSL path sets /etc/wsl.conf hints and adds the user to dialout/uucp.

State and logs

  • ~/.local/state/archduino/runs/<timestamp>.json — per-run structured log: {module, phase, status, duration, error}.
  • ~/.local/state/archduino/last.json — most recent run; read by TUI for the resume hint.
  • Log rotation: keep the last 10 runs.
  • Safe to delete at any time; never read for correctness decisions.

User-facing surface

Pre-install shim (scripts/bootstrap.sh)

~30 lines of Bash, served from a stable URL. Invocation:

curl -fsSL https://archduino.dev/install.sh | bash

Responsibilities in order:

  1. Refuse to run as root.
  2. Detect distro; abort with a clear message if not Arch (v1 scope).
  3. Ensure sudo, git, curl, ca-certificates are installed (pacman -S --needed).
  4. Detect architecture, fetch the matching release binary from GitHub Releases, verify its SHA256 against the published checksum file, install to ~/.local/bin/archduino, chmod +x.
  5. exec archduino install.

Everything further (base-devel, multilib, user groups, toolset) is handled by modules inside the binary.

Subcommands

Command Purpose
archduino install Default; wizard + dashboard flow
archduino theme [name] Switch colorscheme across all tools; no arg = TUI picker
archduino new <name> <board> Scaffold a project (current arduino-new behavior)
archduino doctor Run Detect across all modules; report drift in a read-only dashboard
archduino --version / completion <shell> Standard

Global flags: --dry-run, --halt-on-error, --verbose, --no-tui, --only <mods>, --skip <mods>.

Shell aliases theme and arduino-new are preserved via thin wrappers dropped in ~/.local/bin by the shell-config module.

TUI flow (install)

Wizard phase (Huh forms):

  1. Welcome — detected platform, WSL status, binary version.
  2. Theme picker — 5 options, palette preview.
  3. Module selector — checkbox list; pre-ticked from overlay or "all" by default; disabled modules grayed with reason.
  4. Advanced (collapsible) — parallelism, halt-on-error toggle, overlay path override.
  5. Review plan — rendered Plan() grouped by module, expandable. Back-navigable.
  6. Confirm — [y/N] gate into Apply.

Apply phase (Bubble Tea dashboard):

┌─ archduino install ──────────────────────────────────┐
│ ✓ packages          12/12   2.4s                     │
│ ✓ fonts              1/1    0.8s                     │
│ ⠋ neovim             3/7    running: :MasonInstall…  │
│ ⠋ arduino-cli        2/4    running: core install…   │
│ · zellij             pending (waits: packages)       │
│ ✗ starship           failed — config template parse  │
│ ⊘ shell-config       skipped (dep: starship)         │
├──────────────────────────────────────────────────────┤
│ [tab] focus module  [enter] expand log  [q] abort    │
└──────────────────────────────────────────────────────┘
  • Concurrent modules visible live.
  • enter on a row expands a scrollable log tail.
  • On completion: summary view; footer offers [r]etry failed / [v]iew full log / [q]uit.

Project layout

archduino/
├── cmd/archduino/          # main.go, cobra wiring
├── internal/
│   ├── engine/             # DAG scheduler, Reporter, dry-run, resume
│   ├── module/             # Module interface, Plan/Action types, registry
│   ├── modules/            # one package per module
│   │   ├── packages/
│   │   ├── zsh/
│   │   └── ...
│   ├── platform/           # detection; PackageManager interface + pacman impl
│   ├── privilege/          # sudo -v + keepalive
│   ├── config/             # embedded defaults + overlay merge
│   ├── state/              # run log, last-run tracking
│   ├── theme/              # theme model + switcher
│   └── tui/
│       ├── wizard/         # Huh forms
│       └── dashboard/      # Bubble Tea Apply view
├── assets/                 # embedded configs, themes, zellij layout, templates
│   ├── themes/
│   ├── templates/
│   └── layouts/
├── scripts/bootstrap.sh
├── .github/workflows/
│   ├── ci.yml
│   └── release.yml         # goreleaser
└── docs/

Testing

  • Unit — every module's Detect and Plan against an injected System fake; table-driven cases for clean / already-installed / partial / drifted.
  • Engine — DAG scheduling, failure propagation, dry-run, resume; pure Go, fully covered.
  • Integration — Dockerfile on archlinux:base runs archduino install --no-tui --yes end-to-end in CI; asserts key files/packages/services. One matrix entry for native Arch. WSL smoke test remains manual for v1.
  • TUI — teatest for wizard navigation and dashboard state transitions. No screenshot tests.

Distribution

  • GitHub Releases via goreleaser: linux/amd64, linux/arm64 static binaries; SHA256SUMS file.
  • No AUR package for v1; the shim is the distribution channel. AUR can follow later.
  • Semver; breaking changes bump major.

Migration

  • Existing install.sh + lib/*.sh stay on master at a tagged v0-bash-final so users can still run the old installer if they want.
  • feat/renode-dap merges first; a new feat/go-rewrite branch hosts the rewrite.
  • First Go release (v1.0.0) lands when feature parity with the current Bash installer is verified on a clean Arch VM.

Out of scope (queued for follow-up specs)

  • Toolset redesign (re-evaluating which CLI tools are curated).
  • CLI browser integration.
  • Nerd Font rendering on bare TTY (constrained by the Linux console's 512-glyph PSF limit; needs a separate design covering fbterm/kmscon or the "SSH-in-from-a-real-terminal" fallback).
  • Non-Arch platform support (Debian/Ubuntu/Fedora).
  • AUR packaging.