|
33 | 33 | pull_request: |
34 | 34 | types: [opened, edited, reopened, synchronize] |
35 | 35 |
|
36 | | -# `contents: read` checks the repo out to get at the script. `pull-requests: |
37 | | -# read` is what the commit-list gather below needs, and naming a `permissions:` |
38 | | -# block at all sets every scope NOT listed to `none`, so both must be spelled. |
39 | | -# Read-only is the whole grant: this gate reports, and never closes a PR, |
40 | | -# comments, or edits a body. |
| 36 | +# `contents: read` checks the repo out to get at the script, and naming a |
| 37 | +# `permissions:` block at all sets every scope NOT listed to `none`, so that one |
| 38 | +# line is the whole grant. It used to also grant `pull-requests: read`, for a |
| 39 | +# step that gathered the PR's commit messages; that rule is a pre-push hook now |
| 40 | +# (the script header says why), so the scope went with it. This gate reports, |
| 41 | +# and never closes a PR, comments, or edits a body. |
41 | 42 | permissions: |
42 | 43 | contents: read |
43 | | - pull-requests: read |
44 | 44 |
|
45 | 45 | concurrency: |
46 | 46 | group: partof-closing-keyword-${{ github.event.pull_request.number }} |
|
93 | 93 | # No install step: the script imports one sibling module and reads no |
94 | 94 | # workspace package, so `node` on the pinned runtime is the whole |
95 | 95 | # toolchain it needs. |
96 | | - # RULE 2's input. The script judges it but never fetches it: the judging |
97 | | - # path stays HTTP-free, and the gather is a step of its own so that a |
98 | | - # network failure reads as a failed gather rather than as a verdict about |
99 | | - # somebody's PR. |
100 | | - # |
101 | | - # The endpoint is chosen over `git log base..head` deliberately. It |
102 | | - # returns exactly the set GitHub will squash. The git walk needs the merge |
103 | | - # base present to exclude what is already on the default branch, and the |
104 | | - # checkout above is depth 1 — so on a branch that has merged `main` back |
105 | | - # in, the walk cannot exclude those commits and would report another |
106 | | - # author's landed trailers as this PR's. Deepening until the merge base |
107 | | - # appears is unbounded, and `fetch-depth: 0` clones the whole repository |
108 | | - # to read a handful of messages. |
109 | | - # |
110 | | - # `--paginate` is load-bearing: without it a PR over one page silently |
111 | | - # loses its later commits, and a rule that read half the commits would |
112 | | - # report the unread half as clean. `--jq` emits one JSON object per line, |
113 | | - # and JSON escapes the newlines inside a commit message, so one row really |
114 | | - # is one line. The messages go to a FILE rather than into the environment: |
115 | | - # they are multi-line attacker-controlled text, and a path is inert where |
116 | | - # a body of prose is not. |
117 | | - # |
118 | | - # No pipeline here, on purpose. A `run:` block executes as `bash -e` |
119 | | - # WITHOUT pipefail, so `gh ... | jq ...` would take jq's exit code and a |
120 | | - # failed gather would reach the script as an empty file. It is a single |
121 | | - # redirect, so a failing `gh` fails the step; and if it ever did produce an |
122 | | - # empty file, the script reads zero rows as a failed gather, not as a PR |
123 | | - # with no commits. |
124 | | - - name: Gather the PR's commit messages |
125 | | - env: |
126 | | - GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} |
127 | | - PR_NUMBER: ${{ github.event.pull_request.number }} |
128 | | - REPO: ${{ github.repository }} |
129 | | - run: > |
130 | | - gh api --paginate "/repos/$REPO/pulls/$PR_NUMBER/commits" |
131 | | - --jq '.[] | {sha: .sha, message: .commit.message}' |
132 | | - > "$RUNNER_TEMP/pr-commits.jsonl" |
133 | | -
|
134 | | - - name: A PR body may not close the card it is only part of, and no commit may carry a card trailer |
| 96 | + - name: A PR body may not close the card it is only part of |
135 | 97 | env: |
136 | 98 | PR_BODY: ${{ github.event.pull_request.body }} |
137 | 99 | PR_NUMBER: ${{ github.event.pull_request.number }} |
138 | | - PR_COMMITS_FILE: ${{ runner.temp }}/pr-commits.jsonl |
139 | 100 | run: node scripts/check-partof-closing-keyword.mjs |
0 commit comments