Files
tuio/CLAUDE.md
teste.inaba a05b98fdf5 feat: auto-update app-dono-cliente when a new image is pushed to :latest
Adds a StepRunUpdater step that starts app-dono-updater after the app
container comes up: a poll loop (docker pull, compare image IDs,
recreate on change) baked into the official docker:cli image via
`sh -c`, reusing the same docker run argv as the initial container
start so the two can't drift.

Not built on Watchtower: containrrr/watchtower was archived upstream
in Dec 2025 with no maintained successor recommended for production
use, so this avoids taking on that dependency.
2026-08-26 12:30:44 -03:00

9.2 KiB
Raw Permalink Blame History

CLAUDE.md

Guidance for Claude Code when working in this repository.

User-facing docs are in Portuguese (BR): README.md and docs/. This file is in English and is the technical source of truth for how the code works.

What this is

A terminal UI (TUI) installer for the "App do Dono" client middleware. The middleware runs on a tenant's infrastructure and brokers communication between the tenant's servers and davinTI's central server.

The installer is a guided wizard that: checks Docker → logs into the private registry → pulls images → (optionally) sets up a WireGuard tunnel (vproxy) when there is no public IP → collects configuration via terminal forms → generates config files (config.toml, envs) → runs the Docker containers.

It is not the middleware itself — it only provisions and launches it via docker.

Tech stack

  • Go 1.25
  • Bubble Tea v2 (charm.land/bubbletea/v2) — Elm-architecture TUI runtime.
  • Bubbles v2 — textinput, spinner components.
  • Lip Gloss v2 — styling.
  • BurntSushi/toml — read existing config.toml for form defaults.
  • Module path: git.davinti.com.br/davinTI/app-dono/tui.

Layout

cmd/main.go            Entry point; starts the Bubble Tea program.
internal/tui/
  model.go             Model struct, ConfigValues, loadConfig (defaults), InitialModel, Init.
  steps.go             step enum — the ordered wizard stages.
  update.go            Update: global keys + per-step dispatch (handlers).
  view.go              View: header + per-step body + help footer (AltScreen).
  form.go              Generic FormStep/FormField component (text/password/number/select).
  cmds.go              Msg types and async tea.Cmd wrappers around docker/file ops.
  docker.go            os/exec wrappers around the `docker` binary.
  config.go            Generates & writes config.toml and envs (+ numeric validation).
  styles.go            Lip Gloss palette/styles.
Makefile               Multi-arch build + S3 publish.

Architecture (Model–Update–View)

  • Model (model.go) holds all state: currentStep, progress/error flags, one FormStep per form, and configValues (ConfigValues) which accumulates each form's map[string]string output by section.
  • Update (update.go) handles global messages (Ctrl+C, window size, spinner tick) then dispatches on currentStep to per-step handlers. Step transitions happen when a form returns done == true or when an async completion message arrives.
  • View (view.go) renders per-step.
  • Commands (cmds.go) wrap every blocking op (docker calls, file writes) as a tea.Cmd returning a Msg: DockerCheckedMsg, ImageDownloadFinishedMsg, ConfigFileMsg, DockerRunMsg, TickMsg.

Wizard flow

steps.go defines the order. See docs/fluxo.md for full detail.

  1. StepCheckDocker — exec.LookPath("docker"). Missing → StepDockerInstall (instructs manual install and exits; never auto-installs Docker).
  2. StepDockerLogin → StepDownloadImage — docker login + pull of the app image. These registry credentials are reused later for the vproxy image.
  3. StepIPQuestion — "public IP available?"
    • Yes → jump to StepAppConfig.
    • No → vproxy block: StepWireguardConfig → StepGenerateWireguardFile (writes envs) → StepDownloadWireguard → StepRunWireguard.
  4. Config forms: StepAppConfig → StepServerConfig → StepDatabaseConfig → StepCertConfig.
  5. StepReview — shows all collected config; Enter confirms.
  6. StepGenerateFile — writes config.toml (validates numeric fields first).
  7. StepRunDocker — runs app-dono-cliente container.
  8. StepRunUpdater — runs app-dono-updater, which auto-updates app-dono-cliente whenever a new image is pushed to :latest.
  9. StepDone.

