@@ -11,14 +11,17 @@ review once all required checks pass.
1111## How it works
1212
13131 . You open a draft PR and add a trigger label (default: ` Mark Ready When Ready ` )
14- 2 . The action checks preconditions — if the PR isn't a draft or doesn't have the
14+ 2 . The action validates that the token has all required permissions upfront — if
15+ anything is missing, it fails immediately with a clear error showing the exact
16+ ` permissions: ` block to add
17+ 3 . It checks preconditions — if the PR isn't a draft or doesn't have the
1518 label, it exits immediately with ` result=skipped `
16- 3 . If the repo has no required checks configured, the action skips gracefully
17- 4 . Otherwise, it watches for all required checks to complete successfully
18- 5 . It pauses briefly, then watches again to catch any late-arriving checks
19- 6 . It verifies results via the GitHub GraphQL API (paginated, handles large check suites)
20- 7 . It confirms the PR has no merge conflicts
21- 8 . If everything looks good, it marks the PR as ready for review and removes the label
19+ 4 . If the repo has no required checks configured, the action skips gracefully
20+ 5 . Otherwise, it watches for all required checks to complete successfully
21+ 6 . It pauses briefly, then watches again to catch any late-arriving checks
22+ 7 . It verifies results via the GitHub GraphQL API (paginated, handles large check suites)
23+ 8 . It confirms the PR has no merge conflicts
24+ 9 . If everything looks good, it marks the PR as ready for review and removes the label
2225
2326This is especially useful for repos with long CI suites — open your PR as a
2427draft, slap on the label, and walk away. The PR will be marked ready for review
3336 pull_request :
3437 types : [opened, edited, labeled, unlabeled, synchronize]
3538
36- permissions :
37- checks : read
38- contents : write
39- pull-requests : write
40- statuses : read
41-
4239concurrency :
4340 group : ${{ github.workflow }}-${{ github.event.pull_request.number }}
4441 cancel-in-progress : true
4542
4643jobs :
4744 mark-ready :
4845 runs-on : ubuntu-latest
46+ permissions :
47+ checks : read
48+ contents : write
49+ pull-requests : write
50+ statuses : read
4951 steps :
5052 - uses : kenyonj/mark-ready-when-ready@v1
5153 with :
5254 github-token : ${{ secrets.GITHUB_TOKEN }}
5355` ` `
5456
55- That's it. The action checks for the trigger label and draft status
56- internally — if the PR isn't a draft or doesn't have the label, the action
57- exits immediately with ` result=skipped`.
57+ That's it. The action validates permissions upfront, checks for the trigger
58+ label and draft status internally — if anything isn't right, you'll get a
59+ clear error message or the action exits with ` result=skipped`.
5860
59- > **Important:** The workflow must include `contents: write` — without it,
60- > `GITHUB_TOKEN` cannot call the `markPullRequestReadyForReview` GraphQL
61- > mutation and will fail with `Resource not accessible by integration`.
61+ > **Important:** The workflow must include `contents: write` at the **job
62+ > level** — without it, `GITHUB_TOKEN` cannot call the
63+ > `markPullRequestReadyForReview` GraphQL mutation and will fail with
64+ > `Resource not accessible by integration`. As of v1.1.2, the action validates
65+ > permissions upfront and will tell you exactly what's missing.
6266
6367# ## Saving runner costs with a job-level guard
6468
7579jobs:
7680 mark-ready:
7781 runs-on: ubuntu-latest
82+ permissions:
83+ checks: read
84+ contents: write
85+ pull-requests: write
86+ statuses: read
7887 if: |
7988 contains(github.event.pull_request.labels.*.name, 'Mark Ready When Ready') &&
8089 github.event.pull_request.draft == true
@@ -143,21 +152,29 @@ permissions:
143152> **Note:** `contents: write` is required even though the action doesn't modify
144153> repository contents. Without it, `GITHUB_TOKEN` cannot call the
145154> `markPullRequestReadyForReview` GraphQL mutation.
155+ >
156+ > As of v1.1.2, the action validates these permissions at the start of every run.
157+ > If any are missing, it fails immediately with a clear error message showing the
158+ > exact `permissions:` block to add to your workflow. Permissions should be set at
159+ > the **job level**, not the top-level workflow `permissions:` key.
146160
147161# # Verification strategy
148162
149163The action uses a "trust but verify" approach :
150164
151- 1. **Precondition check** — exits early if the PR isn't a draft or the trigger
165+ 1. **Permission validation** — verifies the token has all required permissions
166+ (`contents:write`, `pull-requests:write`, `checks:read`, `statuses:read`)
167+ and fails fast with an actionable error message if any are missing
168+ 2. **Precondition check** — exits early if the PR isn't a draft or the trigger
152169 label isn't present
153- 2 . **`gh pr checks --watch`** watches for required checks to complete (skips
170+ 3 . **`gh pr checks --watch`** watches for required checks to complete (skips
154171 gracefully if no required checks are configured)
155- 3 . A configurable **pause** catches checks that are re-triggered or start late
156- 4 . **`gh pr checks --watch`** runs again to confirm everything is still green
157- 5 . A **GraphQL query** independently verifies that no required check suites have
172+ 4 . A configurable **pause** catches checks that are re-triggered or start late
173+ 5 . **`gh pr checks --watch`** runs again to confirm everything is still green
174+ 6 . A **GraphQL query** independently verifies that no required check suites have
158175 failing conclusions (`ACTION_REQUIRED`, `TIMED_OUT`, `CANCELLED`, `FAILURE`,
159176 ` STARTUP_FAILURE` ) and no required commit statuses are in a `FAILURE` state
160- 6 . The **mergeable state** is checked to ensure the PR doesn't have conflicts
177+ 7 . The **mergeable state** is checked to ensure the PR doesn't have conflicts
161178
162179This multi-layered approach prevents marking a PR as ready when there are
163180transient or late-arriving failures.
0 commit comments