66 workflow_dispatch : {}
77
88permissions :
9- contents : write
10- actions : write
9+ contents : read
1110
1211concurrency :
1312 group : release
1413 cancel-in-progress : false
1514
1615jobs :
16+ gate :
17+ name : Gate on CI evidence
18+ # A push to main must not publish unless Validate passed on this exact SHA and,
19+ # when it was merged from a PR, every check on that PR passed (Blender Smoke
20+ # runs on PRs only). Direct pushes have no PR; Validate alone gates them.
21+ runs-on : ubuntu-latest
22+ if : " !contains(github.event.head_commit.message, '[skip ci]')"
23+ permissions :
24+ contents : read
25+ actions : read
26+ checks : read
27+ pull-requests : read
28+ steps :
29+ - uses : actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7
30+ with :
31+ sparse-checkout : .github/scripts
32+ - name : Require green Validate and PR checks
33+ env :
34+ GH_TOKEN : ${{ github.token }}
35+ SHA : ${{ github.sha }}
36+ run : bash .github/scripts/release-gate.sh
37+
1738 version-and-release :
1839 name : Bump version, tag, and release
40+ needs : gate
1941 runs-on : ubuntu-latest
2042 if : " !contains(github.event.head_commit.message, '[skip ci]')"
43+ permissions :
44+ contents : write
45+ actions : write
2146 steps :
22- - uses : actions/checkout@v7
47+ - uses : actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7
2348 with :
2449 fetch-depth : 0
2550 token : ${{ secrets.GITHUB_TOKEN }}
@@ -117,7 +142,7 @@ jobs:
117142
118143 - name : Sync release docs
119144 if : steps.check.outputs.skip == 'false' && steps.bump.outputs.release == 'true'
120- uses : TMHSDigital/Developer-Tools-Directory/.github/actions/release-doc-sync@v1
145+ uses : TMHSDigital/Developer-Tools-Directory/.github/actions/release-doc-sync@7886cbe6ef93d57cc73c020b98268fa0dc0bdc98 # v1
121146 with :
122147 plugin-version : ${{ steps.new.outputs.version }}
123148 previous-version : ${{ steps.current.outputs.version }}
@@ -156,20 +181,43 @@ jobs:
156181 PYEOF
157182
158183 - name : Commit version bump
184+ id : commit
159185 if : steps.check.outputs.skip == 'false' && steps.bump.outputs.release == 'true'
160186 run : |
161187 git config user.name "github-actions[bot]"
162188 git config user.email "41898282+github-actions[bot]@users.noreply.github.com"
163- git add -A
189+
190+ # Another PR may have merged after this run checked out (#226). The
191+ # queued run for the newer SHA scans the whole range since the last
192+ # tag and releases everything, so stand down cleanly instead of
193+ # failing with a non-fast-forward push.
194+ git fetch -q origin main
195+ if [ "$(git rev-parse origin/main)" != "$(git rev-parse HEAD)" ]; then
196+ echo "::notice::origin/main moved past $GITHUB_SHA; the newer release run will publish this range"
197+ echo "stale=true" >> "$GITHUB_OUTPUT"
198+ exit 0
199+ fi
200+
201+ # Explicit paths only: the files this workflow owns, never `git add -A`.
202+ git add -- VERSION CHANGELOG.md CLAUDE.md ROADMAP.md \
203+ .cursor-plugin/plugin.json .claude-plugin/plugin.json .claude-plugin/marketplace.json
204+ if [ -n "$(git status --porcelain)" ]; then
205+ echo "::warning::files changed that the release commit does not own:"
206+ git status --porcelain
207+ fi
164208 if git diff --cached --quiet; then
165209 echo "No changes to commit"
166210 else
167211 git commit -s -m "chore: bump version to ${{ steps.new.outputs.version }} [skip ci]"
168- git push origin main
212+ if ! git push origin main; then
213+ echo "::notice::push rejected (main moved); the newer release run will publish this range"
214+ echo "stale=true" >> "$GITHUB_OUTPUT"
215+ exit 0
216+ fi
169217 fi
170218
171219 - name : Create and push tag
172- if : steps.check.outputs.skip == 'false' && steps.bump.outputs.release == 'true'
220+ if : steps.check.outputs.skip == 'false' && steps.bump.outputs.release == 'true' && steps.commit.outputs.stale != 'true'
173221 run : |
174222 new_version="${{ steps.new.outputs.version }}"
175223 IFS='.' read -r major minor _patch <<< "$new_version"
@@ -183,7 +231,7 @@ jobs:
183231 git push origin "v$major.$minor" --force
184232
185233 - name : Create GitHub Release
186- if : steps.check.outputs.skip == 'false' && steps.bump.outputs.release == 'true'
234+ if : steps.check.outputs.skip == 'false' && steps.bump.outputs.release == 'true' && steps.commit.outputs.stale != 'true'
187235 env :
188236 GH_TOKEN : ${{ secrets.GITHUB_TOKEN }}
189237 run : |
@@ -192,7 +240,7 @@ jobs:
192240 --generate-notes
193241
194242 - name : Dispatch Pages
195- if : steps.check.outputs.skip == 'false' && steps.bump.outputs.release == 'true'
243+ if : steps.check.outputs.skip == 'false' && steps.bump.outputs.release == 'true' && steps.commit.outputs.stale != 'true'
196244 env :
197245 GH_TOKEN : ${{ secrets.GITHUB_TOKEN }}
198246 run : gh workflow run pages.yml --ref main
0 commit comments