Skip to content

release-on-upstream #15

release-on-upstream

release-on-upstream #15

# 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"