diff --git a/.github/scripts/setup-supply-chain-tools.sh b/.github/scripts/setup-supply-chain-tools.sh index a7e3eaaa0c..2bcc2a1da4 100755 --- a/.github/scripts/setup-supply-chain-tools.sh +++ b/.github/scripts/setup-supply-chain-tools.sh @@ -31,14 +31,20 @@ verify_digest() { fi } -curl --fail --silent --show-error --location --proto '=https' --proto-redir '=https' \ - "https://github.com/sigstore/cosign/releases/download/v${COSIGN_VERSION}/cosign-linux-amd64" \ - --output "${download_dir}/cosign" +# Release downloads retry curl's transient failures (timeouts, 408, 429 and 5xx) a few times, +# because one momentary error from the release host would otherwise fail the job. Every byte +# is still checked against the reviewed pin below, and a persistent failure still stops here. +download() { + curl --fail --silent --show-error --location --proto '=https' --proto-redir '=https' \ + --retry 5 --retry-delay 3 "$1" --output "$2" +} + +download "https://github.com/sigstore/cosign/releases/download/v${COSIGN_VERSION}/cosign-linux-amd64" \ + "${download_dir}/cosign" verify_digest cosign "${COSIGN_SHA256}" "${download_dir}/cosign" -curl --fail --silent --show-error --location --proto '=https' --proto-redir '=https' \ - "https://github.com/anchore/syft/releases/download/v${SYFT_VERSION}/syft_${SYFT_VERSION}_linux_amd64.tar.gz" \ - --output "${download_dir}/syft.tar.gz" +download "https://github.com/anchore/syft/releases/download/v${SYFT_VERSION}/syft_${SYFT_VERSION}_linux_amd64.tar.gz" \ + "${download_dir}/syft.tar.gz" verify_digest syft "${SYFT_SHA256}" "${download_dir}/syft.tar.gz" # Verify both downloads before extracting or installing either tool. Extract diff --git a/scripts/tests/test-setup-supply-chain-tools.sh b/scripts/tests/test-setup-supply-chain-tools.sh index 224a2f5a71..82a7d54b19 100755 --- a/scripts/tests/test-setup-supply-chain-tools.sh +++ b/scripts/tests/test-setup-supply-chain-tools.sh @@ -25,16 +25,18 @@ syft_sha=$(sha256sum "${test_root}/fixtures/syft.tar.gz" | cut -d' ' -f1) cat >"${test_root}/bin/curl" <<'FAKE_CURL' #!/usr/bin/env bash set -euo pipefail -url='' output='' +url='' output='' retry=0 while (( $# )); do case "$1" in -o|--output) output=$2; shift 2 ;; - --proto|--proto-redir) shift 2 ;; + --retry) retry=$2; shift 2 ;; + --proto|--proto-redir|--retry-delay|--retry-max-time) shift 2 ;; -*) shift ;; *) url=$1; shift ;; esac done printf '%s\n' "${url}" >>"${TEST_ROOT}/downloads" +printf '%s %s\n' "${retry}" "${url}" >>"${TEST_ROOT}/retries" case "${url}" in https://github.com/sigstore/cosign/releases/download/v9.9.9/cosign-linux-amd64) [[ ${FAIL_DOWNLOAD:-} != cosign ]] || exit 22 @@ -85,7 +87,7 @@ make_variant() { } run_installer() { - rm -f "${test_root}/installs" "${test_root}/executed" "${test_root}/downloads" "${test_root}/github-path" "${test_root}/installed/"* + rm -f "${test_root}/installs" "${test_root}/executed" "${test_root}/downloads" "${test_root}/retries" "${test_root}/github-path" "${test_root}/installed/"* status=0 output=$(PATH="${test_root}/bin:${test_root}/installed:${PATH}" \ RUNNER_TEMP="${test_root}/runner" GITHUB_PATH="${test_root}/github-path" \ @@ -107,6 +109,11 @@ cmp "${test_root}/fixtures/cosign" "${test_root}/installed/cosign" || fail 'wron cmp "${test_root}/fixtures/syft" "${test_root}/installed/syft" || fail 'wrong syft bytes installed' grep -Fxq 'cosign version' "${test_root}/executed" || fail 'verified cosign never ran' grep -Fxq 'syft version' "${test_root}/executed" || fail 'verified syft never ran' +# A transient 5xx from the release host must not fail the job on its first attempt. +[[ $(wc -l <"${test_root}/retries") -eq 2 ]] || fail 'expected exactly two recorded downloads' +while read -r count url; do + [[ ${count} =~ ^[1-9][0-9]*$ ]] || fail "download does not retry transient failures: ${url}" +done <"${test_root}/retries" if [[ ! -f ${test_root}/github-path ]] || ! grep -Fxq /usr/local/bin "${test_root}/github-path"; then fail 'subsequent workflow steps can select an unverified tool earlier on PATH' fi