feat(doctor): #77 hibernate/sleep section (VISION § C)
All checks were successful
Check / eval (push) Successful in 3m41s
All checks were successful
Check / eval (push) Successful in 3m41s
nomarchy-doctor gains a read-only hibernate/sleep section (the highest-ROI Day-2 gap, a natural #76 follow-on): resume device on the kernel cmdline (+ resume_offset for a swapfile), disk swap >= RAM, zram active, and any suspend/hibernate error in the previous boot's kernel log. Self-gates (skip on swap=0) and emits only ok/warn/skip, so it never fails the sheet. Adds `hibernate` to the checks.doctor probe list so the row is asserted. Fixes a set -euo pipefail hazard the first VM run caught (exit 1, no verdict): a no-match grep inside $(...) and a `cond && action` both abort under set -e — now guarded with || true / || echo 0 / if. Recorded in MEMORY as a reusable gotcha for writeShellScriptBin scripts. Verification: V2 PASS — checks.doctor VM test green (fresh build, exit 0; asserts the hibernate row renders, healthy run stays 0). V1 — ran on real LUKS+@swap hardware: all three ok rows render (resume set, swap 34G >= RAM, zram 16G) and the sheet reaches its verdict. V0 bash -n + flake check. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
@@ -263,6 +263,62 @@ if [ "$bat_seen" -eq 1 ] && [ "$bat_limited" -eq 0 ]; then
|
||||
skip "battery charge limit (none set — charges to 100%)"
|
||||
fi
|
||||
|
||||
# ── hibernate / sleep (BACKLOG #76) ──────────────────────────────────
|
||||
# Read-only: is there a working hibernate path? zram is RAM-only and can't
|
||||
# hold a resume image, so hibernation needs a disk swap (partition or file)
|
||||
# PLUS resume= wiring; a swapfile additionally needs resume_offset. A
|
||||
# swap=0 machine opts out cleanly and this section skips. Never fails the
|
||||
# sheet — an absent or under-sized swap is advisory, not a running fault.
|
||||
# (|| echo 0 keeps set -euo pipefail happy if a proc file is unreadable.)
|
||||
disk_swap_kb=$(awk 'NR>1 && $1 !~ /zram/ {s+=$3} END{print s+0}' /proc/swaps 2>/dev/null || echo 0)
|
||||
zram_kb=$(awk 'NR>1 && $1 ~ /zram/ {s+=$3} END{print s+0}' /proc/swaps 2>/dev/null || echo 0)
|
||||
ram_kb=$(awk '/^MemTotal:/ {print $2}' /proc/meminfo 2>/dev/null || echo 0)
|
||||
|
||||
if [ "${disk_swap_kb:-0}" -gt 0 ]; then
|
||||
if grep -q 'resume=' /proc/cmdline 2>/dev/null; then
|
||||
ok "hibernate: resume device set on the kernel cmdline"
|
||||
# A swapfile also needs resume_offset to locate the image within it.
|
||||
if awk 'NR>1 && $1 !~ /zram/ && $2=="file"{f=1} END{exit !f}' /proc/swaps 2>/dev/null \
|
||||
&& ! grep -q 'resume_offset=' /proc/cmdline 2>/dev/null; then
|
||||
warn "swapfile in use but no resume_offset= on the cmdline — resume will fail" \
|
||||
"add boot.kernelParams resume_offset (docs/MIGRATION.md → Enabling hibernation)"
|
||||
fi
|
||||
else
|
||||
warn "disk swap present but no resume= on the kernel cmdline — hibernate won't resume" \
|
||||
"set boot.resumeDevice (docs/MIGRATION.md → Enabling hibernation)"
|
||||
fi
|
||||
if [ "${ram_kb:-0}" -gt 0 ] && [ "$disk_swap_kb" -lt "$ram_kb" ]; then
|
||||
warn "disk swap ($(numfmt --to=iec $((disk_swap_kb * 1024)))) is smaller than RAM ($(numfmt --to=iec $((ram_kb * 1024)))) — a full hibernate image may not fit" \
|
||||
"size swap ≥ RAM (docs/MIGRATION.md → Enabling hibernation)"
|
||||
else
|
||||
ok "hibernate: disk swap ≥ RAM ($(numfmt --to=iec $((disk_swap_kb * 1024))))"
|
||||
fi
|
||||
else
|
||||
skip "hibernate (no disk swap — swap=0 opt-out; zram alone can't resume)"
|
||||
fi
|
||||
|
||||
# zram compressed-RAM swap — the memory-pressure layer oom.nix ships on
|
||||
# by default (#76). Its absence on a Nomarchy box means something is off.
|
||||
if [ "${zram_kb:-0}" -gt 0 ]; then
|
||||
ok "zram compressed-RAM swap active ($(numfmt --to=iec $((zram_kb * 1024))))"
|
||||
else
|
||||
warn "zram swap not active (the default memory-pressure layer)" \
|
||||
"expected on by default — check modules/nixos/oom.nix, then sys-rebuild"
|
||||
fi
|
||||
|
||||
# Best-effort: a suspend/hibernate failure recorded in the previous boot's
|
||||
# kernel log. Needs journal read access; silently no-ops without it. The
|
||||
# `|| true` keeps a no-match grep / missing -1 boot from tripping set -e.
|
||||
if command -v journalctl >/dev/null 2>&1; then
|
||||
pmerr=$(journalctl -b -1 -k --no-pager 2>/dev/null \
|
||||
| grep -iE 'PM: .*(hibernat|suspend).*(fail|error)|Failed to (hibernate|suspend)' \
|
||||
| tail -n1 || true)
|
||||
if [ -n "$pmerr" ]; then
|
||||
warn "a suspend/hibernate error is in the previous boot's log" \
|
||||
"review: journalctl -b -1 -k -g 'hibernat|suspend'"
|
||||
fi
|
||||
fi
|
||||
|
||||
# ── verdict ──────────────────────────────────────────────────────────
|
||||
echo
|
||||
if [ "$fails" -eq 0 ]; then
|
||||
|
||||
Reference in New Issue
Block a user