diff --git a/configs/airootfs/usr/local/bin/omarchy-install-diagnose-media b/configs/airootfs/usr/local/bin/omarchy-install-diagnose-media index 9ce5dfd..9b33701 100755 --- a/configs/airootfs/usr/local/bin/omarchy-install-diagnose-media +++ b/configs/airootfs/usr/local/bin/omarchy-install-diagnose-media @@ -47,6 +47,21 @@ corrupt_package() { printf '%s' "$package" } +# A retry in the same boot never gets as far as a checksum. The first failure +# unlinked the package through the bind mount, so pacman now fails opening it, +# and it names the mirror it read from rather than the cache it writes to. +deleted_package() { + local log match package="" + + for log in "$@"; do + [[ -f $log ]] || continue + match=$(sed -n "s|.*Could not open file ${OFFLINE_MIRROR}/\([^ ]*\).*|\1|p" "$log" | tail -n 1) + [[ -n $match ]] && package="$match" + done + + printf '%s' "$package" +} + expected_checksum() { local package="$1" reader @@ -77,7 +92,14 @@ actual_checksum() { printf '%s' "${sum%% *}" } +# The checksum failure is read first wherever both are in the log: it is the +# attempt that names the cause, and the open failures after it are its wake. package=$(corrupt_package "$@") +deleted="" +if [[ -z $package ]]; then + package=$(deleted_package "$@") + deleted=1 +fi if [[ -z $package ]]; then exit 1 fi @@ -87,6 +109,20 @@ fi # filename has no business containing before printing it. display_package="${package//[^A-Za-z0-9._+:@%-]/}" +# Nothing can be weighed here: the bytes that failed are gone, and the copy in +# the mirror went with them. Say which attempt this is, because the screen +# otherwise repeats the same unreadable error for every retry and the one that +# explains it scrolled away with the first. +if [[ -n $deleted ]]; then + echo "An earlier attempt deleted this package" + echo "pacman rejected it off this ISO and unlinked it from the mirror:" + echo "$display_package" + echo + echo "Reboot before retrying: the deletion is in RAM, so the ISO's copy returns" + echo "The first error after that reboot is the one that names the fault" + exit 0 +fi + expected=$(expected_checksum "$package") actual=$(actual_checksum "$package") diff --git a/test/unit/install-media-diagnosis-test.sh b/test/unit/install-media-diagnosis-test.sh index cbad41d..245afa1 100755 --- a/test/unit/install-media-diagnosis-test.sh +++ b/test/unit/install-media-diagnosis-test.sh @@ -142,6 +142,65 @@ if grep -qF "no longer matches the checksum" <<<"$output"; then fi pass "a deleted package names the medium without claiming which way it failed" +# Every retry in the same boot fails on the open rather than the checksum, and +# pacman names the mirror it read from, not the cache it writes to. +write_retry_log() { + local log="$1" path="${2:-$mirror/$PACKAGE}" + + { + echo "[dashboard] :: Retrieving packages..." + echo "[dashboard] error: failed retrieving file '${path##*/}' from disk : Could not open file $path" + echo "[dashboard] error: failed to commit transaction (download library error)" + echo "[dashboard] ==> ERROR: Failed to install packages to new root" + } >"$log" +} + +write_retry_log "$work/retry.log" + +set +e +output=$(diagnose "$work/retry.log") +status=$? +set -e + +(( status == 0 )) || fail "a retry after the deletion is diagnosed" "exit status $status" +[[ $(head -n 1 <<<"$output") == "An earlier attempt deleted this package" ]] || + fail "a retry is diagnosed as a retry" "$output" +grep -qF "$PACKAGE" <<<"$output" || fail "a retry names the package" "$output" +grep -qF "Reboot before retrying" <<<"$output" || + fail "a retry says how to get the package back" "$output" +pass "a retry after the deletion says so instead of going undiagnosed" + +# A log holding both attempts must answer with the one that names the cause. +# The retries come after it and would otherwise win on recency. +printf 'a package, but damaged\n' >"$mirror/$PACKAGE" +write_mirror_db "$(printf '0%.0s' {1..64})" +{ + cat "$log" + cat "$work/retry.log" +} >"$work/both.log" + +set +e +output=$(diagnose "$work/both.log") +set -e + +[[ $(head -n 1 <<<"$output") == "The install medium is damaged" ]] || + fail "the checksum failure outranks the retries that followed it" "$output" +pass "the checksum failure outranks the retries that followed it" + +# An open failure somewhere other than the mirror was not read off this ISO. +write_retry_log "$work/retry-elsewhere.log" "/var/tmp/build/$PACKAGE" + +set +e +output=$(diagnose "$work/retry-elsewhere.log") +status=$? +set -e + +(( status == 1 )) || fail "an open failure outside the mirror is not diagnosed" "exit $status: $output" +[[ -z $output ]] || fail "an open failure outside the mirror prints nothing" "$output" +pass "an open failure outside the mirror is not blamed on the medium" + +rm "$mirror/$PACKAGE" + # A record with no %SHA256SUM% must not borrow the next record's, which would # compare a real file against another package's checksum. printf 'a package, but damaged\n' >"$mirror/$PACKAGE"