From 364b183b48576a91fbc50546eb76d1fda1300706 Mon Sep 17 00:00:00 2001 From: SarthakWade Date: Thu, 10 Sep 2026 22:32:36 +0530 Subject: [PATCH] fix: authenticate and throttle cask polling --- .github/workflows/update-cask.yml | 9 +++-- scripts/test-update-cask.sh | 66 +++++++++++++++++++++++++++++++ scripts/update-cask.sh | 32 ++++++++++++--- 3 files changed, 98 insertions(+), 9 deletions(-) create mode 100755 scripts/test-update-cask.sh diff --git a/.github/workflows/update-cask.yml b/.github/workflows/update-cask.yml index b3fdf19..2688924 100644 --- a/.github/workflows/update-cask.yml +++ b/.github/workflows/update-cask.yml @@ -5,8 +5,9 @@ on: paths: - ".github/workflows/update-cask.yml" - "scripts/update-cask.sh" + - "scripts/test-update-cask.sh" schedule: - - cron: "17,47 * * * *" + - cron: "17 5 * * *" workflow_dispatch: inputs: version: @@ -29,8 +30,9 @@ jobs: - uses: actions/checkout@v7 - name: Validate updater run: | - bash -n scripts/update-cask.sh - shellcheck scripts/update-cask.sh + bash -n scripts/update-cask.sh scripts/test-update-cask.sh + shellcheck scripts/update-cask.sh scripts/test-update-cask.sh + ./scripts/test-update-cask.sh update: if: github.event_name != 'pull_request' @@ -41,6 +43,7 @@ jobs: - name: Verify release and render cask id: render env: + GITHUB_TOKEN: ${{ github.token }} VERSION: ${{ inputs.version }} run: | set +e diff --git a/scripts/test-update-cask.sh b/scripts/test-update-cask.sh new file mode 100755 index 0000000..3fb42b1 --- /dev/null +++ b/scripts/test-update-cask.sh @@ -0,0 +1,66 @@ +#!/bin/bash +set -euo pipefail + +ROOT="$(cd "$(dirname "$0")/.." && pwd -P)" +TEMP_DIRECTORY="$(mktemp -d "${TMPDIR:-/tmp}/headless-cask-test.XXXXXX")" +trap 'rm -rf "$TEMP_DIRECTORY"' EXIT + +cat > "$TEMP_DIRECTORY/curl" <<'MOCK' +#!/bin/bash +set -euo pipefail + +printf '%s\n' "$*" >> "$MOCK_CURL_ARGUMENTS" +if [[ "$*" == *"/releases/latest"* ]]; then + printf '{"tag_name":"v1.2.3"}\n' + exit 0 +fi + +output="" +while [[ "$#" -gt 0 ]]; do + if [[ "$1" == "--output" ]]; then + output="$2" + shift 2 + continue + fi + shift +done +[[ -z "$output" ]] || : > "$output" +printf '%s' "$MOCK_HTTP_STATUS" +exit 22 +MOCK +chmod +x "$TEMP_DIRECTORY/curl" + +run_case() { + local expected_exit="$1" + local http_status="$2" + local output="$TEMP_DIRECTORY/output-$http_status" + local result + + set +e + PATH="$TEMP_DIRECTORY:$PATH" \ + GITHUB_TOKEN="test-token" \ + MOCK_CURL_ARGUMENTS="$TEMP_DIRECTORY/curl-arguments-$http_status" \ + MOCK_HTTP_STATUS="$http_status" \ + "$ROOT/scripts/update-cask.sh" > "$output" 2>&1 + result=$? + set -e + + [[ "$result" -eq "$expected_exit" ]] || { + echo "Expected HTTP $http_status to exit $expected_exit, got $result" >&2 + cat "$output" >&2 + exit 1 + } + grep -q 'Authorization: Bearer test-token' "$TEMP_DIRECTORY/curl-arguments-$http_status" +} + +run_case 78 404 +grep -q 'predates the signed distribution contract' "$TEMP_DIRECTORY/output-404" + +run_case 22 403 +grep -q 'failed to fetch the checksum manifest (HTTP 403)' "$TEMP_DIRECTORY/output-403" +if grep -q 'predates the signed distribution contract' "$TEMP_DIRECTORY/output-403"; then + echo "Transient HTTP failures must not be treated as legacy releases" >&2 + exit 1 +fi + +echo "Cask updater HTTP handling tests passed" diff --git a/scripts/update-cask.sh b/scripts/update-cask.sh index 081ade6..32f950c 100755 --- a/scripts/update-cask.sh +++ b/scripts/update-cask.sh @@ -9,11 +9,20 @@ TEMP_DIRECTORY="$(mktemp -d "${TMPDIR:-/tmp}/headless-cask-update.XXXXXX")" trap 'rm -rf "$TEMP_DIRECTORY"' EXIT curl_release() { - curl --fail --silent --show-error --location \ - --proto '=https' --tlsv1.2 \ - -H 'Accept: application/vnd.github+json' \ - -H 'X-GitHub-Api-Version: 2022-11-28' \ - "$1" + local curl_arguments=( + --fail + --silent + --show-error + --location + --proto '=https' + --tlsv1.2 + --header 'Accept: application/vnd.github+json' + --header 'X-GitHub-Api-Version: 2022-11-28' + ) + if [[ -n "${GITHUB_TOKEN:-}" ]]; then + curl_arguments+=(--header "Authorization: Bearer $GITHUB_TOKEN") + fi + curl "${curl_arguments[@]}" "$@" } if [[ -z "$VERSION" ]]; then @@ -27,10 +36,21 @@ fi ASSET="Headless-${VERSION}-macos.zip" BASE_URL="https://github.com/$REPOSITORY/releases/download/v${VERSION}" -if ! curl_release "$BASE_URL/SHA256SUMS" > "$TEMP_DIRECTORY/SHA256SUMS"; then +set +e +CHECKSUM_HTTP_STATUS="$(curl_release \ + --output "$TEMP_DIRECTORY/SHA256SUMS" \ + --write-out '%{http_code}' \ + "$BASE_URL/SHA256SUMS")" +CHECKSUM_RESULT=$? +set -e +if [[ "$CHECKSUM_RESULT" -ne 0 && "$CHECKSUM_HTTP_STATUS" == "404" ]]; then echo "Homebrew update: Headless $VERSION predates the signed distribution contract; no cask update" >&2 exit 78 fi +if [[ "$CHECKSUM_RESULT" -ne 0 ]]; then + echo "Homebrew update: failed to fetch the checksum manifest (HTTP $CHECKSUM_HTTP_STATUS)" >&2 + exit "$CHECKSUM_RESULT" +fi curl_release "$BASE_URL/$ASSET" > "$TEMP_DIRECTORY/$ASSET" EXPECTED_SHA256="$(awk -v asset="$ASSET" '$2 == asset { print $1 }' "$TEMP_DIRECTORY/SHA256SUMS")"