release-on-upstream #13
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 Crossplane provider's own next xpkg when the busbar stack ships. | |
| # | |
| # Upstream for THIS repo is GetBusbar/terraform-provider-busbar: the Makefile pins | |
| # TERRAFORM_PROVIDER_VERSION, and the build embeds that provider release's linux binary + generates | |
| # CRDs from its schema. That pin IS this repo's record of what it was last built against. | |
| # | |
| # The busbar-core upstream-release dispatch is only a WAKE signal here - this repo does not consume | |
| # busbar core directly. On any trigger this resolves terraform-provider-busbar's LATEST release and | |
| # only proceeds if it is newer than the current pin. Runaway-safe: an idle day, or an | |
| # already-ingested provider release, is a no-op, never a spurious re-cut. | |
| # | |
| # FLEET FAN-OUT ORDERING: busbar core dispatches to the whole fleet at once, so on a dispatch the | |
| # terraform-provider release usually does not exist yet. A dispatch-triggered run therefore POLLS for | |
| # the sibling release for a bounded time and proceeds as soon as it appears; if it never appears the | |
| # run ends on a loud warning rather than a quiet green "nothing to do". | |
| # | |
| # On a real advance this re-pins TERRAFORM_PROVIDER_VERSION, `make generate`s the CRDs/examples, | |
| # bumps THIS repo's own v* tag, commits, and pushes the tag — which fires publish-provider-package.yml | |
| # (builds the controller image + .xpkg and pushes to ghcr.io/getbusbar/provider-busbar via the | |
| # built-in GITHUB_TOKEN; that GHCR publish already works, no extra secret needed). The e2e workflow | |
| # (UPTEST_* secrets) is separate and intentionally NOT gating the release. | |
| # | |
| # No plain `push:` trigger, so merging this file cannot itself cut a release. Pushes use | |
| # RELEASE_DISPATCH_TOKEN (org secret, bypass-capable) as busbar-bot; plain GITHUB_TOKEN is rejected | |
| # by branch protection on main. | |
| name: release-on-upstream | |
| on: | |
| repository_dispatch: | |
| types: [upstream-release] | |
| schedule: | |
| - cron: "37 6 * * *" # daily; minute staggered per repo so the fleet's crons don't all fire at once | |
| workflow_dispatch: | |
| inputs: | |
| version: | |
| description: "Explicit OWN version to cut (e.g. 0.1.0). Blank = patch-bump this repo's latest v* tag, or seed the first tag." | |
| required: false | |
| type: string | |
| permissions: | |
| contents: write | |
| concurrency: | |
| group: release-on-upstream-${{ github.repository }} | |
| cancel-in-progress: false | |
| env: | |
| # This repo has zero tags today; the first real release seeds here. Independent of the busbar and | |
| # terraform-provider version numbers — this is provider-busbar's OWN Crossplane package version, | |
| # starting its own v0.1.0 line (mixed-model: wrappers keep independent semver, they do NOT mirror | |
| # busbar's version). | |
| SEED_VERSION: v0.1.0 | |
| # How long a repository_dispatch wake waits for terraform-provider-busbar's own release to show up | |
| # before giving up (see the ordering-race note in the resolve step). | |
| UPSTREAM_WAIT_SECONDS: "1200" | |
| UPSTREAM_POLL_SECONDS: "30" | |
| jobs: | |
| cut: | |
| runs-on: ubuntu-latest | |
| steps: | |
| - name: Checkout main (bypass-capable token, full history + tags, build submodule) | |
| uses: actions/checkout@v4 | |
| with: | |
| ref: main | |
| fetch-depth: 0 | |
| submodules: recursive | |
| persist-credentials: true | |
| token: ${{ secrets.RELEASE_DISPATCH_TOKEN }} | |
| - uses: actions/setup-go@v5 | |
| with: | |
| go-version: "1.26" | |
| cache: true | |
| - name: Configure git identity | |
| run: | | |
| git config user.name "busbar-bot" | |
| git config user.email "bot@getbusbar.com" | |
| - name: Resolve whether a newer terraform-provider-busbar warrants a re-cut | |
| id: resolve | |
| env: | |
| GH_TOKEN: ${{ secrets.RELEASE_DISPATCH_TOKEN }} | |
| EVENT_NAME: ${{ github.event_name }} | |
| run: | | |
| set -euo pipefail | |
| cur_pin="$(awk -F'=' '/^export TERRAFORM_PROVIDER_VERSION[[:space:]]*\?=/{gsub(/[[:space:]]/,"",$2); print $2; exit}' Makefile)" | |
| echo "current TERRAFORM_PROVIDER_VERSION pin: ${cur_pin:-<none>}" | |
| # Always resolve the upstream terraform-provider's LATEST release (the dispatch payload is | |
| # just a wake signal; our real dependency is the TF provider, not busbar core). | |
| latest_ver() { | |
| local t | |
| t="$(gh api repos/GetBusbar/terraform-provider-busbar/releases/latest --jq .tag_name 2>/dev/null || true)" | |
| printf '%s' "${t#v}" | |
| } | |
| is_newer() { # is_newer <candidate> <current> -> 0 when candidate > current | |
| [ -n "$1" ] || return 1 | |
| [ "$1" != "$2" ] || return 1 | |
| [ "$(printf '%s\n%s\n' "$2" "$1" | sort -V | tail -1)" = "$1" ] | |
| } | |
| target_ver="$(latest_ver)" | |
| echo "terraform-provider-busbar latest release: ${target_ver:-none}" | |
| pin_stale=no | |
| is_newer "$target_ver" "$cur_pin" && pin_stale=yes | |
| # ORDERING RACE. GetBusbar/busbar's notify-downstream job wakes the whole fleet at once, but | |
| # terraform-provider-busbar cuts its own release from that same fan-out, so on a dispatch | |
| # the sibling release we depend on is routinely published minutes AFTER we are woken (for | |
| # busbar 1.5.3 it was about five and a half minutes later). Resolving "latest" a single time | |
| # and reporting a green "nothing to do" hides that. Wait for it, bounded, instead. | |
| if [ "$EVENT_NAME" = repository_dispatch ] && [ "$pin_stale" = no ]; then | |
| echo "::notice::dispatch-triggered and no newer terraform-provider yet -> waiting up to ${UPSTREAM_WAIT_SECONDS}s for the sibling release" | |
| waited=0 | |
| while [ "$waited" -lt "$UPSTREAM_WAIT_SECONDS" ]; do | |
| sleep "$UPSTREAM_POLL_SECONDS" | |
| waited=$((waited + UPSTREAM_POLL_SECONDS)) | |
| probe="$(latest_ver)" | |
| echo " +${waited}s: latest=${probe:-none}" | |
| if is_newer "$probe" "$cur_pin"; then | |
| target_ver="$probe" | |
| pin_stale=yes | |
| echo "::notice::terraform-provider ${probe} appeared after ${waited}s of waiting -> proceeding" | |
| break | |
| fi | |
| done | |
| fi | |
| force=no | |
| [ "$EVENT_NAME" = workflow_dispatch ] && force=yes | |
| should_cut=no | |
| if [ "$force" = yes ]; then | |
| should_cut=yes | |
| echo "::notice::manual workflow_dispatch -> cutting a release (pin_stale=${pin_stale})" | |
| elif [ "$pin_stale" = yes ]; then | |
| should_cut=yes | |
| echo "::notice::terraform-provider advanced (pinned=${cur_pin} -> ${target_ver}) -> cutting a release" | |
| elif [ "$EVENT_NAME" = repository_dispatch ]; then | |
| # An upstream-release dispatch that still ends in "nothing to do" after waiting means the | |
| # fan-out fired too early or the sibling release failed. Do not report that as a quiet | |
| # green; the daily cron still self-heals, but this has to be visible in the run list. | |
| echo "::warning title=upstream-release dispatch produced no release::woken by an upstream-release dispatch but terraform-provider-busbar is still at ${target_ver:-none} (pin ${cur_pin:-none}) after waiting ${UPSTREAM_WAIT_SECONDS}s. Either the sibling release failed or the fan-out fired too early. This repo is NOT refreshed; the daily cron will retry." | |
| else | |
| echo "::notice::no newer terraform-provider (pinned=${cur_pin:-none}, latest=${target_ver:-none}) -> nothing to do" | |
| fi | |
| { | |
| echo "should_cut=$should_cut" | |
| echo "pin_stale=$pin_stale" | |
| echo "target_ver=$target_ver" | |
| } >> "$GITHUB_OUTPUT" | |
| - name: Re-pin TERRAFORM_PROVIDER_VERSION + regenerate | |
| if: steps.resolve.outputs.should_cut == 'yes' && steps.resolve.outputs.pin_stale == 'yes' | |
| env: | |
| TARGET_VER: ${{ steps.resolve.outputs.target_ver }} | |
| run: | | |
| set -euo pipefail | |
| sed -i -E "s|^(export TERRAFORM_PROVIDER_VERSION[[:space:]]*\?=[[:space:]]*).*|\1${TARGET_VER}|" Makefile | |
| grep -n 'TERRAFORM_PROVIDER_VERSION ?=' Makefile | |
| make generate | |
| - 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="v${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 | |
| next="$SEED_VERSION" | |
| echo "::notice::no existing v* tag -> seeding first release ${next}" | |
| else | |
| base="${latest#v}" | |
| major="$(echo "$base" | cut -d. -f1)" | |
| minor="$(echo "$base" | cut -d. -f2)" | |
| patch="$(echo "$base" | cut -d. -f3)" | |
| next="v${major}.${minor}.$((patch + 1))" | |
| echo "::notice::latest tag ${latest} -> next ${next}" | |
| fi | |
| fi | |
| echo "tag=${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: Commit the re-pin + regenerated code to main | |
| if: steps.guard.outputs.exists == 'no' && steps.resolve.outputs.pin_stale == 'yes' | |
| env: | |
| TAG: ${{ steps.ver.outputs.tag }} | |
| TARGET_VER: ${{ steps.resolve.outputs.target_ver }} | |
| run: | | |
| set -euo pipefail | |
| git add Makefile apis config examples examples-generated package | |
| if git diff --cached --quiet; then | |
| echo "::notice::pin + generated code already current, no re-pin commit needed" | |
| else | |
| git commit -m "provider: re-pin terraform-provider-busbar to v${TARGET_VER} 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} — publish-provider-package.yml will now build the .xpkg and push it to ghcr.io/getbusbar/provider-busbar" |