diff --git a/.github/workflows/dependabot_review_request_cleanup.yml b/.github/workflows/dependabot_review_request_cleanup.yml new file mode 100644 index 0000000..1872ee9 --- /dev/null +++ b/.github/workflows/dependabot_review_request_cleanup.yml @@ -0,0 +1,108 @@ +name: Dismiss Dependabot Review Request + +"on": + pull_request_target: + types: [review_requested] + +permissions: + contents: read + issues: read + pull-requests: write + +concurrency: + group: ${{ github.workflow }}-${{ github.event.pull_request.number }} + cancel-in-progress: false + +jobs: + dismiss-review-request: + if: >- + (github.event.pull_request.user.login == 'dependabot[bot]' || + github.event.pull_request.user.login == 'app/dependabot') && + github.event.requested_reviewer.login == 'Pigbibi' && + (github.actor == 'dependabot[bot]' || github.actor == 'app/dependabot') + runs-on: ubuntu-latest + timeout-minutes: 5 + steps: + - name: Dismiss Pigbibi review request + env: + GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} + PR_NUMBER: ${{ github.event.pull_request.number }} + REVIEWER: Pigbibi + run: | + set -euo pipefail + review_events() { + gh api --paginate \ + "repos/${GITHUB_REPOSITORY}/issues/${PR_NUMBER}/timeline?per_page=100" | + jq -sc --arg reviewer "${REVIEWER}" ' + [.[][] | select( + (.event == "review_requested" or .event == "review_request_removed") and + .requested_reviewer.login == $reviewer + )] + ' + } + + current_review_requests() { + gh pr view "${PR_NUMBER}" \ + --repo "${GITHUB_REPOSITORY}" \ + --json reviewRequests \ + --jq '.reviewRequests[].login' + } + + review_requests="$(current_review_requests)" + if ! grep -Fqx "${REVIEWER}" <<<"${review_requests}"; then + echo "No review request for ${REVIEWER}; nothing to dismiss." >> "${GITHUB_STEP_SUMMARY}" + exit 0 + fi + + events_before="$(review_events)" + latest_event="$(jq -r '.[-1].event // ""' <<<"${events_before}")" + latest_actor="$(jq -r '.[-1].actor.login // ""' <<<"${events_before}")" + case "${latest_event}:${latest_actor}" in + 'review_requested:dependabot[bot]'|'review_requested:app/dependabot') ;; + *) + echo "Latest review event was ${latest_event:-} by ${latest_actor:-}; preserving the request." >> "${GITHUB_STEP_SUMMARY}" + exit 0 + ;; + esac + baseline_count="$(jq 'length' <<<"${events_before}")" + + review_requests="$(current_review_requests)" + if ! grep -Fqx "${REVIEWER}" <<<"${review_requests}"; then + echo "Review request was removed during cleanup; nothing to dismiss." >> "${GITHUB_STEP_SUMMARY}" + exit 0 + fi + + if ! gh api --method DELETE \ + "repos/${GITHUB_REPOSITORY}/pulls/${PR_NUMBER}/requested_reviewers" \ + -f "reviewers[]=${REVIEWER}"; then + review_requests="$(current_review_requests)" + if ! grep -Fqx "${REVIEWER}" <<<"${review_requests}"; then + echo "Review request was removed concurrently; nothing to dismiss." >> "${GITHUB_STEP_SUMMARY}" + exit 0 + fi + exit 1 + fi + + events_after="$(review_events)" + human_intent="$(jq -r --argjson baseline "${baseline_count}" ' + [.[$baseline:][] | select( + .actor.login != "dependabot[bot]" and + .actor.login != "app/dependabot" and + .actor.login != "github-actions[bot]" and + .actor.login != "github-actions" + )][-1].event // "" + ' <<<"${events_after}")" + + if [ "${human_intent}" = 'review_requested' ]; then + review_requests="$(current_review_requests)" + if ! grep -Fqx "${REVIEWER}" <<<"${review_requests}"; then + gh api --method POST \ + "repos/${GITHUB_REPOSITORY}/pulls/${PR_NUMBER}/requested_reviewers" \ + -f "reviewers[]=${REVIEWER}" + fi + echo "Preserved a concurrent human review request." >> "${GITHUB_STEP_SUMMARY}" + elif [ "${human_intent}" = 'review_request_removed' ]; then + echo "Preserved a concurrent human review removal." >> "${GITHUB_STEP_SUMMARY}" + else + echo "Dismissed Dependabot review request for ${REVIEWER}." >> "${GITHUB_STEP_SUMMARY}" + fi