Skip to content

Commit 9d818bd

Browse files
aldoruizlunaclaude
andauthored
ci(spec-pin-bump): open the bump PR with DISPATCH_TOKEN; fail visibly without it (#192)
The first scheduled run (2026-10-19, run 39266451346) died pushing ci/spec-pin-bump: the bump rewrites .github/workflows/ci.yml, and GitHub refuses any GITHUB_TOKEN push that changes a workflow file ("refusing to allow a GitHub App to create or update workflow ... without `workflows` permission"). Even past that, "Allow GitHub Actions to create and approve pull requests" is off for this repository, so GITHUB_TOKEN could not open the PR either. No `permissions:` entry fixes either one. - create-pull-request now uses secrets.DISPATCH_TOKEN, the estate's PAT convention (yantra4d's bump-commons-pin.yml uses the same secret). - A guard step before it fails with a run summary (old/new pin and the manifest-lane result) when DISPATCH_TOKEN is not provisioned, instead of dying inside the push. - An explain step names the permissions the token needs if it is refused. - The PR body no longer says the pull_request lanes do not run (a PAT-opened PR does trigger them); the sweep dispatch stays on github.token. - Header comment and README say what the token needs and why. Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
1 parent 2f2b338 commit 9d818bd

2 files changed

Lines changed: 71 additions & 7 deletions

File tree

‎.github/workflows/spec-pin-bump.yml‎

