Commit Graph
7 Commits
Author SHA1 Message Date
pastilhasandClaude Opus 5 5cb243eed9 port into a clean script instead of editing the original in place
Your call, and the right one. Editing in place let mis-grouped code sit unnoticed
until it scrolled past in a live run — which is exactly how the four upstream
binaries buried in "System Update & Essentials" were found. Porting forces the
question of where each thing belongs before it runs, not after.

machine-setup.sh now contains only what has actually been worked through:
pre-flight, system update and core packages, command-line tools, and the summary.
1149 lines down to 172. The sections still to come are listed in a NOT PORTED YET
block, in order, and each arrives as its own commit.

The original is beside the other superseded scripts as
scripts/setup-old/setup-ubuntu.sh — verified byte-identical to the live
/root/ubuntu-setup copy — so porting reads from a file in the repo rather than
from root's home.

Two claims trimmed from the ported summary, because they were true of the old
script and not of this one yet: it reported the shell as "zsh (Oh My Zsh +
Starship)" unconditionally, and told you to reconnect as a user it had not
created. Replaced with what pre-flight actually knows — system, role, user.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-08-12 17:30:29 +00:00
pastilhasandClaude Opus 5 7622239949 split the upstream binaries out of the package section
lazydocker, lazygit, starship and fastfetch were buried inside "System Update &
Essentials", after the package install and with no announcement — so a run
appeared to be installing system packages and then started pulling tarballs and
printing a five-shell starship tutorial. They are a different thing: upstream
binaries on their own release cadence, not anything the distribution ships. Now
their own step, announced in the same shape as the package section.

Each is checked before it is fetched. The original re-ran every installer on
every run, which is why a machine that already had starship got it reinstalled
along with its "add this to your ~/.zshrc" instructions — advice this script
does not want followed, since it writes the shell config itself. Its output is
now dropped; errors still surface.

Two real bugs fixed on the way:

  lazygit's asset name was hardcoded to x86_64, so on arm64 the download 404s
  and tar fails partway through the run. It now maps ARCH, and spells the
  architectures the way lazygit does rather than the way we do.

  The version was extracted with `tr -d 'v'`, which deletes every v in the
  string rather than the leading one. `${version#v}` instead.

fastfetch stays a package but stops assuming the PPA is needed: Ubuntu picked
it up in 24.10, so the repository is now checked first and the PPA added only
where the archive has nothing. Verified on this host — noble genuinely has no
candidate, so the PPA is still the only source here.

Verified both branches of tools_install by stubbing the presence check.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-08-12 17:28:46 +00:00
pastilhasandClaude Opus 5 bba6d854bc stop the run at the end of the rewritten sections
A WIP boundary after section 2 so the finished part can be run start to finish
on its own, without the untouched sections below acting on the machine. It moves
down as each section is worked through and goes away when the walk ends.

Also ignores .setup-progress, which the script writes beside itself and is
per-machine. The exit message names it, because with it in place a second run
skips section 2 and the rewritten part cannot be re-felt from scratch.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-08-12 17:25:38 +00:00
pastilhasandClaude Opus 5 dbdef23d29 install what is missing and keep what is there, per package manager
lib/packages.sh, and section 2 wired to it.

The rule it exists to enforce: `apt-get install <present-package>` is not a
no-op, it upgrades the package if the repository has a newer one. On a machine
somebody already uses that silently moves a version they chose, and a setup
script is the last thing that should do that behind their back. pkg_install
queries the package database first and names only the genuinely absent packages
on the command line — a package already installed is never passed to apt at all.

It also says so out loud, every time, because a provisioning run should not be
opaque about what it is doing to the machine:

  :: Core packages — installs what is missing, keeps what you already have
       already here: curl ca-certificates gnupg git jq …
       to install:   btop tmux

Section 2's flat list of 19 is now pkgs_core(), split per package manager rather
than through a canonical-name table with overrides. The names genuinely disagree
(build-essential/base-devel, fd-find/fd) and three of them are not packages
elsewhere at all — apt-transport-https, lsb-release and software-properties-common
are apt concepts that exist to let later steps add the Docker repo and the
fastfetch PPA. A `case $PM` shows what each system actually gets, in one place.

Of those 19, six are load-bearing and the rest are the environment. Only
build-essential reaches beyond itself: it is a meta-package, so on a box with a
pinned gcc it pulls the distribution default alongside. Noted where it is
declared; it is the first thing to move out of core if that ever bites.

apt-get upgrade stays, but as its own announced step — it is the one place that
deliberately moves versions, rather than something that happens as a side effect
of asking for a tool.

DEBIAN_FRONTEND=noninteractive and NEEDRESTART_MODE=a now live inside the
helpers. needrestart has been on by default since Ubuntu 22.04 and stops to ask
which services to restart, which is how an unattended run ends up silently
waiting for a keypress.

