The weekly release lands its bump through a pull request
main is protected as of today -- no force-push, no deletion, and a pull
request with a green build to merge -- and GITHUB_TOKEN is not a bypass
actor. `git push origin HEAD:main` in the cut job would have been refused
from Monday, on a scheduled run nobody watches.
GitHub would not take the obvious fix. Adding the Actions integration as a
bypass actor is rejected ("must be part of the ruleset source or owner
organization") because the organization has no app installations. The
other two routes -- an organization-level ruleset, a deploy key with write
access -- both amount to handing the release a credential that outranks
the rule, which is a worse thing to own than a slower Monday.
So the bump lands the way every other change does. It commits to
release/v<version>, opens a pull request, waits for the build the ruleset
requires, merges, and tags what came out. The waiting is not merely the
rule being satisfied: a release cut from a tree that does not compile is
the failure this whole arrangement exists to prevent, and until now
nothing checked.
Three details that would each have produced a wrong tag. The sha comes
from GitHub's merge commit, not the tip that was pushed, because a rebase
merge rewrites it. The pull request is tracked by number, not by branch,
because the branch is deleted on merge and a deleted branch no longer
resolves to its pull request. And a failed or slow build leaves the pull
request open and cuts nothing, rather than tagging whatever main happened
to hold.
Quiet weeks are unaffected: the tag still names the bump commit, so
`previous..HEAD` is still zero when nothing else has landed.
The cost is a Monday run that now takes as long as a full build -- about
25 minutes at the moment, most of it saving the cache.
This commit is contained in:
@@ -121,8 +121,9 @@ jobs:
|
|||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
permissions:
|
permissions:
|
||||||
contents: write
|
contents: write
|
||||||
|
pull-requests: write
|
||||||
outputs:
|
outputs:
|
||||||
sha: ${{ steps.bump.outputs.sha }}
|
sha: ${{ steps.land.outputs.sha }}
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
|
- uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
|
||||||
with:
|
with:
|
||||||
@@ -131,6 +132,7 @@ jobs:
|
|||||||
- id: bump
|
- id: bump
|
||||||
env:
|
env:
|
||||||
VERSION: ${{ needs.check.outputs.version }}
|
VERSION: ${{ needs.check.outputs.version }}
|
||||||
|
BRANCH: release/v${{ needs.check.outputs.version }}
|
||||||
run: |
|
run: |
|
||||||
set -euo pipefail
|
set -euo pipefail
|
||||||
|
|
||||||
@@ -153,14 +155,72 @@ jobs:
|
|||||||
git config user.email "41898282+github-actions[bot]@users.noreply.github.com"
|
git config user.email "41898282+github-actions[bot]@users.noreply.github.com"
|
||||||
git add crates/types/src/branding.rs
|
git add crates/types/src/branding.rs
|
||||||
git commit -m "Version ${VERSION}"
|
git commit -m "Version ${VERSION}"
|
||||||
git push origin HEAD:main
|
git push origin "HEAD:refs/heads/${BRANCH}"
|
||||||
|
|
||||||
echo "sha=$(git rev-parse HEAD)" >> "$GITHUB_OUTPUT"
|
# main is protected: it takes a pull request with a green build, and
|
||||||
|
# GITHUB_TOKEN is not among the bypass actors. So the bump lands the way
|
||||||
|
# every other change does. The alternative was to hand the release a
|
||||||
|
# credential that outranks the rule, which is a worse thing to own than
|
||||||
|
# a slower Monday.
|
||||||
|
- id: land
|
||||||
|
env:
|
||||||
|
VERSION: ${{ needs.check.outputs.version }}
|
||||||
|
BRANCH: release/v${{ needs.check.outputs.version }}
|
||||||
|
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||||
|
run: |
|
||||||
|
set -euo pipefail
|
||||||
|
|
||||||
|
url="$(gh pr create --base main --head "${BRANCH}" \
|
||||||
|
--title "Version ${VERSION}" \
|
||||||
|
--body "Weekly release. Bumps \`brand_version!\` to ${VERSION} so the tag names a tree that reports the version the tag claims.")"
|
||||||
|
# The number, not the branch: the branch is deleted on merge, and a
|
||||||
|
# deleted branch no longer resolves to its pull request.
|
||||||
|
pr="${url##*/}"
|
||||||
|
echo "Opened #${pr}"
|
||||||
|
|
||||||
|
# The build is what the rule actually requires, and it is also the
|
||||||
|
# thing worth waiting for: a release cut from a tree that does not
|
||||||
|
# compile is the failure this whole arrangement exists to prevent.
|
||||||
|
# A full build of this tree is long, so the deadline is generous.
|
||||||
|
deadline=$(( SECONDS + 3600 ))
|
||||||
|
while :; do
|
||||||
|
state="$(gh pr view "${pr}" --json statusCheckRollup \
|
||||||
|
--jq '[.statusCheckRollup[]? | .conclusion // "PENDING"] | join(",")')"
|
||||||
|
case "${state}" in
|
||||||
|
*FAILURE*|*CANCELLED*|*TIMED_OUT*)
|
||||||
|
echo "::error::CI failed on ${BRANCH} (${state}); no release cut. PR #${pr} is left open."
|
||||||
|
exit 1 ;;
|
||||||
|
*SUCCESS*) break ;;
|
||||||
|
esac
|
||||||
|
if [ "${SECONDS}" -ge "${deadline}" ]; then
|
||||||
|
echo "::error::timed out waiting for CI on ${BRANCH}. PR #${pr} is left open."
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
sleep 30
|
||||||
|
done
|
||||||
|
|
||||||
|
gh pr merge "${pr}" --rebase --delete-branch
|
||||||
|
|
||||||
|
# A rebase merge rewrites the commit, so the sha to tag is the one
|
||||||
|
# GitHub recorded for the merge, not the tip that was pushed. It can
|
||||||
|
# take a moment to appear.
|
||||||
|
sha=""
|
||||||
|
for _ in $(seq 1 30); do
|
||||||
|
sha="$(gh pr view "${pr}" --json mergeCommit --jq '.mergeCommit.oid // ""')"
|
||||||
|
[ -n "${sha}" ] && break
|
||||||
|
sleep 5
|
||||||
|
done
|
||||||
|
if [ -z "${sha}" ]; then
|
||||||
|
echo "::error::#${pr} merged but GitHub reported no merge commit; nothing safe to tag."
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
echo "sha=${sha}" >> "$GITHUB_OUTPUT"
|
||||||
- env:
|
- env:
|
||||||
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||||
run: |
|
run: |
|
||||||
set -euo pipefail
|
set -euo pipefail
|
||||||
args=(--target "${{ steps.bump.outputs.sha }}"
|
args=(--target "${{ steps.land.outputs.sha }}"
|
||||||
--title "INBUXA ${{ needs.check.outputs.version }}"
|
--title "INBUXA ${{ needs.check.outputs.version }}"
|
||||||
--generate-notes)
|
--generate-notes)
|
||||||
# Bound the notes to what is actually new. Without a start tag the
|
# Bound the notes to what is actually new. Without a start tag the
|
||||||
|
|||||||
Reference in New Issue
Block a user