Ubuntu 24.04, Debian 12, Arch and Fedora 41. OS and package-manager detection
correct on all four; --help works unprivileged; and the install report is written
end to end in a container that had never seen this code — task 1's mechanism
confirmed off the machine it was written on.
Three real findings.
--only does not isolate a step. Running --only "Core utils" still created a user
account, because ask_username and the account creation sit in the preamble above
the step framework, so everything before the first `step` runs every time. It is
defensible and it is not what the flag appears to promise.
.setup-answers travels with a copy of the tree. Correctly gitignored and 0600,
but it lives inside the repository directory, so `cp -r` carries it — a container
that had never run setup came up already knowing the username and created that
account. Nothing secret in it; it is a surprise, which in an installer is the
expensive kind.
adduser leaks its own interactive prompt ("Try again? [y/N]") on the
account-creation path. Harmless here because the run had already stopped, but a
hang on a real unattended install.
Also records what containers cannot reach: no init means systemd, netplan, ufw
and the sshd drop-ins are only verifiable as "wrote the right file"; Docker and
Postgres are untested; macOS is unreachable entirely and everything about it is
reasoned rather than executed.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
3.1 KiB
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.