dpkg-query on the status field rather than `dpkg -s`, which also succeeds for a
package removed but leaving its config behind — that state would read as present
and never be reinstalled.

Verified against this host's real dpkg database: all 19 report present, and a
mixed list correctly passes only the absent ones through.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-08-12 17:19:46 +00:00
pastilhasandClaude Opus 5 41ff8030e9 ask what the machine is for, once, in pre-flight
MACHINE_ROLE is homelab, vps or dev, and several steps have a different right
answer per role with no way to work it out themselves: whether the address is
yours to pin (static IP), whether the box faces the open internet (fail2ban, SSH
hardening, UFW), and whether it is allowed to sleep (suspend, logind).

Asked in pre-flight rather than at each point of use. The steps that care run
from swap through to the firewall, and being asked "is this a VPS?" for the
fourth time halfway down a provisioning run is how people start answering
without reading.

The default offered is guessed from whether this machine's own address is in
RFC1918 space, which beats asking whether it is virtualised — a homelab is very
often a VM on Proxmox and would be misread as rented — and is the same fact most
of the branches turn on anyway. A graphical session means dev; so does macOS.
It is only ever a suggestion the user confirms.

MACHINE_ROLE in the environment answers it ahead of time for an unattended run,
which is why it is declared with :- rather than a plain assignment. The first
version wiped the caller's value before ask_machine_role ever saw it; caught by
running with MACHINE_ROLE=vps and watching the menu appear anyway.

Verified: guesses vps on this host (public IPv4, no DISPLAY, no display
manager), env override takes, and a bad value fails with the three valid ones
named. Nothing consumes the role yet — the steps get wired as each is worked
through.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-08-12 17:11:47 +00:00
pastilhasandClaude Opus 5 34bb8fc22a put the superseded setup scripts in setup-old, and repair what the move broke
scripts/setup/ is now what the new installer is being built in — machine-setup/
for the box, officer-setup.sh for the platform on top — and everything being
replaced moved to scripts/setup-old/. It still works and is still what to run.

Three things the move broke, and what each needed:

  starship.toml is not an old-setup artifact. os-user-shell.ts reads it at
  RUNTIME to seed a member's ~/.config/starship.toml when their Linux account is
  provisioned, and line 125 reads it inside a try whose catch returns
  "could not read the shell templates" — so account provisioning would have
  failed outright, not degraded. Moved back to scripts/setup/, which is where it
  belongs anyway (one file, both audiences) and which leaves the code correct
  with no edit.

  package.json's `setup` script pointed at a path that no longer exists. It now
  points at officer-setup.sh, where the installer is going, rather than at
  setup-old/ which is temporary.

  officer-setup.sh was created empty. An empty script exits 0, so `bun setup`
  would have reported success while doing nothing — worse than the broken path
  it replaced. It now explains that it is not written yet and exits 1, naming
  the setup-old script to run meanwhile.

Also brought .tmux.conf and ufw-docker-rules.conf in beside machine-setup.sh,
which reads both from SCRIPT_DIR and had been silently skipping them since the
script was vendored. ssh-keys.zip deliberately stays out: it is key material,
and *.zip is ignored.

Comments in os-user-claude.ts, app-store/preflight.ts and two docs still name the
old scripts/setup/setup.sh path. Left alone on purpose — repointing them at
setup-old/ only to repoint them again when officer-setup.sh lands is churn.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-08-12 17:07:28 +00:00
pastilhasandClaude Opus 5 44141faf0a split machine-setup into an entry point and a base library
Structure before the work rather than during it: scripts/machine-setup.sh becomes
scripts/setup/machine-setup/, with the script itself as the entry point and
lib/base.sh holding what every part of it needs.

  machine-setup.sh   pre-flight and the numbered sections, for now
  lib/base.sh        shared state, output, the step/resume machine, prompts,
                     and OS detection

The rule for lib/ is definitions only — nothing there installs, writes or
restarts anything, so sourcing it is safe from anywhere. That is why the ERR
trap stayed in the entry point: a trap is a side effect on whoever sources it.

Behaviour is unchanged. Verified by diffing the moved region against the previous
commit: identical set of functions, and the only differences are added comments,
section banners, fail() reformatted onto three lines, and one new line — a guard
against double-sourcing, which matters because steps will source this directly
once they move out, and a second pass would reset SUMMARY.

The sections are still one 1111-line block below pre-flight; they move into
steps/ as each is worked through. The script also still reads ssh-keys.zip,
.tmux.conf and ufw-docker-rules.conf from SCRIPT_DIR, which is now this
directory, so those three steps warn and skip until the files follow it here.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-08-12 17:02:15 +00:00