fix: disk picker fd0 (#112), menu Back guard (#106), offline theme (#113)
Some checks failed
Check / eval (push) Has been cancelled

- #112: list_installable_disks drops fd/loop/sr/zram/<8GiB, largest-first
  so OVMF no longer offers /dev/fd0 as the default wipe target
- #106: tools/check-menu-back.py + checks.menu-back (proved to fail)
- #113: offline theme-switch contract documented (default/pinned only);
  theme-sync run_switch adds offline-oriented hint when no network

Verified: V0 flake check; installer-safety; menu-back; py_compile.
This commit is contained in:
2026-07-15 10:02:19 +01:00
parent 748d4af414
commit e6be5a5770
10 changed files with 202 additions and 49 deletions

View File

@@ -224,13 +224,43 @@ live_disk=""
live_src=$(findmnt -no SOURCE /iso 2>/dev/null || true)
[[ -n "$live_src" ]] && live_disk=$(lsblk -no PKNAME "$live_src" 2>/dev/null || true)
mapfile -t disks < <(lsblk -dpno NAME,SIZE,MODEL,TYPE \
| awk -v skip="/dev/${live_disk:-NONE}" \
'$NF == "disk" && $1 != skip && $1 !~ /loop|zram|sr[0-9]/ {NF--; print}')
[[ ${#disks[@]} -gt 0 ]] || fail "No installable disks found."
# Installable whole disks only (#112): drop floppy/pseudo/tiny devices that
# OVMF and some firmwares expose as TYPE=disk (e.g. /dev/fd0 first in the
# list — a blind Enter would erase nothing useful and break the install).
# Bytes via lsblk -b; 8 GiB floor catches fd0/USB crumbs; REQUIREMENTS still
# recommend ≥40 GiB for a real install.
# NOMARCHY_MIN_DISK_BYTES overrides the floor (tests / expert installs).
MIN_DISK_BYTES="${NOMARCHY_MIN_DISK_BYTES:-$((8 * 1024 * 1024 * 1024))}"
list_installable_disks() {
local skip="/dev/${live_disk:-NONE}"
local name size type model hsize
# NAME SIZE TYPE in bytes (-b); MODEL looked up separately (may contain spaces).
{
while read -r name size type; do
[[ "$type" == "disk" ]] || continue
[[ "$name" == "$skip" ]] && continue
case "$name" in
/dev/loop*|/dev/zram*|/dev/sr*|/dev/fd*|/dev/ram*|/dev/nbd*|/dev/md*)
continue ;;
esac
[[ "$size" =~ ^[0-9]+$ ]] || continue
(( size >= MIN_DISK_BYTES )) || continue
hsize=$(lsblk -dno SIZE "$name" 2>/dev/null || echo "?")
model=$(lsblk -dno MODEL "$name" 2>/dev/null | tr -s '[:space:]' ' ' | sed 's/^ //;s/ $//')
printf '%s %s %s\n' "$name" "$hsize" "${model:-}"
done < <(lsblk -dpbno NAME,SIZE,TYPE 2>/dev/null)
} | sort -k2 -h -r # largest first — never default to a tiny leftover
}
mapfile -t disks < <(list_installable_disks)
[[ ${#disks[@]} -gt 0 ]] || fail "No installable disks found (need a whole disk ≥ $(( MIN_DISK_BYTES / 1024 / 1024 / 1024 )) GiB; floppy/loop/optical excluded)."
if [[ "$UNATTENDED" == "1" ]]; then
TARGET_DISK="${NOMARCHY_DISK:?NOMARCHY_DISK required in unattended mode}"
# Unattended still rejects non-installable targets (CI footgun).
if ! printf '%s\n' "${disks[@]}" | grep -q "^${TARGET_DISK} "; then
fail "$TARGET_DISK is not an installable disk (missing, live medium, or below size floor)."
fi
else
choice=$(printf '%s\n' "${disks[@]}" \
| gum choose --header "Install Nomarchy on which disk? (EVERYTHING on it will be erased)")

View File

@@ -370,9 +370,23 @@ def run_switch() -> None:
"Rebuild FAILED — scroll up for last lines; run nomarchy-doctor",
urgency="critical",
)
# Live ISO pins only the *default* theme's HM generation. Applying
# another preset offline tries to build the world from source (#113).
offline_hint = ""
try:
import socket
socket.create_connection(("1.1.1.1", 53), timeout=1.5).close()
except OSError:
offline_hint = (
" Offline? Only the live ISO's already-pinned theme is "
"guaranteed without a network — other themes need builds "
"or a binary cache. Connect and retry, or apply the default "
"theme (boreal)."
)
die(
"rebuild failed (state already written; fix and re-run). "
"Diagnose: nomarchy-doctor. Recovery: docs/RECOVERY.md"
"Diagnose: nomarchy-doctor. Recovery: docs/RECOVERY.md."
+ offline_hint
)
notify("Changes applied ✓")
# Waybar runs from Hyprland's exec-once (not a systemd unit HM would