diff options
| author | Craig Jennings <c@cjennings.net> | 2026-08-20 00:17:09 -0700 |
|---|---|---|
| committer | Craig Jennings <c@cjennings.net> | 2026-08-20 00:17:09 -0700 |
| commit | 87ff0b77cdc1d8a66a063cfe471dab78d1966dc4 (patch) | |
| tree | 986d88e47d6fa3887acd5be4b6b595d3cddb9bb0 /scripts | |
| parent | a028aa589056167160b39cf9e023c12dd30dec34 (diff) | |
| download | archsetup-87ff0b77cdc1d8a66a063cfe471dab78d1966dc4.tar.gz archsetup-87ff0b77cdc1d8a66a063cfe471dab78d1966dc4.zip | |
feat(post-rebuild-check): flag a stopped idle daemon and a read-only remote
Both are states where the machine looks finished and isn't, which is the whole point of this script.
Check 7 asks whether hypridle is running. Nothing else notices when it isn't. Idle lock and suspend stop happening, and the laptop runs until its battery is gone. That's how velox reset its RTC on 2026-08-19, which is what dropped it into check 6's clock and DNS deadlock. The check asks whether the daemon is alive rather than why it might not be, so a crash and a stale caffeine surface alike. It's gated on hypridle being installed, since only Hyprland machines get it.
Check 8 asks whether the working repos can push. The installer clones them from the read-only https endpoint. That's right for someone installing archsetup with no key on my server, and wrong for my own machines. Nothing about the tree shows it. velox's dotfiles remote sat that way for four days and announced itself as a 403.
Only my own read-only endpoint is flagged. An https remote elsewhere may push fine through a credential helper, and guessing about hosts this machine doesn't own would stand noise in front of real findings.
Diffstat (limited to 'scripts')
| -rwxr-xr-x | scripts/post-rebuild-check | 124 |
1 files changed, 113 insertions, 11 deletions
diff --git a/scripts/post-rebuild-check b/scripts/post-rebuild-check index 24e99a4..2013b26 100755 --- a/scripts/post-rebuild-check +++ b/scripts/post-rebuild-check @@ -1,6 +1,6 @@ #!/bin/sh # SPDX-License-Identifier: GPL-3.0-or-later -# post-rebuild-check - the five checks a rebuilt machine actually needs. +# post-rebuild-check - the eight checks a rebuilt machine actually needs. # # A rebuilt machine looks finished and isn't. Five gaps surfaced on velox # within two days of the 2026-08-13 reinstall, and three of them LOOKED @@ -25,6 +25,14 @@ # DoT/DNSSEC validation this machine's DNS runs on, so nothing # resolves -- including the NTP pool that would fix the clock; velox # deadlocked exactly this way 2026-08-19 and needed a second device) +# 7. hypridle installed but not running (nothing then triggers idle lock +# or suspend, so a laptop runs until its battery is gone -- which is +# how velox reset the RTC that caused check 6's deadlock in the first +# place; a caffeine remembered from an earlier boot is the known cause) +# 8. a working repo cloned from the read-only https endpoint (correct +# for a stranger with no key on the server, wrong for this machine, +# which finds out at the first push with a 403 -- velox's dotfiles +# remote sat that way for four days after its rebuild) # # The .gitignore rule in check 4 is what scopes it: a tooling path is only # expected where the project's own .gitignore names it, so a project that @@ -58,6 +66,10 @@ # the special value MISSING = no NTP daemon active # PRC_CHRONY_CONF path to chrony.conf (a fixture, under test) -- the # confdir it names is what decides which drop-ins count +# PRC_IDLE_DAEMON pgrep output for hypridle; "" = installed but not +# running, the special value MISSING = not installed +# PRC_REPO_REMOTES newline list of "path origin-url"; an empty URL +# means origin could not be read # PRC_SYSTEMCTL path to the systemctl binary (a fake, under test) # PRC_SYSTEMCTL_TIMEOUT seconds to allow each systemctl call (default 5) # @@ -69,10 +81,11 @@ usage() { cat <<'EOF' post-rebuild-check - verify a rebuilt machine is actually finished -Runs the six checks that caught velox's 2026-08 reinstall gaps: failed +Runs the eight checks that caught velox's 2026-08 reinstall gaps: failed units, present-but-inert user units, orphaned *.example configs, missing -per-project tooling state, the signal-cli registration, and whether time -sync can recover from a wrong clock without DNS. +per-project tooling state, the signal-cli registration, whether time sync +can recover from a wrong clock without DNS, whether anything still +triggers idle lock and suspend, and whether the working repos can push. Usage: post-rebuild-check [--help] @@ -94,6 +107,7 @@ CHECK_FINDINGS=0 FINDING_LINES="" signal_missing="" ntp_missing="" +idle_absent="" # Every systemctl call is bounded. A wedged user manager spins and answers # nothing -- seen live on velox 2026-08-17, where `is-enabled`, `cat`, and @@ -190,7 +204,7 @@ while IFS= read -r line; do unit=${line#*:} finding "$scope unit failed: $unit" done < "$STAGE" -report "check 1/6: failed units" +report "check 1/8: failed units" # --- 2. user unit files present but not enabled --------------------------- @@ -280,7 +294,7 @@ while read -r name state; do esac finding "unit file present but not enabled: $name ($state)" done < "$STAGE" -report "check 2/6: unit files" +report "check 2/8: unit files" # --- 3. *.example files whose real sibling is missing --------------------- @@ -325,7 +339,7 @@ while IFS= read -r root; do [ -e "${ex%.example}" ] || finding "example without its real file: $ex" done < "$WORK/examples" done < "$WORK/roots" -report "check 3/6: local files" +report "check 3/8: local files" # --- 4. gitignore-mode projects missing their tooling --------------------- @@ -379,7 +393,7 @@ todo.org todo\.org inbox inbox EOF done < "$WORK/projects" -report "check 4/6: project tooling" +report "check 4/8: project tooling" # --- 5. signal-cli registration ------------------------------------------- @@ -406,7 +420,7 @@ if [ "$signal_missing" = 1 ]; then elif [ -z "$signal_missing" ] && [ -z "$accounts" ]; then finding "no signal account registered — agent-text relays into this machine, so paging breaks for the whole fleet" fi -report "check 5/6: signal registration" +report "check 5/8: signal registration" # --- 6. NTP can recover a wrong clock without DNS ------------------------- # @@ -483,7 +497,95 @@ else finding "every NTP source is named by hostname — a wrong clock breaks DNS, so nothing can resolve them and the clock stays wrong" fi fi -report "check 6/6: NTP bootstrap" +report "check 6/8: NTP bootstrap" + +# --- 7. the idle daemon survives session start ---------------------------- +# +# A laptop that never sleeps has no symptom until the battery is gone, so +# nothing surfaces this without being asked. On velox 2026-08-19 hypridle +# started cleanly at 15:29:48 and `settings restore` killed it six seconds +# later, replaying a caffeine stored in an earlier boot. The machine ran +# 11h40m fully awake on battery, died when it flattened, and reset its RTC -- +# which took DNS down with it, the same deadlock check 6 exists for. The +# desktop looked correct throughout. +# +# Behavioural on purpose: this asks whether the daemon is alive, not why it +# might not be, so a stale caffeine, a crash, and a broken config all surface +# the same way. Gated on hypridle being installed, because that is what marks +# a machine as using it -- archsetup installs it only for Hyprland, so a +# headless or dwm box would otherwise report a finding on every run. + +if [ -n "${PRC_IDLE_DAEMON+set}" ]; then + idle_pids=$PRC_IDLE_DAEMON + if [ "$idle_pids" = "MISSING" ]; then + idle_pids="" + idle_absent=1 + fi +elif command -v hypridle >/dev/null 2>&1; then + # pgrep exits non-zero with no match, which is the not-running case rather + # than a probe failure, so the || keeps `set -e`-style callers out of it. + idle_pids=$(pgrep -x hypridle 2>/dev/null) || idle_pids="" +else + idle_pids="" + idle_absent=1 +fi + +if [ "$idle_absent" = 1 ]; then + : # hypridle is not part of this machine -- nothing to check +elif [ -z "$idle_pids" ]; then + finding "hypridle is installed but not running — nothing triggers idle lock or suspend, so this machine stays awake until its battery is gone; a caffeine remembered from an earlier boot is the known cause" +fi +report "check 7/8: idle daemon" + +# --- 8. working repos cloned from the read-only endpoint ------------------ +# +# archsetup clones the user's own archsetup and dotfiles from +# https://git.cjennings.net/..., which serves anonymous clones and refuses +# pushes. That default is correct for a stranger installing archsetup -- they +# have no key on the server -- and wrong for this machine, which has to push. +# ARCHSETUP_REPO / DOTFILES_REPO override it, but only where they are +# configured: a curl|bash install, or a rebuild from a stock ISO, takes the +# default straight back. +# +# Nothing about the tree shows it. The clone is complete and ordinary, and the +# machine finds out at the first push, with a 403 -- which is how velox's +# dotfiles remote was found on 2026-08-17, four days after its rebuild, by +# which time the same rebuild's shallow clone had already answered a +# credential-history question wrongly. +# +# Only the read-only endpoint is flagged. An https remote elsewhere may be +# perfectly pushable through a credential helper, and guessing about hosts +# this machine does not own would stand noise in front of the real findings. + +if [ -n "${PRC_REPO_REMOTES+set}" ]; then + repo_remotes=$PRC_REPO_REMOTES +else + repo_remotes="" + for repo in "$HOME/code/archsetup" "$HOME/.dotfiles"; do + # -e not -d: a worktree or submodule .git is a file naming the gitdir. + [ -e "$repo/.git" ] || continue + # A repo with no origin still gets a line, with an empty URL, so the + # loop below reports it rather than skipping it into a silent pass. + repo_url=$(git -C "$repo" remote get-url origin 2>/dev/null) + repo_remotes="${repo_remotes}${repo} ${repo_url} +" + done +fi + +stage "$repo_remotes" +while IFS= read -r repo_line; do + [ -n "$repo_line" ] || continue + repo_path=${repo_line%% *} + repo_url=${repo_line#"$repo_path"} + repo_url=${repo_url# } + case "$repo_url" in + "") + finding "$repo_path: origin could not be read — the remote was not checked" ;; + https://git.cjennings.net/*|https://cjennings.net/*) + finding "$repo_path: origin is the read-only endpoint ($repo_url) — git push returns 403; set the ssh form, or ARCHSETUP_REPO/DOTFILES_REPO before installing" ;; + esac +done < "$STAGE" +report "check 8/8: repo remotes" # --- summary -------------------------------------------------------------- @@ -491,5 +593,5 @@ if [ "$TOTAL_FINDINGS" -eq 0 ]; then echo "all checks clean" exit 0 fi -echo "$TOTAL_FINDINGS finding(s) across 6 checks" +echo "$TOTAL_FINDINGS finding(s) across 8 checks" exit 1 |
