#!/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 above the Hyprland block is a machine that boots to # nothing. for fragile in 'dnf swap' 'groupupdate' 'group upgrade' 'gstreamer1-plugins' \ 'flatpak install' 'nvm install' 'curl -fsSL'; do while read -r at; do (( at < hyprland_at )) \ && note "'$fragile' runs at line $at, above the desktop at line $hyprland_at" done < <(grep -n -- "$fragile" "$installer" | grep -v '^\s*#' | cut -d: -f1) done # ── 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"