Navigation & error handling

  • Back navigation: Esc returns to the previous input step. The Model keeps a history []step stack; advance(next) pushes the current input step, goBack() pops. isInputStep (steps.go) gates which steps participate — action/wait steps (downloads, file gen, container runs) are excluded so back never re-enters a side-effecting step. Esc is handled globally in Update; forms don't consume it. Form values survive going back because the FormSteps live on the Model.
  • Retry vs. fix: transient failures (image pull, container run) offer r to re-run just that command (q/other quits). Validation failures (file generation) route back to the relevant form to correct the value instead of quitting.

Key constants

In update.go:

  • imageName = hub.davinti.com.br:443/app-dono/app-cliente:latest
  • wireguardImageName = hub.davinti.com.br:443/davinti-vproxy:latest
  • updaterImageName = docker:cli
  • configPath = config.toml, wireguardConfigPath = envs

In docker.go: networkName = app-dono_app (all containers join this network; created on demand).

Generated files

  • config.toml — app config, bind-mounted at /app/config.toml. If present at startup it seeds form defaults via loadConfig. Generated by GenerateConfigTOML.
  • envs — vproxy/WireGuard env vars, passed via --env-file. Generated only when there is no public IP. By GenerateWireguardConfig.

Both are gitignored.

Containers

Container Image Notes
app-dono-cliente app-cliente <host port>:8080, mounts config.toml + cert dir, --restart unless-stopped, runs as host uid:gid.
vproxy davinti-vproxy --cap-add=NET_ADMIN, /dev/net/tun, --env-file envs, only without public IP.
app-dono-updater docker:cli Not a third-party updater — a poll loop (sh -c) baked into the official docker:cli image, since Watchtower was archived upstream in Dec 2025 with no maintained successor recommended for production. Every updaterPollIntervalSeconds (300s) it docker pulls app-dono-cliente's image, compares image IDs, and if changed, stops/removes/recreates the container using the exact same docker run argv as the original start (appClienteRunArgs in docker.go, shared by both call sites so they can't drift). Mounts /var/run/docker.sock and the host's ~/.docker/config.json (written by the earlier docker login) for private-registry auth. Not on app-dono_app — only talks to the Docker daemon.

seccomp=unconfined is added to either container when the "Modo Compatibilidade" (seccomp_unconfined) select is "Sim" — for old machines.

Gotchas (read before editing)

  • Port: config.toml always writes port = 8080 (hardcoded in GenerateConfigTOML). The form's "Porta" value only sets the host-side port in the docker run mapping (<port>:8080); inside the container the app always listens on 8080.
  • Network name is the networkName constant (app-dono_app) in docker.go; use it rather than re-typing the literal.
  • Registry credentials are collected once (login form) and reused for the vproxy pull — no second prompt.
  • Containers run detached and are verified with docker inspect for running; on failure the last 20 log lines are surfaced.

Build & run

go run ./cmd                 # run from source
go build -o installer ./cmd  # local binary

make build                   # multi-arch (linux/darwin/windows, amd64/arm64) → ./dist
make build VERSION=2.0.0
make clean
make push    S3_BUCKET=<bucket> VERSION=<v>   # sync dist/ to S3 (version + latest)
make release S3_BUCKET=<bucket> VERSION=<v>   # per-binary copy with OS/arch naming

Versioning: tui.Version (internal/tui/version.go) defaults to "dev" and is stamped at build time via VERSION_LDFLAG (-X .../internal/tui.Version=$(VERSION)) in the Makefile. installer --version (or -v) prints it and exits before the TUI starts; it is also shown in the TUI header. A plain go build leaves it as "dev". The Makefile build rules have no source deps, so make build will not rebuild existing dist/ artifacts — run make clean first when re-stamping a new VERSION.

Tests (go test ./...) cover the pure logic in config_test.go (config.toml/envs generation + numeric validation) and form_test.go (FormStep.Values()). The docker wrappers, Update and View are not unit-tested — verify those by running go build ./... and exercising the TUI manually. Fully running the installer requires a working Docker daemon and registry access.

Conventions

  • User-facing strings in the TUI are Portuguese (BR); keep new ones consistent.
  • Keep code comments and identifiers matching the surrounding style (mixed English identifiers, Portuguese user messages).
  • When adding a wizard step: add it to steps.go, a handler in update.go, a render branch in view.go, and any new Msg/Cmd in cmds.go.