release-on-upstream #4
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| # Re-cut this plugin's own next signed release when busbar core ships a NEWER version. | |
| # | |
| # On an upstream-release dispatch (or daily self-heal cron, or a manual run) this compares the | |
| # incoming busbar version against the version recorded in .busbar-ref (field 2) and cuts ONLY when | |
| # the incoming version is strictly newer (sort -V). A same-version core re-tag is a clean no-op: | |
| # no commit, no tag. On a real cut it overwrites .busbar-ref with "<new-sha> <new-version>" — a | |
| # PLAIN data file the RELEASE_DISPATCH_TOKEN can push, unlike release.yml which is a workflow file | |
| # the PAT is refused (GH013) — patch-bumps THIS repo's own v* tag, and pushes it, firing release.yml | |
| # (build + sign + publish). No plain `push:` trigger, so merging this file cannot itself cut a | |
| # release. Pushes use RELEASE_DISPATCH_TOKEN (bypass-capable) as busbar-bot. | |
| name: release-on-upstream | |
| on: | |
| repository_dispatch: | |
| types: [upstream-release] | |
| schedule: | |
| - cron: "7 7 * * *" # daily; minute staggered per repo so the fleet's crons don't all fire at once | |
| workflow_dispatch: | |
| inputs: | |
| version: | |
| description: "Explicit version to cut (e.g. 1.0.2). Blank = patch-bump this repo's latest v* tag." | |
| required: false | |
| type: string | |
| permissions: | |
| contents: write | |
| concurrency: | |
| # Serialize overlapping dispatch + cron so we never double-cut; never cancel a run mid-tag-push. | |
| group: release-on-upstream-${{ github.repository }} | |
| cancel-in-progress: false | |
| jobs: | |
| cut: | |
| runs-on: ubuntu-latest | |
| steps: | |
| - name: Checkout main (bypass-capable token, full history + tags) | |
| uses: actions/checkout@v4 | |
| with: | |
| ref: main | |
| fetch-depth: 0 | |
| persist-credentials: true | |
| token: ${{ secrets.RELEASE_DISPATCH_TOKEN }} | |
| - name: Configure git identity | |
| run: | | |
| git config user.name "busbar-bot" | |
| git config user.email "bot@getbusbar.com" | |
| - name: Resolve whether a newer busbar version warrants a cut | |
| id: resolve | |
| env: | |
| GH_TOKEN: ${{ secrets.RELEASE_DISPATCH_TOKEN }} | |
| DISPATCH_SHA: ${{ github.event.client_payload.sha }} | |
| DISPATCH_VERSION: ${{ github.event.client_payload.version }} | |
| EVENT_NAME: ${{ github.event_name }} | |
| run: | | |
| set -euo pipefail | |
| rec_sha="$(cut -d' ' -f1 .busbar-ref)" | |
| rec_ver="$(cut -d' ' -f2 .busbar-ref)" | |
| echo "recorded busbar ref: ${rec_sha:-<none>} ${rec_ver:-<none>}" | |
| in_sha="" | |
| in_ver="" | |
| force=no | |
| case "$EVENT_NAME" in | |
| repository_dispatch) | |
| in_sha="${DISPATCH_SHA:-}" | |
| in_ver="${DISPATCH_VERSION:-}" | |
| ;; | |
| workflow_dispatch) | |
| force=yes # a human explicitly asked for a re-cut | |
| ;; | |
| schedule) | |
| # Self-heal: discover busbar's latest RELEASE and the commit it points at. Best-effort — | |
| # if we cannot read it, do nothing (never guess, so the cron never spuriously cuts). | |
| tag="$(gh api repos/GetBusbar/busbar/releases/latest --jq .tag_name 2>/dev/null || true)" | |
| if [ -n "$tag" ]; then | |
| in_ver="${tag#v}" | |
| in_sha="$(gh api "repos/GetBusbar/busbar/commits/${tag}" --jq .sha 2>/dev/null || true)" | |
| fi | |
| ;; | |
| esac | |
| in_ver="${in_ver#v}" | |
| should_cut=no | |
| if [ "$force" = yes ]; then | |
| should_cut=yes | |
| echo "::notice::manual workflow_dispatch -> cutting a release" | |
| elif [ -z "$in_ver" ]; then | |
| echo "::notice::no incoming busbar version (recorded=${rec_ver:-none}) -> nothing to do" | |
| elif [ "$in_ver" = "$rec_ver" ]; then | |
| echo "::notice::incoming busbar ${in_ver} == recorded ${rec_ver} (same-version re-tag) -> nothing to do" | |
| elif [ "$(printf '%s\n%s\n' "$rec_ver" "$in_ver" | sort -V | tail -1)" = "$in_ver" ]; then | |
| should_cut=yes | |
| echo "::notice::busbar advanced (recorded=${rec_ver:-none} -> ${in_ver}) -> cutting a release" | |
| else | |
| echo "::notice::incoming busbar ${in_ver} not newer than recorded ${rec_ver} -> nothing to do" | |
| fi | |
| { | |
| echo "should_cut=$should_cut" | |
| echo "in_sha=$in_sha" | |
| echo "in_ver=$in_ver" | |
| } >> "$GITHUB_OUTPUT" | |
| - name: Compute this repo's next version | |
| id: ver | |
| if: steps.resolve.outputs.should_cut == 'yes' | |
| env: | |
| INPUT_VERSION: ${{ github.event.inputs.version }} | |
| run: | | |
| set -euo pipefail | |
| if [ -n "${INPUT_VERSION:-}" ]; then | |
| next="${INPUT_VERSION#v}" | |
| echo "::notice::using explicit workflow_dispatch version -> ${next}" | |
| else | |
| latest="$(git tag --list 'v*' | sort -V | tail -1)" | |
| if [ -z "$latest" ]; then | |
| echo "::error::no existing v* tag to patch-bump from" | |
| exit 1 | |
| fi | |
| base="${latest#v}" | |
| major="$(echo "$base" | cut -d. -f1)" | |
| minor="$(echo "$base" | cut -d. -f2)" | |
| patch="$(echo "$base" | cut -d. -f3)" | |
| next="${major}.${minor}.$((patch + 1))" | |
| echo "::notice::latest tag ${latest} -> next v${next}" | |
| fi | |
| echo "tag=v${next}" >> "$GITHUB_OUTPUT" | |
| - name: Guard — no-op if the tag already exists | |
| id: guard | |
| if: steps.resolve.outputs.should_cut == 'yes' | |
| env: | |
| TAG: ${{ steps.ver.outputs.tag }} | |
| run: | | |
| set -euo pipefail | |
| if git rev-parse -q --verify "refs/tags/${TAG}" >/dev/null; then | |
| echo "::notice::${TAG} already exists — idempotent no-op, exiting 0" | |
| echo "exists=yes" >> "$GITHUB_OUTPUT" | |
| else | |
| echo "exists=no" >> "$GITHUB_OUTPUT" | |
| fi | |
| - name: Record the new busbar ref in .busbar-ref | |
| if: steps.guard.outputs.exists == 'no' && steps.resolve.outputs.in_sha != '' | |
| env: | |
| TAG: ${{ steps.ver.outputs.tag }} | |
| IN_SHA: ${{ steps.resolve.outputs.in_sha }} | |
| IN_VER: ${{ steps.resolve.outputs.in_ver }} | |
| run: | | |
| set -euo pipefail | |
| printf '%s %s\n' "${IN_SHA}" "${IN_VER}" > .busbar-ref | |
| if git diff --quiet .busbar-ref; then | |
| echo "::notice::.busbar-ref already at ${IN_SHA} ${IN_VER}, no commit needed" | |
| else | |
| git add .busbar-ref | |
| git commit -m ".busbar-ref: record busbar ${IN_VER} (${IN_SHA}) for the ${TAG} release" | |
| git push origin HEAD:main | |
| fi | |
| - name: Create and push the release tag | |
| if: steps.guard.outputs.exists == 'no' | |
| env: | |
| TAG: ${{ steps.ver.outputs.tag }} | |
| run: | | |
| set -euo pipefail | |
| git tag "${TAG}" | |
| git push origin "refs/tags/${TAG}" | |
| echo "::notice::pushed ${TAG} — release.yml will now build + sign + publish" |