|
1 | 1 | name: Auto-approve admin PRs |
2 | 2 |
|
3 | | -# Automatically approve a pull request when its author is a member of the |
4 | | -# Aswincloud/admins team. It ONLY approves — it never merges, so you stay in |
5 | | -# control of when to merge and can push more changes first. This workflow |
6 | | -# re-fires on each push; whether that dismisses a prior approval depends on the |
7 | | -# repo's stale-review-dismissal setting (off on this repo). |
8 | | -# |
9 | | -# Two identities, split by a hard GitHub constraint: |
10 | | -# - Membership read -> "Aswincloud Bot" GitHub App (short-lived token minted |
11 | | -# per run; needs Members: Read). Replaces the old ORG_READ_TOKEN PAT. |
12 | | -# - The approval -> WRITE_ACCESS_PAT, a real write-collaborator USER. |
13 | | -# This is REQUIRED: GitHub does not count an app/bot review toward a |
14 | | -# ruleset's required-approval count — only a user-with-write review does. |
15 | | -# (We proved this: the app's approval left reviewDecision=REVIEW_REQUIRED.) |
16 | | -# Secrets: APP_ID, APP_PRIVATE_KEY (app) and WRITE_ACCESS_PAT (approver). |
17 | | -# |
18 | | -# pull_request_target runs in the BASE repo context (so the secrets are |
19 | | -# available even for fork PRs). Safe here: the job never checks out or runs the |
20 | | -# PR's code — it only calls the GitHub API. |
21 | | - |
| 3 | +# Thin caller — the logic lives in the org-level reusable workflow so it's |
| 4 | +# defined once and reused everywhere. See Aswincloud/.github. |
22 | 5 | on: |
23 | 6 | pull_request_target: |
24 | 7 | types: [opened, reopened, ready_for_review, synchronize] |
25 | 8 |
|
26 | | -permissions: {} # the App token carries the perms; the built-in token needs none |
27 | | - |
28 | | -# One run per PR; a newer event supersedes an in-flight one. |
29 | 9 | concurrency: |
30 | 10 | group: auto-approve-${{ github.event.pull_request.number }} |
31 | 11 | cancel-in-progress: true |
32 | 12 |
|
33 | 13 | jobs: |
34 | 14 | approve: |
35 | | - runs-on: ubuntu-latest |
36 | | - # Skip drafts — approve once it's a real review candidate. |
37 | | - if: github.event.pull_request.draft == false |
38 | | - steps: |
39 | | - # Mint a short-lived installation token for the Aswincloud Bot app. |
40 | | - # owner (no repositories:) => an org-level token that can read org teams |
41 | | - # AND act on this org's repos. |
42 | | - - name: Mint app token |
43 | | - id: app |
44 | | - uses: actions/create-github-app-token@bcd2ba49218906704ab6c1aa796996da409d3eb1 # v3.2.0 |
45 | | - with: |
46 | | - app-id: ${{ secrets.APP_ID }} |
47 | | - private-key: ${{ secrets.APP_PRIVATE_KEY }} |
48 | | - owner: ${{ github.repository_owner }} |
49 | | - |
50 | | - # 1) Is the PR author an ACTIVE member of the admins team? |
51 | | - - name: Check admins-team membership |
52 | | - id: member |
53 | | - env: |
54 | | - GH_TOKEN: ${{ steps.app.outputs.token }} |
55 | | - AUTHOR: ${{ github.event.pull_request.user.login }} |
56 | | - ORG: Aswincloud |
57 | | - TEAM: admins |
58 | | - run: | |
59 | | - set -euo pipefail |
60 | | - state="$(gh api "orgs/${ORG}/teams/${TEAM}/memberships/${AUTHOR}" \ |
61 | | - --jq '.state' 2>/dev/null || true)" |
62 | | - if [ "${state}" = "active" ]; then |
63 | | - echo "✓ @${AUTHOR} is an active member of @${ORG}/${TEAM}." |
64 | | - echo "is_admin=true" >> "$GITHUB_OUTPUT" |
65 | | - else |
66 | | - echo "↷ @${AUTHOR} is not an active member of @${ORG}/${TEAM} — nothing to do." |
67 | | - echo "is_admin=false" >> "$GITHUB_OUTPUT" |
68 | | - fi |
69 | | -
|
70 | | - # 2) Approve the PR — only when the author is an admin. |
71 | | - # This uses WRITE_ACCESS_PAT (a real write-collaborator USER), not the |
72 | | - # app token, on purpose: GitHub does NOT count an app/bot review toward |
73 | | - # a ruleset's required-approval count — only a review from a user with |
74 | | - # write access satisfies it. The approval is all this step does; it |
75 | | - # never merges, so you stay in control of WHEN it merges and can push |
76 | | - # further changes first. This workflow re-fires on each push; whether a prior |
77 | | - # approval is dismissed depends on the stale-review setting. |
78 | | - - name: Approve the PR |
79 | | - if: steps.member.outputs.is_admin == 'true' |
80 | | - env: |
81 | | - GH_TOKEN: ${{ secrets.WRITE_ACCESS_PAT }} |
82 | | - REPO: ${{ github.repository }} |
83 | | - PR: ${{ github.event.pull_request.number }} |
84 | | - AUTHOR: ${{ github.event.pull_request.user.login }} |
85 | | - HEAD_SHA: ${{ github.event.pull_request.head.sha }} |
86 | | - ORG: Aswincloud |
87 | | - TEAM: admins |
88 | | - run: | |
89 | | - set -euo pipefail |
90 | | -
|
91 | | - ME="$(gh api user --jq '.login')" |
92 | | -
|
93 | | - # Don't stack reviews: if this user already approved this exact commit, |
94 | | - # stop. (Re-runs on `synchronize` then become no-ops.) |
95 | | - already="$(gh api "repos/${REPO}/pulls/${PR}/reviews" --paginate \ |
96 | | - --jq "[.[] | select(.user.login==\"${ME}\" and .state==\"APPROVED\" and .commit_id==\"${HEAD_SHA}\")] | length")" |
97 | | - if [ "${already}" -gt 0 ]; then |
98 | | - echo "↷ Already approved ${HEAD_SHA} as @${ME} — skipping." |
99 | | - exit 0 |
100 | | - fi |
101 | | -
|
102 | | - # GitHub forbids approving your OWN PR, so if the token's user is the |
103 | | - # author, comment instead of failing. |
104 | | - if [ "${ME}" = "${AUTHOR}" ]; then |
105 | | - echo "↷ Token user (@${ME}) is the PR author — GitHub blocks self-approval; leaving a note." |
106 | | - gh pr comment "${PR}" --repo "${REPO}" \ |
107 | | - --body "Auto-approve skipped: @${AUTHOR} is an admin, but the approver account is the author and GitHub doesn't allow approving your own PR." || true |
108 | | - exit 0 |
109 | | - fi |
110 | | -
|
111 | | - gh pr review "${PR}" --repo "${REPO}" --approve \ |
112 | | - --body "Auto-approved: @${AUTHOR} is a member of @${ORG}/${TEAM}." |
113 | | - echo "✓ Approved PR #${PR} as @${ME}." |
| 15 | + uses: Aswincloud/.github/.github/workflows/auto-approve.yml@main |
| 16 | + secrets: inherit |
0 commit comments