Lines changed: 67 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -21,11 +21,24 @@ name: SPEC_PIN bump
2121
# Runs on GitHub-hosted runners (R116) with the ADR-010 expression ci.yml uses. It runs
2222
# no pull-request code: only schedule and workflow_dispatch trigger it.
2323
#
24-
# Token: github.token only. A PR opened with it does NOT trigger ci.yml's
25-
# pull_request lanes (GitHub suppresses workflow runs from GITHUB_TOKEN events, except
26-
# workflow_dispatch), which is why step 2 runs the manifest lane here and step 4
27-
# dispatches the sweep. The repository setting "Allow GitHub Actions to create and
28-
# approve pull requests" must be on for step 3.
24+
# Token: step 3 needs DISPATCH_TOKEN, the estate's PAT convention (the same secret
25+
# yantra4d's bump-commons-pin.yml uses). github.token cannot do step 3, for two
26+
# separate reasons, and no `permissions:` entry changes either:
27+
# - the bump rewrites .github/workflows/ci.yml (SPEC_PIN lives there), and GitHub
28+
# refuses any push from GITHUB_TOKEN that creates or updates a workflow file
29+
# ("refusing to allow a GitHub App to create or update workflow ... without
30+
# `workflows` permission"). That is how the first scheduled run (2026-10-19)
31+
# died: the branch push was rejected before a PR could exist;
32+
# - "Allow GitHub Actions to create and approve pull requests" is off for this
33+
# repository, so GITHUB_TOKEN cannot open the PR either.
34+
# DISPATCH_TOKEN therefore needs, on this repository: Contents read/write, Pull
35+
# requests read/write and Workflows read/write. When it is not provisioned the job
36+
# still runs steps 1-2 and then fails at a guard that says so in the run summary,
37+
# instead of dying inside the push. A PR opened with DISPATCH_TOKEN DOES trigger
38+
# ci.yml's pull_request lanes; step 2 and step 4 stay, because the in-job manifest
39+
# lane result lands in the PR body and the per-PR render lane re-renders only the
40+
# graph cartridges on a pin change. Step 4 dispatches with github.token
41+
# (workflow_dispatch is the one event GITHUB_TOKEN may start).
2942
on:
3043
schedule:
3144
# Mondays 07:00Z (01:00 CDMX), two hours before the 09:00Z nightly.
@@ -89,12 +102,45 @@ jobs:
89102
echo "scripts=exit $rc: $(tail -n 1 scripts.log)" >> "$GITHUB_OUTPUT"
90103
rm -rf .venv-bump manifests.log scripts.log
91104
105+
# Fail here, visibly, rather than inside the push: without DISPATCH_TOKEN the push
106+
# of ci.yml is refused (see the header). The lane result above is still worth a
107+
# week's run, so it goes into the summary.
108+
- name: Require DISPATCH_TOKEN (github.token cannot push ci.yml)
109+
if: steps.pin.outputs.changed == 'true'
110+
env:
111+
DISPATCH_TOKEN_SET: ${{ secrets.DISPATCH_TOKEN != '' }}
112+
OLD: ${{ steps.pin.outputs.old }}
113+
NEW: ${{ steps.pin.outputs.new }}
114+
VERSION: ${{ steps.lane.outputs.version }}
115+
MANIFESTS: ${{ steps.lane.outputs.manifests }}
116+
SCRIPTS: ${{ steps.lane.outputs.scripts }}
117+
run: |
118+
set -euo pipefail
119+
[ "$DISPATCH_TOKEN_SET" = "true" ] && exit 0
120+
{
121+
echo "### SPEC_PIN bump could not open a PR: DISPATCH_TOKEN is not provisioned"
122+
echo
123+
echo "Keystone main moved: \`$OLD\` -> \`$NEW\` (hyperobjects-spec $VERSION)."
124+
echo
125+
echo "- manifest lane at the new pin: \`$MANIFESTS\`"
126+
echo "- \`pytest .github/scripts\`: \`$SCRIPTS\`"
127+
echo
128+
echo "The bump rewrites \`.github/workflows/ci.yml\`, and GitHub refuses a push"
129+
echo "from \`GITHUB_TOKEN\` that changes a workflow file. Provision"
130+
echo "\`DISPATCH_TOKEN\` (org level recommended) with Contents, Pull requests and"
131+
echo "Workflows read/write on this repository. Until then, run"
132+
echo "\`python3 .github/scripts/spec_pin_bump.py --to $NEW\` locally and open the"
133+
echo "PR by hand."
134+
} >> "$GITHUB_STEP_SUMMARY"
135+
echo "::error title=DISPATCH_TOKEN not provisioned::keystone main is $NEW but the bump PR cannot be opened: github.token may not push .github/workflows/ci.yml. See the run summary."
136+
exit 1
137+
92138
- name: Open or update the bump PR
93139
id: cpr
94140
if: steps.pin.outputs.changed == 'true'
95141
uses: peter-evans/create-pull-request@5f6978faf089d4d20b00c7766989d076bb2fc7f1 # v8.1.1
96142
with:
97-
token: ${{ github.token }}
143+
token: ${{ secrets.DISPATCH_TOKEN }}
98144
branch: ${{ env.BRANCH }}
99145
base: main
100146
signoff: true
@@ -113,7 +159,7 @@ jobs:
113159
body: |
114160
Moves `SPEC_PIN` from `${{ steps.pin.outputs.old }}` to keystone main `${{ steps.pin.outputs.new }}` (hyperobjects-spec ${{ steps.lane.outputs.version }}), in `ci.yml` and the entry docs.
115161
116-
**At the new pin, in the bump job** (this PR was opened with `GITHUB_TOKEN`, so the `pull_request` lanes of `ci.yml` do not run on it by themselves):
162+
**At the new pin, in the bump job** (`ci.yml`'s `pull_request` lanes run on this PR too):
117163
- manifest lane: `${{ steps.lane.outputs.manifests }}`
118164
- `pytest .github/scripts`: `${{ steps.lane.outputs.scripts }}`
119165
@@ -146,3 +192,17 @@ jobs:
146192
exit 1
147193
fi
148194
gh pr comment "$PR" --body "Full sweep at the new pin: $url"
195+
196+
- name: Explain a refused push or PR
197+
if: failure() && steps.cpr.outcome == 'failure'
198+
run: |
199+
cat >> "$GITHUB_STEP_SUMMARY" <<'EOF2'
200+
### SPEC_PIN bump: DISPATCH_TOKEN was refused
201+
202+
`DISPATCH_TOKEN` is provisioned, but pushing `ci/spec-pin-bump` or opening the PR
203+
with it failed (the error is in the "Open or update the bump PR" step). The bump
204+
rewrites `.github/workflows/ci.yml`, so the token needs, on this repository:
205+
Contents read/write, Pull requests read/write and **Workflows** read/write
206+
(a classic PAT: `repo` + `workflow`). Check its repository access and expiry.
207+
EOF2
208+
exit 1

‎README.md‎

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -180,6 +180,10 @@ nonblocking until whole-commons false-positive analysis supports a stronger rule
180180
lane at the new pin, opens or updates one PR from `ci/spec-pin-bump`, and
181181
dispatches the full nightly sweep on that branch, because the per-PR render
182182
lane re-renders only the graph cartridges on a pin change. It never merges.
183+
It pushes and opens the PR with the `DISPATCH_TOKEN` secret (Contents, Pull
184+
requests and Workflows read/write): `GITHUB_TOKEN` may not push a change to a
185+
workflow file, and `ci.yml` is where the pin lives. Without that secret the
186+
job stops after the manifest lane and says so in its run summary.
183187
- **The bar per render.** Watertight, positive volume, no inverted
184188
(negative-volume) body, **B-Rep validity before tessellation** (`BRepCheck`
185189
plus a per-solid signed-volume test — a `Reversed()` solid is `IsValid()` yet

0 commit comments

Comments
 (0)