-
Notifications
You must be signed in to change notification settings - Fork 0
2. UI + dry run
The guided interface that dresses the engine, and the functional
--dry-run. It lives insrc/prompt.rs(input withinquire),src/progress.rs(progress withindicatif) and insrc/main.rs's flow.This is a dressing layer, not logic: it does not touch the rollback and does not change the steps. The value is entirely in the decoupling — if the UI is a shell around an engine that does not know it, you can change it (or add another) without touching the installation and the rollback.
From CLAUDE.md: the same installer runs interactively or non-interactively through one flow.
- The steps know nothing about the UI: they do not import
inquire/indicatifand print no prompts. They read from theContextand log throughtracing. - Input collection happens before the engine, in
prompt.rs, and populates theContext. The engine receives an already-resolvedContextand does not know whether the values came frominquire, from--configor from the defaults. - Progress is an observer of the engine (
ProgressReporter), not part of the steps.
The proof it is done right: every earlier test stayed green without modification.
execute's signature did not change (it delegates toexecute_with_reporterwith aNoopReporter), and theSteptrait was not touched. A suit of clothes, not a rewritten body.
A structural test verifies the decoupling: no step imports inquire/indicatif, and the engine does
not use indicatif:: — it depends only on the abstraction.
prompt.rs implements input collection behind the same boundary as the config layer: the cascade
logic stays in config; only the how of asking
changes.
| Field | Widget |
|---|---|
| Version |
Select (16.0/17.0/18.0/19.0) |
| OS user, DB name, port, install subdir |
Text with inline validation (reusing config's validators) |
| Admin password |
Password, masked → goes into the Secret, never logged |
| Nginx, final confirmation | Confirm |
Only fields not passed on the CLI are prompted for: the priority stays CLI > env > interactive >
default. Without a TTY, main bypasses the prompts and uses CLI → env → default: inquire never
blocks when there is no terminal.
The engine notifies an abstraction, not indicatif:
trait ProgressReporter {
fn step_start(&self, name, index, total);
fn step_done(&self, name);
fn step_failed(&self, name);
fn rollback_start(&self, total); // the rollback is visible: the user sees it is undoing
fn undo_start(&self, name);
fn undo_done(&self, name);
}| Impl | Use |
|---|---|
IndicatifReporter |
interactive TTY: spinner + [pos/len] bar with the current step |
LogReporter |
no TTY / output to a file: tracing only |
NoopReporter |
silent (the default for execute) |
The reporter is passed to execute_with_reporter, not to the Step trait: the steps stay unaware.
dry_run_plan shows the complete plan without mutating:
for each step: snapshot (read-only) → run in dry-run (LOGS the intent, mutates nothing)
no persistence · no rollback
- In dry-run, each step distinguishes “would act” from “no-op (pre-existing)” based on its own snapshot (“I would create the directory” vs “already there, skip”).
- An unavailable snapshot does not interrupt the plan: it is reported and the plan carries on.
- In dry-run
mainskips the preflight checks that require root: it is a preview without sudo, useful for understanding what will happen and for validating a.envbefore running it for real.
Honesty about “without sudo”. The plan interrogates the system: every step takes its own snapshot, and some of them ask PostgreSQL through
sudo. Without privileges those questions get no answer, the steps involved show up as “snapshot unavailable”, and the plan — while true — is incomplete. The installer says so before printing it, rather than leaving it to be discovered in a warning line somewhere in the output.Every
sudoinvocation also passes-n(non-interactive): a missing permission becomes an immediate error instead of a silent wait on a password prompt. In production we are already root, so nothing changes there.
Tests: in dry-run the engine calls no mutating operation of SystemOps and does not write the
state.
-
executeunchanged → full compatibility with the earlier tests.execute_with_reporter(androllback_with_reporter) were added; theSteptrait did not change. - The reporter is selected automatically in
main: TTY + a real installation →indicatif; otherwise logs. - The password is never logged: masked
Passwordon input,Secretfor storage.
Start here
Key concepts
References
For developers
Technical detail — how it works inside
Steps:
- 1.1 PrepareOptRoot
- 1.2 CreateOdooUser
- 1.3 SetupLogDir
- 1.3b SetupCacheDir
- 1.4 AptPackages (delta)
- 1.5 InstallWkhtmltopdf
- 1.6 SetupPostgres
- 1.7 CreateDbRole
- 1.8 CreateDatabase
- 1.9 CloneOdooRepo
- 1.10 CreateVirtualenv
- 1.11 InstallPythonRequirements
- 1.12 GenerateConfig
- 1.12b SetupDataDir
- 1.13 InitializeOdooDatabase
- 1.14 SetupSystemd
- 1.15 Nginx (6 sub-steps)
- 1.16 WriteControlScript + PatchBashrc
Cross-cutting: