refresh Phase 2A transaction and readiness gates

This commit is contained in:
makearmy 2026-07-12 17:36:55 -04:00
parent 2cf249d6f2
commit da03be61a9
23 changed files with 2409 additions and 37 deletions

View file

@ -10,7 +10,10 @@ fail() {
manifest_value() {
key=$1
file=$2
value=$(awk -F= -v k="$key" '$1==k {sub(/^[^=]*=/, ""); print; found++} END {if(found!=1) exit 1}' "$file")
count=$(awk -F= -v k="$key" '$1==k {found++} END {print found+0}' "$file")
rc=$?
if [ "$rc" -ne 0 ] || [ "$count" != 1 ]; then return 1; fi
value=$(awk -F= -v k="$key" '$1==k {sub(/^[^=]*=/, ""); print}' "$file")
rc=$?
if [ "$rc" -ne 0 ]; then return "$rc"; fi
printf '%s\n' "$value"
@ -89,11 +92,130 @@ validate_health_url_file() {
return 0
}
rerun_volatile_checks() {
systemctl --failed --no-legend --plain --no-pager > "$record/volatile-failed-units.txt" 2>&1
capture_failed_unit_fingerprint() {
output=$1
: > "$output"
systemctl --failed --no-legend --plain --no-pager | awk '{print $1}' | LC_ALL=C sort |
while IFS= read -r unit; do
[ -n "$unit" ] || continue
printf '%s|%s|%s|%s|%s|%s|%s|%s|%s|%s|%s\n' \
"$(systemctl show "$unit" -p Id --value --no-pager)" \
"$(systemctl show "$unit" -p Type --value --no-pager)" \
"$(systemctl show "$unit" -p LoadState --value --no-pager)" \
"$(systemctl show "$unit" -p ActiveState --value --no-pager)" \
"$(systemctl show "$unit" -p SubState --value --no-pager)" \
"$(systemctl show "$unit" -p Result --value --no-pager)" \
"$(systemctl show "$unit" -p InvocationID --value --no-pager)" \
"$(systemctl show "$unit" -p StateChangeTimestamp --value --no-pager)" \
"$(systemctl show "$unit" -p FragmentPath --value --no-pager)" \
"$(systemctl show "$unit" -p ExecMainCode --value --no-pager)" \
"$(systemctl show "$unit" -p ExecMainStatus --value --no-pager)"
done > "$output"
return ${PIPESTATUS[0]}
}
verify_failed_unit_disposition() {
output=$1
(cd "$script_dir/failed-unit-disposition" && sha256sum -c SHA256SUMS) \
> "$record/failed-unit-allowlist-checksum-latest.txt" 2>&1
rc=$?
[ "$rc" -eq 0 ] || { fail 'cannot capture current failed units' "$rc"; return $?; }
[ ! -s "$record/volatile-failed-units.txt" ] || { fail 'one or more failed units are present' 45; return $?; }
[ "$rc" -eq 0 ] || { fail 'failed-unit allowlist checksum failed' 45; return $?; }
capture_failed_unit_fingerprint "$output"
rc=$?
[ "$rc" -eq 0 ] || { fail 'cannot fingerprint current failed units' "$rc"; return $?; }
diff -u "$script_dir/failed-unit-disposition/drkonqi-failed-units.allowlist.psv" "$output" \
> "$record/failed-unit-disposition-latest.diff" 2>&1
rc=$?
[ "$rc" -eq 0 ] || { fail 'failed-unit set, state, invocation ID, or fingerprint changed' 45; return $?; }
count=$(wc -l < "$output")
[ "$count" = 234 ] || { fail "failed-unit count changed: expected=234 actual=$count" 45; return $?; }
return 0
}
verify_snapshot_subvolume() {
number=$1
description=$2
snapshot_dir=/.snapshots/$number
snapshot_subvolume=$snapshot_dir/snapshot
[ -d "$snapshot_dir" ] || { fail "Snapper numbered directory missing: $snapshot_dir" 50; return $?; }
btrfs subvolume show "$snapshot_subvolume" > "$record/pre-upgrade-snapshot-subvolume.txt" 2>&1
rc=$?
[ "$rc" -eq 0 ] || { fail "Snapper snapshot is not a verified Btrfs subvolume: $snapshot_subvolume" 50; return $?; }
snapper list --columns number,type,pre-number,date,user,cleanup,description \
> "$record/snapper-snapshots-after-explicit-create.txt" 2>&1
rc=$?
[ "$rc" -eq 0 ] || { fail 'cannot verify explicit snapshot through Snapper' "$rc"; return $?; }
awk -v number="$number" -v description="$description" \
'$1 == number && index($0, description) {print}' \
"$record/snapper-snapshots-after-explicit-create.txt" \
> "$record/pre-upgrade-snapshot-list-match.txt" 2>&1
rc=$?
if [ "$rc" -ne 0 ] || [ ! -s "$record/pre-upgrade-snapshot-list-match.txt" ]; then
fail 'explicit snapshot number and description not found together in Snapper list' 50
return $?
fi
return 0
}
create_explicit_pre_upgrade_snapshot() {
btrfs subvolume show /.snapshots > "$record/snapshots-subvolume-before-create.txt" 2>&1
rc=$?
[ "$rc" -eq 0 ] || { fail '/.snapshots is not a verified Btrfs subvolume' "$rc"; return $?; }
snapper list --columns number > "$record/snapper-numbers-before-explicit.txt" 2>&1
rc=$?
[ "$rc" -eq 0 ] || { fail 'cannot capture Snapper number baseline' "$rc"; return $?; }
grep -E '^[[:space:]]*[0-9]+[[:space:]]*$' "$record/snapper-numbers-before-explicit.txt" | tr -d '[:space:]' | LC_ALL=C sort \
> "$record/snapper-numbers-before-explicit.normalized"
description="le-phase2a-pre-upgrade-$(date -u +%Y%m%dT%H%M%SZ)-$(basename "$record")"
snapshot_output=$(snapper create --type single --cleanup-algorithm number \
--description "$description" --print-number 2> "$record/pre-upgrade-snapshot-create.err")
rc=$?
printf '%s\n' "$snapshot_output" > "$record/pre-upgrade-snapshot-create.out"
[ "$rc" -eq 0 ] || { fail "explicit pre-upgrade Snapper snapshot failed rc=$rc" 50; return $?; }
snapshot_number=$(printf '%s' "$snapshot_output" | tr -d '[:space:]')
case "$snapshot_number" in ''|*[!0-9]*) fail 'Snapper returned an invalid snapshot number' 50; return $? ;; esac
[ "$snapshot_number" -gt 0 ] || { fail 'Snapper returned nonpositive snapshot number' 50; return $?; }
verify_snapshot_subvolume "$snapshot_number" "$description" || return $?
{
printf 'snapshot_number=%s\n' "$snapshot_number"
printf 'description=%s\n' "$description"
printf 'snapshot_directory=/.snapshots/%s\n' "$snapshot_number"
printf 'snapshot_subvolume=/.snapshots/%s/snapshot\n' "$snapshot_number"
} > "$record/pre-upgrade-snapshot.manifest"
btrfs qgroup show / > "$record/btrfs-quota-observation-before-upgrade.txt" 2>&1
rc=$?
if [ "$rc" -ne 0 ]; then
printf 'btrfs-quota-accounting|unavailable-or-disabled|rc=%s|explicit-snapshot-still-verified\n' "$rc" \
>> "$record/warnings-before-upgrade.txt"
fi
return 0
}
verify_snap_pac_snapshots() {
snapper list --columns number > "$record/snapper-numbers-after-transaction.txt" 2>&1
rc=$?
[ "$rc" -eq 0 ] || { fail 'cannot capture post-transaction Snapper numbers' "$rc"; return $?; }
grep -E '^[[:space:]]*[0-9]+[[:space:]]*$' "$record/snapper-numbers-after-transaction.txt" | tr -d '[:space:]' | LC_ALL=C sort \
> "$record/snapper-numbers-after-transaction.normalized"
comm -13 "$record/snapper-numbers-before-explicit.normalized" "$record/snapper-numbers-after-transaction.normalized" \
> "$record/snapper-new-numbers.txt"
new_count=$(wc -l < "$record/snapper-new-numbers.txt")
if [ "$new_count" -lt 3 ]; then
fail "expected explicit plus snap-pac pre/post snapshots; observed new snapshot count=$new_count" 51
return $?
fi
while IFS= read -r number; do
case "$number" in ''|*[!0-9]*) fail 'invalid new Snapper snapshot number' 51; return $? ;; esac
btrfs subvolume show "/.snapshots/$number/snapshot" \
> "$record/snapper-new-subvolume-$number.txt" 2>&1
rc=$?
[ "$rc" -eq 0 ] || { fail "new Snapper snapshot $number is not a Btrfs subvolume" 51; return $?; }
done < "$record/snapper-new-numbers.txt"
return 0
}
rerun_volatile_checks() {
verify_failed_unit_disposition "$record/volatile-failed-units-fingerprint.psv" || return $?
pacman -Dk > "$record/volatile-pacman-Dk.txt" 2>&1
rc=$?
[ "$rc" -eq 0 ] || { fail 'package database consistency failed immediately before execution' "$rc"; return $?; }
@ -161,6 +283,7 @@ main() {
fi
validate_preflight "$2" || return $?
validate_health_url_file "$validated_preflight/health-urls.sanitized" || return $?
script_dir=$(CDPATH= cd -- "$(dirname -- "$0")" && pwd -P)
umask 077
install -d -o root -g root -m 0700 /var/log/le-phase2a-maintenance
@ -179,15 +302,14 @@ main() {
rc=$?
[ "$rc" -eq 0 ] || { fail 'cannot copy validated preflight record' "$rc"; return $?; }
report=/tmp/le-phase2-package-decision-20260712.Mn7mIx/phase2-package-decision-report.md
report_manifest=/tmp/le-phase2-package-decision-20260712.Mn7mIx/SHA256SUMS.main
printf '%s %s\n' 195d74afe121556294318d0ed49e3fb5504088dbb31a4c0786b8adea44f1369d "$report" | sha256sum -c - \
approved_transaction=$script_dir/approved-transaction
(cd "$approved_transaction" && sha256sum -c SHA256SUMS) \
> "$record/approved-report-checksum.txt" 2>&1
rc=$?
[ "$rc" -eq 0 ] || { fail 'approved report checksum failed' "$rc"; return $?; }
cp "$report" "$report_manifest" "$record/"
[ "$rc" -eq 0 ] || { fail 'approved transaction checksum failed' "$rc"; return $?; }
cp -a "$approved_transaction" "$record/approved-transaction"
rc=$?
[ "$rc" -eq 0 ] || { fail 'cannot copy approved report and checksum manifest' "$rc"; return $?; }
[ "$rc" -eq 0 ] || { fail 'cannot copy approved transaction evidence' "$rc"; return $?; }
uname -a > "$record/running-kernel-before.txt" 2>&1
rc=$?; [ "$rc" -eq 0 ] || { fail 'cannot capture running kernel' "$rc"; return $?; }
@ -199,9 +321,9 @@ main() {
"$validated_preflight/running-containers-before.txt" "$validated_preflight/containers-all.psv" \
"$validated_preflight/critical-units-baseline.psv" "$validated_preflight/database-baseline.psv" "$record/"
rc=$?; [ "$rc" -eq 0 ] || { fail 'cannot copy essential rollback baseline evidence' "$rc"; return $?; }
systemctl --failed --no-legend --plain --no-pager > "$record/failed-units-before.txt" 2>&1
rc=$?; [ "$rc" -eq 0 ] || { fail 'cannot capture failed-unit baseline' "$rc"; return $?; }
[ ! -s "$record/failed-units-before.txt" ] || { fail 'failed units appeared before transaction preview' 45; return $?; }
verify_failed_unit_disposition "$record/failed-units-before-fingerprint.psv" || return $?
btrfs subvolume show /.snapshots > "$record/snapshots-subvolume-before-preview.txt" 2>&1
rc=$?; [ "$rc" -eq 0 ] || { fail '/.snapshots is not a Btrfs subvolume' "$rc"; return $?; }
rerun_volatile_checks || return $?
@ -228,9 +350,7 @@ main() {
normalize_transaction "$record/latest-transaction.psv" "$record/latest-normalized-actions.psv" "$record/latest-downgrades.psv"
[ ! -s "$record/latest-downgrades.psv" ] || { fail 'latest transaction contains a downgrade' 14; return $?; }
awk -F'|' '{print $1"|"$2"|"$3"|"$5}' \
/tmp/le-phase2-package-decision-20260712.Mn7mIx/inventory.psv | sort \
> "$record/approved-normalized-actions.psv"
cp "$approved_transaction/normalized-actions.psv" "$record/approved-normalized-actions.psv"
rc=$?
[ "$rc" -eq 0 ] || { fail 'cannot normalize approved preview' "$rc"; return $?; }
diff -u "$record/approved-normalized-actions.psv" "$record/latest-normalized-actions.psv" \
@ -273,11 +393,19 @@ main() {
return $?
fi
create_explicit_pre_upgrade_snapshot || return $?
printf 'At Pacman Y/n, answer no if any package, version, action, count, downgrade status, signature, or repository differs.\n'
script -q -e -f -c 'pacman -Syu --needed rootlesskit slirp4netns openai-codex' "$record/pacman-complete.log"
rc=$?
if [ "$rc" -ne 0 ]; then fail "Pacman returned $rc; stop without ad hoc repair" "$rc"; return $?; fi
verify_snapshot_subvolume "$snapshot_number" "$description" || return $?
snapper list --columns number,type,pre-number,date,user,cleanup,description \
> "$record/snapper-snapshots-after-transaction.txt" 2>&1
rc=$?; [ "$rc" -eq 0 ] || { fail 'cannot inventory Snapper snapshots after transaction' "$rc"; return $?; }
verify_snap_pac_snapshots || return $?
pacman -Q > "$record/packages-after.txt" 2>&1
rc=$?; [ "$rc" -eq 0 ] || { fail 'cannot capture post-transaction package inventory' "$rc"; return $?; }
diff -u "$record/packages-before.txt" "$record/packages-after.txt" > "$record/package-inventory.diff" 2>&1
@ -308,6 +436,7 @@ main() {
printf 'PACKAGE TRANSACTION COMPLETED; REBOOT NOT PERFORMED.\n'
printf 'MAINTENANCE_RECORD=%s\n' "$record"
printf 'PRE_UPGRADE_SNAPPER_SNAPSHOT=%s\n' "$snapshot_number"
printf 'Review all warnings, hooks, services, kernel artifacts, and pacnew/pacsave files.\n'
printf 'Reboot and rootless-Docker provisioning remain separate approval gates.\n'
return 0