#!/bin/sh set -eu REPOSITORY="${1:-}" MODE="${2:-}" TOKEN="${HOMEBREW_PR_TOKEN:-}" usage() { printf '%s\n' 'usage: verify-homebrew-pr-token.sh [--canary]' >&2 } err() { printf 'error: %s\n' "$*" >&2 exit 1 } [ -n "$REPOSITORY" ] || { usage exit 2 } case "$REPOSITORY" in */*) ;; *) usage; exit 2 ;; esac case "$MODE" in ""|--canary) ;; *) usage; exit 2 ;; esac [ -n "$TOKEN" ] || err "HOMEBREW_PR_TOKEN is required to open Formula PRs from official releases" command -v gh >/dev/null 2>&1 || err "gh is required to verify HOMEBREW_PR_TOKEN" TMP_ROOT="$(mktemp -d "${TMPDIR:-/tmp}/dws-homebrew-token-XXXXXX")" CANARY_ROOT="" CANARY_BRANCH="" CANARY_COMMIT="" CANARY_BRANCH_MAY_EXIST="false" CANARY_PR_MAY_EXIST="false" CANARY_PR_NUMBER="" cleanup() { status=$? trap - EXIT INT TERM set +e cleanup_failed="false" if [ "$CANARY_PR_MAY_EXIST" = "true" ]; then cleanup_pr_number="$CANARY_PR_NUMBER" if [ -z "$cleanup_pr_number" ]; then if ! cleanup_pr_number="$(find_canary_pr_number)"; then printf 'error: could not inspect Homebrew token canary PR for branch %s\n' \ "$CANARY_BRANCH" >&2 cleanup_failed="true" fi fi if [ -n "$cleanup_pr_number" ] && ! close_canary_pr "$cleanup_pr_number"; then printf 'error: could not clean up Homebrew token canary PR #%s\n' \ "$cleanup_pr_number" >&2 cleanup_failed="true" fi fi if [ "$CANARY_BRANCH_MAY_EXIST" = "true" ]; then if ! delete_canary_branch >/dev/null 2>&1; then printf 'error: could not clean up Homebrew token canary branch %s\n' \ "$CANARY_BRANCH" >&2 cleanup_failed="true" fi fi if ! rm -rf "$TMP_ROOT"; then printf 'error: could not clean up Homebrew token canary workspace\n' >&2 cleanup_failed="true" fi if [ "$cleanup_failed" = "true" ] && [ "$status" -eq 0 ]; then status=1 fi exit "$status" } trap cleanup EXIT trap 'exit 130' INT trap 'exit 143' TERM response="$TMP_ROOT/repository-response" if ! GH_TOKEN="$TOKEN" gh api \ -i \ -H 'Accept: application/vnd.github+json' \ -H 'X-GitHub-Api-Version: 2026-03-10' \ "repos/$REPOSITORY" \ --jq '[.full_name, (.permissions.push // false), .default_branch] | @tsv' \ > "$response"; then err "HOMEBREW_PR_TOKEN could not authenticate to $REPOSITORY" fi scopes="$( awk ' tolower($0) ~ /^x-oauth-scopes:/ { sub(/^[^:]*:[[:space:]]*/, "") sub(/\r$/, "") print exit } ' "$response" )" credential_kind="fine-grained" if [ -n "$scopes" ]; then [ "$scopes" = "public_repo" ] || { err "classic HOMEBREW_PR_TOKEN must have only public_repo scope" } credential_kind="classic-public_repo" fi repository_state="$(tail -n 1 "$response")" actual_repository="$(printf '%s\n' "$repository_state" | cut -f1)" can_push="$(printf '%s\n' "$repository_state" | cut -f2)" default_branch="$(printf '%s\n' "$repository_state" | cut -f3)" [ "$actual_repository" = "$REPOSITORY" ] || { err "HOMEBREW_PR_TOKEN resolved unexpected repository $actual_repository" } [ "$can_push" = "true" ] || { err "HOMEBREW_PR_TOKEN owner does not have push permission to $REPOSITORY" } [ -n "$default_branch" ] || err "HOMEBREW_PR_TOKEN returned an empty default branch" actor="$( GH_TOKEN="$TOKEN" gh api \ -H 'Accept: application/vnd.github+json' \ -H 'X-GitHub-Api-Version: 2026-03-10' \ user \ --jq '.login' )" || err "HOMEBREW_PR_TOKEN could not resolve its owner" [ -n "$actor" ] || err "HOMEBREW_PR_TOKEN returned an empty owner" GH_TOKEN="$TOKEN" gh api \ -H 'Accept: application/vnd.github+json' \ -H 'X-GitHub-Api-Version: 2026-03-10' \ "repos/$REPOSITORY/pulls?state=open&per_page=1" \ --silent \ || err "HOMEBREW_PR_TOKEN cannot access pull requests in $REPOSITORY" if [ "$MODE" = "--canary" ]; then command -v git >/dev/null 2>&1 || err "git is required to run the Homebrew token canary" nonce="$(basename "$TMP_ROOT" | tr -cd 'A-Za-z0-9')" run_id="${GITHUB_RUN_ID:-manual-$(date +%s)-$nonce}" run_attempt="${GITHUB_RUN_ATTEMPT:-1}" case "$run_id-$run_attempt" in *[!A-Za-z0-9_-]*) err "invalid Homebrew token canary identity" ;; esac CANARY_BRANCH="automation/homebrew-token-canary-$run_id-$run_attempt" CANARY_ROOT="$TMP_ROOT/canary" canonical_remote="https://github.com/$REPOSITORY.git" askpass="$TMP_ROOT/git-askpass.sh" printf '%s\n' \ '#!/bin/sh' \ 'case "$1" in' \ ' *Username*) printf "%s\n" "x-access-token" ;;' \ ' *) printf "%s\n" "$HOMEBREW_PR_TOKEN" ;;' \ 'esac' > "$askpass" chmod 700 "$askpass" export HOMEBREW_PR_TOKEN GIT_ASKPASS="$askpass" GIT_TERMINAL_PROMPT=0 export GIT_CONFIG_NOSYSTEM=1 GIT_CONFIG_SYSTEM=/dev/null GIT_CONFIG_GLOBAL=/dev/null export GIT_CONFIG_COUNT=0 git_with_homebrew_token() { git -c credential.helper= -c http.extraHeader= "$@" } find_canary_pr_number() { owner="${REPOSITORY%%/*}" numbers="$( GH_TOKEN="$TOKEN" gh api \ -H 'Accept: application/vnd.github+json' \ -H 'X-GitHub-Api-Version: 2026-03-10' \ "repos/$REPOSITORY/pulls?state=open&head=$owner:$CANARY_BRANCH&base=$default_branch&per_page=2" \ --jq '.[].number' )" || return 1 case "$numbers" in "") return 0 ;; *[!0-9]*) return 1 ;; *) printf '%s\n' "$numbers" ;; esac } close_canary_pr() { GH_TOKEN="$TOKEN" gh api \ -X PATCH \ -H 'Accept: application/vnd.github+json' \ -H 'X-GitHub-Api-Version: 2026-03-10' \ "repos/$REPOSITORY/pulls/$1" \ -f state=closed \ --silent } delete_canary_branch() { [ -n "$CANARY_COMMIT" ] || return 1 remote_ref="$( git_with_homebrew_token -C "$CANARY_ROOT" ls-remote \ --heads "$canonical_remote" "refs/heads/$CANARY_BRANCH" )" || return 1 [ -n "$remote_ref" ] || return 0 remote_sha="$(printf '%s\n' "$remote_ref" | awk 'NR == 1 { print $1 }')" [ "$remote_sha" = "$CANARY_COMMIT" ] || return 1 git_with_homebrew_token -C "$CANARY_ROOT" push \ --force-with-lease="refs/heads/$CANARY_BRANCH:$CANARY_COMMIT" \ "$canonical_remote" ":refs/heads/$CANARY_BRANCH" } git_with_homebrew_token clone \ --depth=1 \ --no-tags \ --single-branch \ --branch "$default_branch" \ "$canonical_remote" \ "$CANARY_ROOT" >/dev/null \ || err "HOMEBREW_PR_TOKEN cannot clone $REPOSITORY over canonical HTTPS" git -C "$CANARY_ROOT" checkout -b "$CANARY_BRANCH" >/dev/null \ || err "could not create local Homebrew token canary branch" mkdir -p "$CANARY_ROOT/.dws" printf 'run=%s attempt=%s actor=%s\n' "$run_id" "$run_attempt" "$actor" \ > "$CANARY_ROOT/.dws/homebrew-token-canary.txt" git -C "$CANARY_ROOT" config user.name "DWS Release Bot" git -C "$CANARY_ROOT" config user.email "dws-release-bot@example.com" git -C "$CANARY_ROOT" add .dws/homebrew-token-canary.txt git -C "$CANARY_ROOT" commit -m "chore: verify Homebrew token [skip ci]" >/dev/null \ || err "could not create Homebrew token canary commit" CANARY_COMMIT="$(git -C "$CANARY_ROOT" rev-parse HEAD)" CANARY_BRANCH_MAY_EXIST="true" git_with_homebrew_token -C "$CANARY_ROOT" push \ "$canonical_remote" "HEAD:refs/heads/$CANARY_BRANCH" >/dev/null \ || err "HOMEBREW_PR_TOKEN cannot push a canary branch to $REPOSITORY" CANARY_PR_MAY_EXIST="true" canary_pr_url="$( GH_TOKEN="$TOKEN" gh pr create \ --repo "$REPOSITORY" \ --base "$default_branch" \ --head "$CANARY_BRANCH" \ --title "chore: verify Homebrew PR token" \ --body "Automated permission canary. This PR is closed and its branch deleted by the same workflow run." \ --draft )" || err "HOMEBREW_PR_TOKEN cannot create a canary pull request in $REPOSITORY" CANARY_PR_NUMBER="${canary_pr_url##*/}" case "$CANARY_PR_NUMBER" in ""|*[!0-9]*) err "Homebrew token canary returned an invalid pull request URL" ;; esac close_canary_pr "$CANARY_PR_NUMBER" \ || err "HOMEBREW_PR_TOKEN could not close canary PR #$CANARY_PR_NUMBER" CANARY_PR_MAY_EXIST="false" CANARY_PR_NUMBER="" delete_canary_branch >/dev/null \ || err "HOMEBREW_PR_TOKEN could not delete canary branch $CANARY_BRANCH" CANARY_BRANCH_MAY_EXIST="false" fi printf 'Homebrew PR token capability passed for %s as %s (%s)\n' \ "$REPOSITORY" "$actor" "$credential_kind"