Repository navigation
208 lines (192 loc) · 10.9 KB
/
Copy pathspec-pin-bump.yml
File metadata and controls
208 lines (192 loc) · 10.9 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
name: SPEC_PIN bump
# Proposes moving SPEC_PIN (ci.yml) to hyperobjects-spec main, weekly and on demand.
#
# Why: the pin trailed the keystone by six minor versions (0.9.0 against 0.15.0, the
# 2026-10-18 audit) because nothing ever proposed a bump. This job compares the pin
# with keystone main and, when they differ:
# 1. rewrites the pin in ci.yml and the entry docs (.github/scripts/spec_pin_bump.py),
# 2. runs the manifest lane at the new pin (every cartridge, no CAD kernel) and the
# CI-script tests, and records the result in the PR body,
# 3. opens or updates ONE PR from the branch `ci/spec-pin-bump`,
# 4. dispatches ci.yml's full nightly sweep on that branch: every cartridge, every
# preset, both kernels, at the new pin. The per-PR render lane re-renders only the
# graph cartridges on a SPEC_PIN change (render_scope.graph_scope_on_spec_change),
# so the dispatched sweep is the "render everything" a bar change needs. ci.yml
# keeps the tracking issue for main only, so a branch sweep never opens or closes
# it; read the run itself.
#
# Never merges. A human merges the PR after reading the sweep.
#
# Runs on GitHub-hosted runners (R116) with the ADR-010 expression ci.yml uses. It runs
# no pull-request code: only schedule and workflow_dispatch trigger it.
#
# Token: step 3 needs DISPATCH_TOKEN, the estate's PAT convention (the same secret
# yantra4d's bump-commons-pin.yml uses). github.token cannot do step 3, for two
# separate reasons, and no `permissions:` entry changes either:
# - the bump rewrites .github/workflows/ci.yml (SPEC_PIN lives there), and GitHub
# refuses any push from GITHUB_TOKEN that creates or updates a workflow file
# ("refusing to allow a GitHub App to create or update workflow ... without
# `workflows` permission"). That is how the first scheduled run (2026-10-19)
# died: the branch push was rejected before a PR could exist;
# - "Allow GitHub Actions to create and approve pull requests" is off for this
# repository, so GITHUB_TOKEN cannot open the PR either.
# DISPATCH_TOKEN therefore needs, on this repository: Contents read/write, Pull
# requests read/write and Workflows read/write. When it is not provisioned the job
# still runs steps 1-2 and then fails at a guard that says so in the run summary,
# instead of dying inside the push. A PR opened with DISPATCH_TOKEN DOES trigger
# ci.yml's pull_request lanes; step 2 and step 4 stay, because the in-job manifest
# lane result lands in the PR body and the per-PR render lane re-renders only the
# graph cartridges on a pin change. Step 4 dispatches with github.token
# (workflow_dispatch is the one event GITHUB_TOKEN may start).
on:
schedule:
# Mondays 07:00Z (01:00 CDMX), two hours before the 09:00Z nightly.
- cron: "0 7 * * 1"
workflow_dispatch:
permissions: {}
concurrency:
group: spec-pin-bump
cancel-in-progress: false
jobs:
bump:
name: propose a SPEC_PIN bump
runs-on: ${{ vars.ARC_BOOTSTRAP_COMPLETE == 'true' && 'madfam-runners-blue' || 'ubuntu-24.04' }}
timeout-minutes: 20
permissions:
contents: write # push the bump branch
pull-requests: write # open/update the PR, comment the sweep link
actions: write # dispatch ci.yml on the bump branch
env:
KEYSTONE: https://github.com/madfam-org/hyperobjects-spec
BRANCH: ci/spec-pin-bump
steps:
- uses: actions/checkout@11d5960a326750d5838078e36cf38b85af677262 # v4
- name: Keystone main against the pin
id: pin
run: |
set -euo pipefail
new=$(git ls-remote "$KEYSTONE" refs/heads/main | cut -f1)
case "$new" in
*[!0-9a-f]*|"") echo "::error title=keystone main unresolved::git ls-remote returned '$new'"; exit 1 ;;
esac
[ "${#new}" -eq 40 ] || { echo "::error title=keystone main unresolved::'$new' is not a full sha"; exit 1; }
python3 .github/scripts/spec_pin_bump.py --to "$new" --github-output "$GITHUB_OUTPUT"
- name: Manifest lane and CI-script tests at the new pin
id: lane
if: steps.pin.outputs.changed == 'true'
run: |
set -uo pipefail
python3 -m venv .venv-bump
.venv-bump/bin/python -m pip install --quiet --upgrade pip
if ! .venv-bump/bin/pip install --quiet "hyperobjects-spec @ git+${KEYSTONE}@${{ steps.pin.outputs.new }}" pytest; then
echo "::error title=keystone not installable::hyperobjects-spec could not be installed at ${{ steps.pin.outputs.new }}"
echo "version=not installable" >> "$GITHUB_OUTPUT"
echo "manifests=not run (keystone not installable)" >> "$GITHUB_OUTPUT"
echo "scripts=not run" >> "$GITHUB_OUTPUT"
exit 0
fi
version=$(.venv-bump/bin/python -c "import importlib.metadata as m; print(m.version('hyperobjects-spec'))")
echo "version=$version" >> "$GITHUB_OUTPUT"
dirs=$(for d in */; do [ -f "$d/project.json" ] && printf './%s ' "$d"; done)
.venv-bump/bin/y4d-spec check $dirs > manifests.log 2>&1
rc=$?
echo "manifests=exit $rc: $(tail -n 1 manifests.log)" >> "$GITHUB_OUTPUT"
.venv-bump/bin/python -m pytest .github/scripts -q > scripts.log 2>&1
rc=$?
echo "scripts=exit $rc: $(tail -n 1 scripts.log)" >> "$GITHUB_OUTPUT"
rm -rf .venv-bump manifests.log scripts.log
# Fail here, visibly, rather than inside the push: without DISPATCH_TOKEN the push
# of ci.yml is refused (see the header). The lane result above is still worth a
# week's run, so it goes into the summary.
- name: Require DISPATCH_TOKEN (github.token cannot push ci.yml)
if: steps.pin.outputs.changed == 'true'
env:
DISPATCH_TOKEN_SET: ${{ secrets.DISPATCH_TOKEN != '' }}
OLD: ${{ steps.pin.outputs.old }}
NEW: ${{ steps.pin.outputs.new }}
VERSION: ${{ steps.lane.outputs.version }}
MANIFESTS: ${{ steps.lane.outputs.manifests }}
SCRIPTS: ${{ steps.lane.outputs.scripts }}
run: |
set -euo pipefail
[ "$DISPATCH_TOKEN_SET" = "true" ] && exit 0
{
echo "### SPEC_PIN bump could not open a PR: DISPATCH_TOKEN is not provisioned"
echo
echo "Keystone main moved: \`$OLD\` -> \`$NEW\` (hyperobjects-spec $VERSION)."
echo
echo "- manifest lane at the new pin: \`$MANIFESTS\`"
echo "- \`pytest .github/scripts\`: \`$SCRIPTS\`"
echo
echo "The bump rewrites \`.github/workflows/ci.yml\`, and GitHub refuses a push"
echo "from \`GITHUB_TOKEN\` that changes a workflow file. Provision"
echo "\`DISPATCH_TOKEN\` (org level recommended) with Contents, Pull requests and"
echo "Workflows read/write on this repository. Until then, run"
echo "\`python3 .github/scripts/spec_pin_bump.py --to $NEW\` locally and open the"
echo "PR by hand."
} >> "$GITHUB_STEP_SUMMARY"
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."
exit 1
- name: Open or update the bump PR
id: cpr
if: steps.pin.outputs.changed == 'true'
uses: peter-evans/create-pull-request@5f6978faf089d4d20b00c7766989d076bb2fc7f1 # v8.1.1
with:
token: ${{ secrets.DISPATCH_TOKEN }}
branch: ${{ env.BRANCH }}
base: main
signoff: true
add-paths: |
.github/workflows/ci.yml
README.md
AGENTS.md
CONTRIBUTING.md
llms.txt
llms-full.txt
commit-message: |
ci: bump SPEC_PIN to hyperobjects-spec ${{ steps.lane.outputs.version }} (${{ steps.pin.outputs.new }})
From ${{ steps.pin.outputs.old }}. Opened by .github/workflows/spec-pin-bump.yml.
title: "ci: bump SPEC_PIN to hyperobjects-spec ${{ steps.lane.outputs.version }}"
body: |
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.
**At the new pin, in the bump job** (`ci.yml`'s `pull_request` lanes run on this PR too):
- manifest lane: `${{ steps.lane.outputs.manifests }}`
- `pytest .github/scripts`: `${{ steps.lane.outputs.scripts }}`
**Full sweep:** the bump job dispatched `ci.yml` (nightly sweep: every cartridge, every preset, both kernels) on `${{ env.BRANCH }}`; its run is linked in a comment below. Merge only when that run is green, or when every new failure or note is explained. The per-PR render lane would re-render only the graph cartridges.
Keystone changes since the old pin: ${{ env.KEYSTONE }}/compare/${{ steps.pin.outputs.old }}...${{ steps.pin.outputs.new }}
Then bump the other consumers (soft-hyperobjects, yantra4d) to the same sha.
delete-branch: true
- name: Dispatch the full sweep on the bump branch
if: steps.cpr.outputs.pull-request-operation == 'created' || steps.cpr.outputs.pull-request-operation == 'updated'
env:
GH_TOKEN: ${{ github.token }}
PR: ${{ steps.cpr.outputs.pull-request-number }}
run: |
set -euo pipefail
before=$(date -u +%Y-%m-%dT%H:%M:%SZ)
gh workflow run ci.yml --ref "$BRANCH"
url=""
for _ in $(seq 1 12); do
sleep 10
url=$(gh run list --workflow ci.yml --branch "$BRANCH" --event workflow_dispatch \
--created ">=$before" --limit 1 --json url --jq '.[0].url // ""')
[ -n "$url" ] && break
done
if [ -z "$url" ]; then
echo "::error title=sweep not found::dispatched ci.yml on $BRANCH but found no run within 2 minutes"
gh pr comment "$PR" --body "Dispatched the full sweep (ci.yml, workflow_dispatch) on \`$BRANCH\`, but its run did not appear within 2 minutes. Look under Actions → CI for a workflow_dispatch run on this branch."
exit 1
fi
gh pr comment "$PR" --body "Full sweep at the new pin: $url"
- name: Explain a refused push or PR
if: failure() && steps.cpr.outcome == 'failure'
run: |
cat >> "$GITHUB_STEP_SUMMARY" <<'EOF2'
### SPEC_PIN bump: DISPATCH_TOKEN was refused
`DISPATCH_TOKEN` is provisioned, but pushing `ci/spec-pin-bump` or opening the PR
with it failed (the error is in the "Open or update the bump PR" step). The bump
rewrites `.github/workflows/ci.yml`, so the token needs, on this repository:
Contents read/write, Pull requests read/write and **Workflows** read/write
(a classic PAT: `repo` + `workflow`). Check its repository access and expiry.
EOF2
exit 1