diff --git a/.github/workflows/assignee-approval-sync.yml b/.github/workflows/assignee-approval-sync.yml new file mode 100644 index 0000000..783c68a --- /dev/null +++ b/.github/workflows/assignee-approval-sync.yml @@ -0,0 +1,47 @@ +name: Sync bot approval with assignee +on: + pull_request_review: + types: [submitted, dismissed, edited] + pull_request: + types: [assigned, unassigned] + +permissions: + pull-requests: write + +jobs: + sync: + runs-on: ubuntu-latest + if: github.event.pull_request.head.repo.full_name == github.repository && github.event.pull_request.user.login != 'dependabot[bot]' + steps: + - name: Mirror assignee approval as a second (bot) approval + env: + GH_TOKEN: ${{ github.token }} + BOT: "github-actions[bot]" + PR: ${{ github.event.pull_request.number }} + REPO: ${{ github.repository }} + AUTHOR: ${{ github.event.pull_request.user.login }} + run: | + set -euo pipefail + assignees=$(gh pr view "$PR" -R "$REPO" --json assignees -q '.assignees[].login') + reviews=$(gh api "/repos/$REPO/pulls/$PR/reviews" --paginate --jq '.[]' | jq -s '.') + + # Latest review of any non-author assignee is APPROVED? + # (GitHub ignores the author's own approval for required approvals.) + approved=false + for a in $assignees; do + [ "$a" = "$AUTHOR" ] && continue + state=$(echo "$reviews" | jq -r --arg u "$a" \ + '[.[]|select(.user.login==$u)]|last|.state // empty') + [ "$state" = "APPROVED" ] && approved=true + done + + # Bot's active approval = its latest review, only if still APPROVED + botrev=$(echo "$reviews" | jq -r --arg b "$BOT" \ + '([.[]|select(.user.login==$b)]|last) as $r | if $r.state=="APPROVED" then $r.id else empty end') + + if $approved && [ -z "$botrev" ]; then + gh pr review "$PR" -R "$REPO" --approve --body "Auto-approve: assignee approved." + elif ! $approved && [ -n "$botrev" ]; then + gh api -X PUT "/repos/$REPO/pulls/$PR/reviews/$botrev/dismissals" \ + -f message="Assignee approval no longer active — dismissing bot approval." + fi