Files
dingtalk-workspace-cli/scripts/release/verify-homebrew-pr-token.sh

255 lines
8.2 KiB
Bash
Executable File

#!/bin/sh
set -eu
REPOSITORY="${1:-}"
MODE="${2:-}"
TOKEN="${HOMEBREW_PR_TOKEN:-}"
usage() {
printf '%s\n' 'usage: verify-homebrew-pr-token.sh <owner/repository> [--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"