# Testing the installer in containers

**2026-08-13.** First pass. Ubuntu 24.04, Debian 12, Arch, Fedora 41.

## What passed

**OS and package-manager detection is correct on all four.**

| image | `OS` | `PM` |
| --- | --- | --- |
| ubuntu:24.04 | `ubuntu` | `apt` |
| debian:12 | `debian` | `apt` |
| archlinux | `arch` | `pacman` |
| fedora:41 | `fedora` | `dnf` |

**`--help` and argument handling work unprivileged** in a clean container, before any escalation.

**The install report is written**, end to end, in a container that had never seen this code. That is
task 1's mechanism confirmed outside the machine it was written on.

**Refusing beats hanging.** With no answer available the run stopped with
`FAIL: No answer. Set ASSUME_YES=1 to run without prompts.` rather than blocking forever on a prompt
nobody could see. That is the behaviour an unattended run needs, and it already exists.

---

## What it found

### 1. `--only` does not isolate a step

Running `--only "Core utils"` still **created a user account**, because `ask_username` and the
account creation happen in the preamble, above the step framework. Everything before the first
`step` call runs on every invocation.

Defensible — every step needs to know who it is installing for — but it means `--only` is not the
surgical tool it appears to be, and a first-time reader will assume it is. Either the preamble
becomes lazy, or `--only` says plainly what it will still do.

### 2. `.setup-answers` travels with a copy of the tree

It lives at `scripts/setup/machine-setup/.setup-answers`, is correctly gitignored, and is `0600`
root-owned. But it is **inside the repository directory**, so `cp -r` or a tarball of the tree
carries it — which is exactly what happened here: a container that had never run setup came up
already knowing the username `pastilhas` and created that account.

Not a leak (username and install path, nothing secret). It is a surprise, and surprises in an
installer are the expensive kind. Worth moving outside the repo, next to the progress file.

### 3. `adduser` leaks its own prompts

```
Use of uninitialized value $answer in pattern match (m//) at /usr/sbin/adduser line 848.
Try again? [y/N]
```

The account-creation path reaches an interactive `adduser` question the script does not answer.
Harmless here because the run stopped anyway, but on a real unattended install this is a hang.

---

## Coverage this cannot reach

Containers have no init by default, so **`systemctl`, netplan, ufw and the sshd drop-ins were not
exercised**. Those sections can only be verified as "wrote the right file", not "the service came
up". Running privileged containers with systemd would close most of that gap and is the obvious next
step.

**Docker-in-Docker** was not attempted, so the Docker section and Postgres provisioning are
untested. Mounting the host socket would test the section's logic while telling us nothing about the
install path.

**macOS is untestable here entirely.** The 17 skipped sections, the Homebrew paths, the Xcode
command line tools step and the refusal-to-run-as-root are all reasoned from documentation and
unverified by execution.
