|
1 | 1 | name: First contribution |
2 | 2 |
|
3 | | -# Thanks someone the first time a PR of theirs is merged. On merge rather than on open: |
4 | | -# at that point they've actually given something, so it reads as thanks instead of a pitch. |
| 3 | +# Thanks someone the first time a PR of theirs is merged. On merge rather than on open: at |
| 4 | +# that point they've actually given something, so it reads as thanks instead of a pitch. |
5 | 5 | # |
6 | | -# Note on the check: author_association is NOT usable here. Merging a PR promotes its author |
7 | | -# from FIRST_TIME_CONTRIBUTOR to CONTRIBUTOR, and the payload delivered with the `closed` |
8 | | -# event already carries the new value — so a condition on it never matches. Counting the |
9 | | -# author's merged PRs is the signal that survives that. |
| 6 | +# On `push` to main rather than `pull_request_target: closed`. The 403s that prompted the |
| 7 | +# change turned out to be the permissions block below, not the trigger — so treat this as a |
| 8 | +# preference, not a fix: a push to main is plainly not fork-triggered, which takes the whole |
| 9 | +# question of what token a fork PR gets off the table. `pull_request_target` would very |
| 10 | +# possibly work now too; it was never tested with the permission right, because the |
| 11 | +# permission was wrong the entire time. |
10 | 12 | # |
11 | | -# pull_request_target is required to comment on a fork's PR — it runs with the base repo's |
12 | | -# permissions. It therefore MUST NOT check out or execute the PR's code; this job only |
13 | | -# posts a comment, and has no checkout step for that reason. |
| 13 | +# Every run before that fix was green. A 403 here only raises a ::warning::, and nobody reads |
| 14 | +# a warning on a green run — which is why this went unnoticed through several merges. |
| 15 | +# |
| 16 | +# Note on the check: author_association is NOT usable here either. Merging a PR promotes its |
| 17 | +# author from FIRST_TIME_CONTRIBUTOR to CONTRIBUTOR before the event is delivered, so a |
| 18 | +# condition on it never matches. Counting the author's merged PRs survives that. |
14 | 19 | on: |
15 | | - pull_request_target: |
16 | | - types: [closed] |
| 20 | + push: |
| 21 | + branches: [main] |
| 22 | + workflow_dispatch: |
| 23 | + inputs: |
| 24 | + pr: |
| 25 | + description: "PR number to thank for (testing; skips the push-derived lookup)" |
| 26 | + required: true |
17 | 27 |
|
18 | 28 | permissions: |
19 | | - pull-requests: read |
20 | | - issues: write |
| 29 | + contents: read |
| 30 | + # write, and it must be pull-requests rather than issues. The comment goes through |
| 31 | + # POST /repos/:owner/:repo/issues/:number/comments, so `issues: write` is the obvious |
| 32 | + # reading and it is wrong: GitHub scopes that endpoint by what the number points at, and |
| 33 | + # for a pull request the permission checked is pull-requests. Declaring `pull-requests: |
| 34 | + # read` here was the denial — an explicit permissions block is absolute, so it capped the |
| 35 | + # very thing being asked for while `issues: write` was granted and never consulted. |
| 36 | + pull-requests: write |
21 | 37 |
|
22 | 38 | jobs: |
23 | 39 | thanks: |
24 | | - if: github.event.pull_request.merged == true && github.event.pull_request.user.type != 'Bot' |
25 | 40 | runs-on: ubuntu-latest |
26 | 41 | steps: |
27 | 42 | - env: |
28 | 43 | GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} |
29 | 44 | REPO: ${{ github.repository }} |
30 | | - PR: ${{ github.event.pull_request.number }} |
31 | | - AUTHOR: ${{ github.event.pull_request.user.login }} |
| 45 | + SHA: ${{ github.sha }} |
| 46 | + MANUAL_PR: ${{ inputs.pr }} |
32 | 47 | run: | |
33 | 48 | set -euo pipefail |
34 | 49 |
|
| 50 | + if [ -n "${MANUAL_PR:-}" ]; then |
| 51 | + pr="$MANUAL_PR" |
| 52 | + else |
| 53 | + # Whatever PR this commit came from, however it was merged. Reading the API |
| 54 | + # rather than parsing the commit subject: squash writes "(#12)", a merge commit |
| 55 | + # writes "Merge pull request #12", and a rebase merge writes neither. |
| 56 | + pr=$(gh api "repos/$REPO/commits/$SHA/pulls" --jq '.[0].number // empty') |
| 57 | + fi |
| 58 | + if [ -z "$pr" ]; then |
| 59 | + echo "No pull request behind this commit — direct push." |
| 60 | + exit 0 |
| 61 | + fi |
| 62 | +
|
| 63 | + author=$(gh api "repos/$REPO/pulls/$pr" --jq '.user.login') |
| 64 | + type=$(gh api "repos/$REPO/pulls/$pr" --jq '.user.type') |
| 65 | + if [ "$type" = "Bot" ]; then |
| 66 | + echo "$author is a bot." |
| 67 | + exit 0 |
| 68 | + fi |
| 69 | +
|
35 | 70 | merged=$(gh api --paginate "repos/$REPO/pulls?state=closed&per_page=100" \ |
36 | | - --jq ".[] | select(.user.login==\"$AUTHOR\" and .merged_at != null) | .number" | wc -l) |
37 | | - echo "$AUTHOR has $merged merged PR(s) here." |
| 71 | + --jq ".[] | select(.user.login==\"$author\" and .merged_at != null) | .number" | wc -l) |
| 72 | + echo "$author has $merged merged PR(s) here." |
38 | 73 | if [ "$merged" -ne 1 ]; then |
39 | 74 | echo "Not their first — nothing to say." |
40 | 75 | exit 0 |
41 | 76 | fi |
42 | 77 |
|
43 | 78 | body="$RUNNER_TEMP/thanks.md" |
44 | 79 | cat > "$body" <<EOF |
45 | | - Merged — thanks @$AUTHOR, that's your first one here. |
| 80 | + Merged — thanks @$author, that's your first one here. |
46 | 81 |
|
47 | 82 | If the project turned out to be useful to you, a ⭐ genuinely helps: stacktale is |
48 | 83 | new, and stars are most of what decides whether anyone else finds it. |
|
51 | 86 | list is kept honest, and each one names the files to touch and how to verify. |
52 | 87 | EOF |
53 | 88 |
|
54 | | - # An org policy can force workflow tokens to read-only, which makes this POST 403. |
55 | | - # Don't fail the run over a thank-you note — a red X on every merged PR is worse |
56 | | - # than a missing comment. Switch it on at Settings > Actions > Workflow permissions. |
57 | | - if ! gh api --method POST "repos/$REPO/issues/$PR/comments" -F body=@"$body"; then |
58 | | - echo "::warning::could not comment — the workflow token is read-only for this org" |
| 89 | + # Still tolerated rather than fatal: a red X on main over a thank-you note is worse |
| 90 | + # than a missing note. The ::warning:: is what makes it visible instead of silent. |
| 91 | + if ! gh api --method POST "repos/$REPO/issues/$pr/comments" -F body=@"$body"; then |
| 92 | + echo "::warning::could not comment — check the permissions block above and" |
| 93 | + echo "::warning::Settings > Actions > Workflow permissions" |
59 | 94 | fi |
0 commit comments