feat(release): add GitHub App token, git-log release notes, and Buildkite automation

The sync-release-changelog workflow now uses a GitHub App token instead
of GITHUB_TOKEN so pushes to the release PR branch re-trigger CI (the
repo's ruleset requires 5 status checks to pass). Also hardens PR
discovery with --limit 100 --base main, and serializes concurrent runs
with cancel-in-progress: false to prevent non-fast-forward push races.

release.yml generates GitHub release notes from git log at release time
instead of reading from CHANGELOG.md, closing the merge-before-sync
race where someone merges the release PR before the changelog sync
finishes.

auto-tag-on-release-pr-merge.yml now triggers the internal
sprout-releases Buildkite pipeline via REST API after pushing the tag,
passing version metadata so the pipeline's input step is skipped.
This commit is contained in:
Will Pfleger
2026-06-03 15:29:09 -04:00
parent 15aadbd3c2
commit 3c2edfeb8a
4 changed files with 61 additions and 17 deletions

View File

@@ -60,3 +60,25 @@ jobs:
gh workflow run release.yml \
-f version="$VERSION" \
-f ref="v$VERSION"
- name: Trigger internal release build
env:
BUILDKITE_API_TOKEN: ${{ secrets.BUILDKITE_API_TOKEN }}
VERSION: ${{ env.version }}
run: |
curl -sf -X POST \
"https://api.buildkite.com/v2/organizations/runway/pipelines/sprout-releases/builds" \
-H "Authorization: Bearer ${BUILDKITE_API_TOKEN}" \
-H "Content-Type: application/json" \
-d "{
\"commit\": \"HEAD\",
\"branch\": \"main\",
\"message\": \"Release v${VERSION}\",
\"meta_data\": {
\"version\": \"${VERSION}\",
\"sprout_ref\": \"v${VERSION}\",
\"relay_url\": \"wss://sprout-oss.stage.blox.sqprod.co\",
\"publish_latest\": \"true\"
}
}"

View File

@@ -50,6 +50,8 @@ jobs:
- uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6
with:
ref: ${{ github.event_name == 'push' && github.ref || inputs.ref }}
fetch-depth: 0
fetch-tags: true
persist-credentials: false
- uses: cashapp/activate-hermit@e49f5cb4dd64ff0b0b659d1d8df499595451155a # v1
@@ -229,12 +231,11 @@ jobs:
DMG_PATH: ${{ steps.artifacts.outputs.dmg }}
run: |
RELEASE_SHA=$(git rev-parse HEAD)
NOTES=""
if [[ -f CHANGELOG.md ]]; then
NOTES=$(awk "/^## v${VERSION}$/,/^## v/" CHANGELOG.md | sed '$d')
fi
if [[ -z "$NOTES" ]]; then
NOTES="Sprout Desktop v${VERSION}"
PREV_TAG=$(git describe --tags --abbrev=0 --match 'v[0-9]*' HEAD^ 2>/dev/null || echo "")
if [[ -n "$PREV_TAG" ]]; then
NOTES=$(git log "${PREV_TAG}..HEAD" --oneline --no-merges)
else
NOTES="Initial release"
fi
gh release create "v${VERSION}" \
--target "$RELEASE_SHA" \

View File

@@ -4,10 +4,10 @@ on:
push:
branches: [main]
# Collapse rapid successive main pushes into one sync run.
# Serialize successive main pushes — each run generates the correct changelog.
concurrency:
group: sync-release-changelog
cancel-in-progress: true
cancel-in-progress: false
permissions:
contents: write
@@ -29,6 +29,8 @@ jobs:
PRS=$(gh pr list \
--repo block/sprout \
--state open \
--base main \
--limit 100 \
--json number,headRefName,headRepository \
--jq '[.[] |
select(.headRepository.nameWithOwner == "block/sprout") |
@@ -59,6 +61,14 @@ jobs:
echo "version=$VERSION" >> "$GITHUB_OUTPUT"
echo "Found release PR #${NUMBER} (${BRANCH})"
- name: Generate GitHub App token
if: steps.find-pr.outputs.found == 'true'
id: app-token
uses: actions/create-github-app-token@v3
with:
client-id: ${{ vars.RELEASE_APP_CLIENT_ID }}
private-key: ${{ secrets.RELEASE_APP_PRIVATE_KEY }}
- name: Check out release PR branch
if: steps.find-pr.outputs.found == 'true'
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6
@@ -66,13 +76,14 @@ jobs:
ref: ${{ steps.find-pr.outputs.branch }}
fetch-depth: 0
fetch-tags: true
token: ${{ steps.app-token.outputs.token }}
- name: Regenerate changelog section
if: steps.find-pr.outputs.found == 'true'
env:
VERSION: ${{ steps.find-pr.outputs.version }}
PR_NUMBER: ${{ steps.find-pr.outputs.number }}
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
GH_TOKEN: ${{ steps.app-token.outputs.token }}
run: |
set -euo pipefail