Skip to content

Commit c36c433

Browse files
authored
ci(release): use RELEASE_TOKEN PAT to fix required-check gate (#5)
Signed-off-by: Git'Fellow <12234510+solracsf@users.noreply.github.com>
1 parent 2bbe443 commit c36c433

1 file changed

Lines changed: 93 additions & 37 deletions

File tree

.github/workflows/release-prepare.yml

Lines changed: 93 additions & 37 deletions
Original file line numberDiff line numberDiff line change
@@ -12,27 +12,44 @@
1212
# 3. Creates a `release/vX.Y.Z` branch
1313
# 4. Updates info.xml + CHANGELOG.md (promotes [Unreleased] block
1414
# content into the new versioned entry; resets [Unreleased])
15-
# 5. Commits + pushes the release branch (release/* is not a
16-
# protected ref; main is)
15+
# 5. Commits + pushes the release branch using the RELEASE_TOKEN PAT
16+
# (release/* is not a protected ref; main is)
1717
# 6. Opens a PR titled `chore(release): vX.Y.Z` with label `release`
18-
# 7. Manually fires CI + integration workflows on the release branch
19-
# so the PR's required-checks gate gets satisfied
2018
#
21-
# Why "manually fires" CI
22-
# -----------------------
23-
# Pushes and PRs created with the built-in GITHUB_TOKEN do NOT trigger
24-
# downstream workflow runs (anti-loop protection in GitHub Actions).
25-
# `workflow_dispatch` is the documented exception
26-
# (https://docs.github.com/en/actions/security-for-github-actions/security-guides/automatic-token-authentication#using-the-github_token-in-a-workflow),
27-
# so we add `workflow_dispatch:` to ci.yml + integration.yml and call
28-
# them with `gh workflow run --ref release/vX.Y.Z`. This avoids the
29-
# need for a long-lived PAT secret.
19+
# Why a PAT (RELEASE_TOKEN) is required
20+
# -------------------------------------
21+
# Pushes and PRs created with the built-in GITHUB_TOKEN are exempt
22+
# from triggering downstream workflow runs (anti-loop protection).
23+
# This means the resulting PR's required-checks rollup never gets
24+
# the CI/Integration check_runs, so branch protection blocks merge
25+
# even though every job actually passes when invoked manually.
26+
#
27+
# A user PAT (or fine-grained token) bypasses the anti-loop limit:
28+
# its `git push` and `gh pr create` calls fire the normal `push` /
29+
# `pull_request: opened` events, which trigger ci.yml + integration.yml
30+
# in the standard `pull_request` check_suite — exactly the suite that
31+
# branch protection consults.
32+
#
33+
# Setup (one-time)
34+
# ----------------
35+
# 1. Create a fine-grained PAT at
36+
# https://github.com/settings/personal-access-tokens/new
37+
# scoped to this repository, with these permissions:
38+
# Contents : Read and write
39+
# Pull requests : Read and write
40+
# Workflows : Read and write
41+
# Metadata : Read-only (auto-included)
42+
# 2. Add it as repo secret `RELEASE_TOKEN`
43+
# (Settings → Secrets and variables → Actions → New repository secret)
44+
# 3. Done — every Release — Prepare PR run will use it.
3045
#
3146
# Phase 2
3247
# -------
3348
# When the prep PR merges, release-publish.yml fires automatically,
3449
# tags the merge commit, builds the App Store tarball, creates the
35-
# GitHub Release, and uploads to the Nextcloud App Store.
50+
# GitHub Release, and uploads to the Nextcloud App Store. Phase 2
51+
# uses GITHUB_TOKEN; it does not need the PAT because it pushes a
52+
# tag (which is not covered by branch rulesets).
3653

3754
name: Release — Prepare PR
3855

@@ -58,21 +75,65 @@ on:
5875
- "true"
5976

6077
permissions:
61-
contents: write # push to release/* branch
62-
pull-requests: write # gh pr create
63-
actions: write # gh workflow run
78+
contents: write # push to release/* branch (only matters as a token-scope hint;
79+
# actual push uses RELEASE_TOKEN)
80+
pull-requests: write # gh pr create (likewise)
6481

6582
jobs:
6683
prepare:
6784
name: Open release PR
6885
runs-on: ubuntu-latest
6986
steps:
87+
# -----------------------------------------------------------------------
88+
# 0. Guard — fail loudly if RELEASE_TOKEN is missing
89+
#
90+
# Failing in the first step is much friendlier than letting the workflow
91+
# bump the version, push the branch, and only THEN fail at PR creation
92+
# leaving a stale `release/vX.Y.Z` branch on origin.
93+
# -----------------------------------------------------------------------
94+
- name: Verify RELEASE_TOKEN secret is configured
95+
env:
96+
RELEASE_TOKEN: ${{ secrets.RELEASE_TOKEN }}
97+
run: |
98+
if [ -z "$RELEASE_TOKEN" ]; then
99+
cat <<'MSG' >&2
100+
101+
::error::RELEASE_TOKEN secret is not set.
102+
103+
Phase 1 of the release flow needs a personal access token (PAT) to
104+
push and open the prep PR — pushes/PRs from the built-in GITHUB_TOKEN
105+
do not trigger CI in the pull_request check_suite, which leaves the
106+
PR's required-checks gate permanently unsatisfied.
107+
108+
One-time setup:
109+
110+
1. Create a fine-grained PAT scoped to this repository at
111+
https://github.com/settings/personal-access-tokens/new
112+
with permissions:
113+
Contents : Read and write
114+
Pull requests : Read and write
115+
Workflows : Read and write
116+
2. Add it as repo secret RELEASE_TOKEN
117+
(Settings → Secrets and variables → Actions)
118+
119+
See the header comment of .github/workflows/release-prepare.yml
120+
for the full rationale.
121+
122+
MSG
123+
exit 1
124+
fi
125+
echo "RELEASE_TOKEN is configured."
126+
70127
- name: Checkout main with full history
71128
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
72129
with:
73130
ref: main
74131
fetch-depth: 0
75-
token: ${{ secrets.GITHUB_TOKEN }}
132+
# PAT, not GITHUB_TOKEN — see step 0's failure message for why.
133+
# actions/checkout configures git's credential helper from this
134+
# token, so subsequent `git push` calls in this job authenticate
135+
# as the PAT owner and trigger CI events normally.
136+
token: ${{ secrets.RELEASE_TOKEN }}
76137

77138
# -----------------------------------------------------------------------
78139
# 1. Read current version from appinfo/info.xml
@@ -206,6 +267,12 @@ jobs:
206267
207268
# -----------------------------------------------------------------------
208269
# 5. Commit on a new release/vX.Y.Z branch and push
270+
#
271+
# The push goes out as the RELEASE_TOKEN PAT owner (configured by
272+
# actions/checkout above), so GitHub fires `push` events normally
273+
# — and the matching `pull_request: opened` event in step 6 is
274+
# also unrestricted, which means CI fires automatically without
275+
# any explicit workflow_dispatch trick.
209276
# -----------------------------------------------------------------------
210277
- name: Create release branch and commit
211278
env:
@@ -230,6 +297,8 @@ jobs:
230297
# -----------------------------------------------------------------------
231298
- name: Ensure 'release' label exists
232299
env:
300+
# Using GITHUB_TOKEN is fine for label CRUD — labels are not
301+
# subject to the anti-loop rule, and gh CLI just needs auth.
233302
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
234303
run: |
235304
# Idempotent — `--force` updates the label if it already exists.
@@ -242,7 +311,12 @@ jobs:
242311
- name: Open release PR
243312
id: pr
244313
env:
245-
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
314+
# PAT, NOT GITHUB_TOKEN — `gh pr create` authenticated as the
315+
# PAT owner fires the `pull_request: opened` event normally,
316+
# which triggers CI + Integration in the pull_request
317+
# check_suite. With GITHUB_TOKEN the event is suppressed and
318+
# the PR's required-checks gate stays unsatisfied.
319+
GH_TOKEN: ${{ secrets.RELEASE_TOKEN }}
246320
OLD: ${{ steps.current.outputs.current }}
247321
NEW: ${{ steps.new.outputs.version }}
248322
TAG: ${{ steps.new.outputs.tag }}
@@ -282,24 +356,6 @@ jobs:
282356
echo "Opened: $PR_URL"
283357
echo "url=$PR_URL" >> "$GITHUB_OUTPUT"
284358
285-
# -----------------------------------------------------------------------
286-
# 7. Trigger CI on the release branch via workflow_dispatch
287-
#
288-
# Pushes/PRs created with GITHUB_TOKEN do not trigger downstream
289-
# workflows — workflow_dispatch IS the exception. ci.yml and
290-
# integration.yml each declare a workflow_dispatch trigger; calling
291-
# them here gives the PR's required-checks gate something to gate on
292-
# without a PAT.
293-
# -----------------------------------------------------------------------
294-
- name: Trigger CI on the release branch
295-
env:
296-
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
297-
BRANCH: ${{ steps.new.outputs.branch }}
298-
run: |
299-
gh workflow run ci.yml --ref "$BRANCH"
300-
gh workflow run integration.yml --ref "$BRANCH"
301-
echo "Triggered ci.yml + integration.yml on $BRANCH"
302-
303359
- name: Print next steps
304360
env:
305361
PR_URL: ${{ steps.pr.outputs.url }}

0 commit comments

Comments
 (0)