diff --git a/arcup/arcup b/arcup/arcup index 3590cd36..1458f24a 100755 --- a/arcup/arcup +++ b/arcup/arcup @@ -6,7 +6,7 @@ set -euo pipefail # NOTE: if you make modifications to this script, please increment the version number. # WARNING: the SemVer pattern: major.minor.patch must be followed as we use it to determine if the script is up to date. -ARCUP_INSTALLER_VERSION="0.2.0" +ARCUP_INSTALLER_VERSION="0.2.1" REPO="${ARC_REPO:-circlefin/arc-node}" if [[ -n "${ARC_REPO:-}" ]] && [[ ! "$ARC_REPO" =~ ^[A-Za-z0-9_.-]+/[A-Za-z0-9_.-]+$ ]]; then @@ -656,7 +656,16 @@ verify_checksum_file() { local archive_name="$3" local expected_checksum expected_name actual_checksum - if ! read -r expected_checksum expected_name < "$checksum_path"; then + if [[ ! -r "$checksum_path" ]]; then + error "Checksum file is missing or unreadable: $checksum_path" + fi + + # `read` reports failure at EOF even when it populated the variables, which is + # what happens when the checksum file has no trailing newline. Inspect the parsed + # value rather than the exit status so a valid single-line file is still accepted. + read -r expected_checksum expected_name < "$checksum_path" || true + + if [[ -z "$expected_checksum" ]]; then error "Checksum file is empty: $checksum_path" fi diff --git a/arcup/test_arcup.sh b/arcup/test_arcup.sh index 49021191..c8c061d6 100755 --- a/arcup/test_arcup.sh +++ b/arcup/test_arcup.sh @@ -47,6 +47,25 @@ expect_fail() { pass "$name" } +# Like expect_fail, but also pins the message. Without this a test only proves the +# call exited non-zero, which it already did before the fix for the wrong reason. +expect_fail_message() { + local name="$1" + local expected="$2" + shift 2 + + if ( "$@" ) >"$TEST_TMP/expect_fail.out" 2>&1; then + cat "$TEST_TMP/expect_fail.out" >&2 + fail "$name" + fi + if ! grep -qF "$expected" "$TEST_TMP/expect_fail.out"; then + printf 'expected message: %s\nactual output:\n' "$expected" >&2 + cat "$TEST_TMP/expect_fail.out" >&2 + fail "$name" + fi + pass "$name" +} + test_version_normalization() { assert_eq "v1.2.3" "$(normalize_version "1.2.3")" "normalizes missing v prefix" assert_eq "v1.2.3" "$(normalize_version "v1.2.3")" "keeps v-prefixed version" @@ -119,6 +138,27 @@ test_checksum_validation() { printf '%s other-asset.tar.gz\n' "$checksum" > "$checksum_file" expect_fail "checksum filename mismatch fails" verify_checksum_file "$archive" "$checksum_file" "$archive_name" + + # A checksum file with no trailing newline is still a valid single-line file; + # `read` reports EOF for it even though the fields parsed fine. + printf '%s %s' "$checksum" "$archive_name" > "$checksum_file" + verify_checksum_file "$archive" "$checksum_file" "$archive_name" + pass "checksum file without trailing newline passes" + + printf '%s' "$checksum" > "$checksum_file" + verify_checksum_file "$archive" "$checksum_file" "$archive_name" + pass "bare checksum without filename or newline passes" + + : > "$checksum_file" + expect_fail_message "empty checksum file fails" "Checksum file is empty" \ + verify_checksum_file "$archive" "$checksum_file" "$archive_name" + + printf '\n' > "$checksum_file" + expect_fail_message "blank-line checksum file fails" "Checksum file is empty" \ + verify_checksum_file "$archive" "$checksum_file" "$archive_name" + + expect_fail_message "missing checksum file fails" "Checksum file is missing or unreadable" \ + verify_checksum_file "$archive" "$TEST_TMP/absent.sha256" "$archive_name" } test_download_error_lists_assets() {