Files
Panama/tests/setup/desktop-first-contract
T
Gabriel Brown f33da41cc6 Panama learns what a server is: from a root login to running containers
A machine's role is now the interview's first question and the one answer
Panama records. Servers get the same shell minus the screen: core packages,
nvm, Bun, Claude Code and Codex (desktops get Codex too), linger, rootless
ports from 80, firewalld, the nginx-bridge network, and a nightly image
updater that replaced watchtower for cause.

server/containers/ carries junior's 23 compose services -- secrets moved to
per-machine .env files that never enter this public repo, every transformed
compose proven to render byte-identical to what is live. 'panama server'
enables, disables and relinks them; nothing here restarts a running service.
'boot --server' walks a fresh VPS from its root login to a normal install.

Five new contracts pin the secrets rule, the catalog's shape, panama-server's
behavior, the role plumbing, and the dotfile classification.

Claude-Session: https://claude.ai/code/session_01NU5JGiN3JfzqrLQB6wmJ1E
2026-08-25 23:11:49 -04:00

156 lines
6.4 KiB
Bash
Executable File

#!/usr/bin/env bash
# The desktop installs before anything that is allowed to fail.
#
# install-packages runs under `set -euo pipefail`, and the Hyprland block used
# to sit near the bottom of it, below a codec swap, two group updates and a
# GStreamer glob. Any one of those exiting non-zero ended the stage where it
# stood. On a machine whose spin never shipped ffmpeg-free, the swap failed, the
# stage died, and the install finished reporting one red line among twenty
# minutes of scrollback -- with no Hyprland on the disk at all.
#
# The rule that prevents it: anything that can fail for a reason outside this
# repository goes BELOW the desktop, and goes through `soft`. What is left above
# the desktop is only what the desktop needs.
#
# This pins the order, not the individual commands, because the next fragile
# thing somebody adds will not be a codec swap.
set -uo pipefail
repo_dir="$(cd "$(dirname "${BASH_SOURCE[0]}")/../.." && pwd)"
installer="$repo_dir/setup/scripts/install-packages"
findings=()
note() { findings+=("$1"); }
[[ -x "$installer" ]] || { printf 'desktop first contract: %s is not executable\n' "$installer" >&2; exit 1; }
line_of() { grep -n "$1" "$installer" | head -1 | cut -d: -f1; }
hyprland_at="$(line_of '^HYPR_FILE=')"
[[ -n "$hyprland_at" ]] || { printf 'desktop first contract: no Hyprland block found\n' >&2; exit 1; }
# ── Nothing fragile above the desktop ───────────────────────────────────────
#
# Named individually rather than by pattern: each is a command whose failure is
# survivable, and each one that EXECUTES above the Hyprland block is a machine
# that boots to nothing.
#
# Executes, not appears: the fragile steps live in functions defined near the
# top (the server role calls them without ever reaching a desktop section),
# and a definition runs nothing. So function bodies are excluded from the
# position scan, and the desktop path's calls to those functions are required
# to sit below the desktop instead.
while IFS= read -r finding; do
[[ -n "$finding" ]] && note "$finding"
done < <(python3 - "$installer" "$hyprland_at" <<'PY'
import re, sys
path, hypr = sys.argv[1], int(sys.argv[2])
lines = open(path, encoding="utf-8").read().splitlines()
in_body = False
body = set()
for i, line in enumerate(lines, 1):
if not in_body and re.match(r'^[a-z_]+\(\)\s*\{', line):
in_body = True
body.add(i)
continue
if in_body:
body.add(i)
if line == '}':
in_body = False
fragile = ['dnf swap', 'groupupdate', 'group upgrade', 'gstreamer1-plugins',
'flatpak install', 'nvm install', 'curl -fsSL']
for i, line in enumerate(lines, 1):
if i >= hypr or i in body or line.strip().startswith('#'):
continue
for needle in fragile:
if needle in line:
print(f"'{needle}' runs at line {i}, above the desktop at line {hypr}")
# The desktop path still has to run the fragile helpers -- below the desktop.
# (The server path calls them above, inside a branch that exits before the
# desktop section; the exit is asserted back in bash.)
for call in ('setup_node', 'install_bun', 'install_claude_code', 'install_codex'):
calls = [i for i, line in enumerate(lines, 1)
if re.match(r'^\s*' + call + r'\s*$', line) and i not in body]
if not calls:
print(f"{call} is never called, so the desktop path skips it")
elif not any(i > hypr for i in calls):
print(f"{call} is only called above the desktop")
PY
)
# The server branch is what excuses fragile calls above the desktop, and only
# because it never falls through into the desktop section.
sed -n '/^if \[\[ "\$ROLE" == server \]\]; then/,/^fi/p' "$installer" | grep -q '^\s*exit 0' \
|| note 'the server branch does not exit before the desktop section'
# ── Everything fragile is actually tolerated ────────────────────────────────
#
# Being below the desktop is only half of it. A bare `dnf swap` below the
# Hyprland block still kills every step after it -- the flatpaks, the extras
# somebody explicitly chose.
# Comments dropped and backslash continuations joined, so a `soft` invocation
# wrapped across three lines reads as the one command it is.
uncommented() { grep -vE '^\s*#' "$installer" | sed -e :a -e '/\\$/N; s/\\\n\s*/ /; ta'; }
while read -r command; do
uncommented | grep -q "soft .*$command" \
|| note "'$command' runs without soft, so its failure still ends the stage"
done <<'FRAGILE'
dnf swap -y 'ffmpeg-free'
dnf swap -y mesa-va-drivers
groupupdate
group upgrade
gstreamer1-plugins
flatpak install -y flathub $FLATPAK_PACKAGES
FRAGILE
grep -q '^soft()' "$installer" \
|| note 'install-packages defines no soft helper, so nothing can be tolerated deliberately'
# ── The desktop failing is still fatal, and still said out loud ─────────────
#
# The inverse mistake: making everything survivable turns a machine with no
# desktop into a run that reports success.
python3 - "$installer" "$hyprland_at" <<'PY' || note 'a missing Hyprland does not stop the stage'
import sys
lines = open(sys.argv[1], encoding="utf-8").read().splitlines()
start = int(sys.argv[2])
after = "\n".join(lines[start:start + 40])
if "rpm -q hyprland" not in after or "exit 1" not in after:
raise SystemExit(1)
PY
uncommented | grep -q 'soft .*HYPR_PACKAGES' \
&& note 'the Hyprland install is tolerated, so a machine with no desktop reports success'
# ── Soft failures are reported ──────────────────────────────────────────────
grep -q 'softly_failed' "$installer" \
|| note 'nothing collects what was stepped over, so a tolerated failure is a silent one'
python3 - "$installer" <<'PY' || note 'the list of stepped-over steps is never printed'
import sys
text = open(sys.argv[1], encoding="utf-8").read()
tail = text[text.rindex("softly_failed"):]
if "printf" not in tail and "log" not in tail:
raise SystemExit(1)
PY
if (( ${#findings[@]} > 0 )); then
mapfile -t findings < <(printf '%s\n' "${findings[@]}" | sort -u)
printf 'desktop first contract: %d finding(s)\n' "${#findings[@]}" >&2
printf ' - %s\n' "${findings[@]}" >&2
exit 1
fi
printf 'desktop first contract: PASS (desktop at line %s, everything fragile below it)\n' "$hyprland_at"