headroom-release-watch #46
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
| # Watch upstream headroom (headroomlabs-ai/headroom) for a NEW release and, when one appears that is | |
| # newer than what we're built against, bump our `headroom-core` pin to that release TAG, prove it | |
| # builds + tests green (lexical / no-ml), then bump our own patch version and push the commit to | |
| # `dev` and open (or update) a PR to `main`. Tagging + republishing (docker.yml/release.yml) happens | |
| # separately, in headroom-release-publish.yml, only once that PR is actually merged — see that | |
| # file's header for why. | |
| # | |
| # Deliberately does NOT touch `main` directly: it isn't a repo admin, so pushing straight to a | |
| # protected main via GITHUB_TOKEN doesn't actually work (required-review branch protection blocks | |
| # it, same as it would for any non-admin). `dev` has no such protection, which is the point of the | |
| # split — see RELEASING.md. | |
| # | |
| # Why polling (not a webhook): we don't control headroomlabs-ai/headroom, so their side can't dispatch | |
| # to us. A daily poll is the realistic catch. Manual `workflow_dispatch` is provided for an immediate run. | |
| # | |
| # FLOOR = the last headroom release that PREDATES the `ml`-feature gate (#2165). Only releases strictly | |
| # newer than the floor carry the gate, so we never bump onto a pre-gate release (which would drag the | |
| # whole ONNX stack back in). Until the first post-gate release ships we stay pinned to the merge commit. | |
| name: headroom-release-watch | |
| on: | |
| schedule: | |
| - cron: "17 6 * * *" # 06:17 UTC daily (off the :00 pileup) | |
| workflow_dispatch: {} | |
| permissions: | |
| contents: write # commit the bump + push to dev | |
| pull-requests: write # open/update the dev -> main PR | |
| env: | |
| UPSTREAM: headroomlabs-ai/headroom | |
| FLOOR: v0.31.0 | |
| # Same BUSBAR_REF convention ci.yml/release.yml use for the sibling path dependency this crate | |
| # needs (busbar-plugin-sdk) — see the sibling-checkout step below for why it's required at all. | |
| BUSBAR_REF: dev | |
| jobs: | |
| watch: | |
| runs-on: ubuntu-latest | |
| steps: | |
| # headroom-hook's Cargo.toml declares busbar-plugin-sdk as `path = "../busbarAI/crates/..."` | |
| # — a real sibling-relative path (see the `a61e7f8` fix), which means whatever checks this | |
| # repo out MUST also check out a real `busbarAI` as an actual filesystem sibling. This job | |
| # was checking out ONLY headroom-hook at the workspace root with no sibling at all — `../busbarAI` | |
| # resolved to nothing, so `cargo build`/`cargo test` below failed immediately on every real | |
| # (bump=yes) run. Fix: check both repos out into explicit sibling paths under the workspace, | |
| # matching release.yml's own layout, and run every later step with | |
| # `working-directory: headroom-hook` instead of the (implicit) workspace root. | |
| - name: Checkout headroom-hook | |
| uses: actions/checkout@v4 | |
| with: | |
| ref: dev | |
| path: headroom-hook | |
| fetch-depth: 0 | |
| - name: Checkout busbar (sibling path dependency) | |
| uses: actions/checkout@v4 | |
| with: | |
| repository: GetBusbar/busbar | |
| ref: ${{ env.BUSBAR_REF }} | |
| path: busbarAI | |
| - uses: dtolnay/rust-toolchain@stable | |
| - name: Resolve latest upstream release + current pin | |
| id: resolve | |
| working-directory: headroom-hook | |
| env: | |
| GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} | |
| run: | | |
| set -euo pipefail | |
| latest="$(gh api "repos/${UPSTREAM}/releases/latest" --jq .tag_name)" | |
| current="$(grep -m1 'headroom-core' Cargo.toml | grep -oE 'tag = "[^"]+"' | cut -d'"' -f2 || true)" | |
| echo "latest=$latest" >> "$GITHUB_OUTPUT" | |
| echo "current=$current" >> "$GITHUB_OUTPUT" | |
| # A bump is warranted iff latest is strictly greater than BOTH the floor and our current tag. | |
| newest_vs_floor="$(printf '%s\n%s\n' "$latest" "$FLOOR" | sort -V | tail -1)" | |
| bump=no | |
| if [ "$latest" != "$FLOOR" ] && [ "$newest_vs_floor" = "$latest" ] && [ "$latest" != "$current" ]; then | |
| bump=yes | |
| fi | |
| echo "bump=$bump" >> "$GITHUB_OUTPUT" | |
| echo "::notice::upstream latest=$latest, current pin=${current:-<commit>}, floor=$FLOOR -> bump=$bump" | |
| - name: Bump pin, build + test (lexical/no-ml), push to dev, open/update PR to main | |
| if: steps.resolve.outputs.bump == 'yes' | |
| working-directory: headroom-hook | |
| env: | |
| GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} | |
| LATEST: ${{ steps.resolve.outputs.latest }} | |
| run: | | |
| set -euo pipefail | |
| # Re-pin headroom-core to the new release TAG (drops any rev pin), keeping default-features = false. | |
| # NOTE: use `~` as the s/// delimiter, NOT `{}` — the pattern/replacement contain literal braces | |
| # and `[^}]`, which perl mis-balances against `{}` delimiters (dies with "Substitution replacement | |
| # not terminated"). `~` never appears in a Cargo git-dep line, so it is a safe delimiter. | |
| perl -0pi -e 's~headroom-core = \{ git = "https://github.com/headroomlabs-ai/headroom"[^}]*\}~headroom-core = { git = "https://github.com/headroomlabs-ai/headroom", tag = "'"$LATEST"'", default-features = false }~' Cargo.toml | |
| grep headroom-core Cargo.toml | |
| cargo update -p headroom-core | |
| # Prove the lexical build + tests are green against the new headroom BEFORE pushing anything. | |
| cargo build --release --locked | |
| cargo test --locked | |
| # Bump our own patch version (a dependency bump is a new hook release). | |
| old="$(grep -m1 '^version = ' Cargo.toml | cut -d'"' -f2)" | |
| new="$(echo "$old" | awk -F. '{printf "%s.%s.%s", $1, $2, $3+1}')" | |
| perl -0pi -e 's/^version = "'"$old"'"/version = "'"$new"'"/m' Cargo.toml | |
| cargo update -p headroom-hook --precise "$new" 2>/dev/null || cargo generate-lockfile | |
| git config user.name "headroom-release-watch" | |
| git config user.email "actions@users.noreply.github.com" | |
| git add Cargo.toml Cargo.lock | |
| git commit -m "chore(deps): headroom-core -> ${LATEST}; headroom-hook v${new} | |
| Auto-bump by headroom-release-watch: upstream headroom released ${LATEST}, which now carries | |
| the ml-feature gate. Built + tested green (lexical/no-ml)." | |
| git push origin HEAD:dev | |
| echo "new_version=${new}" >> "$GITHUB_ENV" | |
| # Open a dev -> main PR if one isn't already open (a second bump before the first is | |
| # reviewed just adds a commit to the same PR, since it's pushing to the same dev branch). | |
| existing="$(gh pr list --repo "${GITHUB_REPOSITORY}" --base main --head dev --state open --json number --jq '.[0].number // empty')" | |
| if [ -z "$existing" ]; then | |
| gh pr create --repo "${GITHUB_REPOSITORY}" --base main --head dev \ | |
| --reviewer MattJackson \ | |
| --title "Release: headroom-hook v${new} (headroom-core ${LATEST})" \ | |
| --body "Auto-opened by headroom-release-watch. Bumps headroom-core to ${LATEST} and headroom-hook to v${new}. Built + tested green (lexical/no-ml) before this PR was opened. Merging this triggers headroom-release-publish.yml, which tags the merge commit and republishes docker.yml + release.yml." | |
| else | |
| echo "::notice::PR #$existing already open dev -> main, pushed additional commit onto it" | |
| fi | |
| echo "::notice::pushed headroom-hook v${new} (headroom-core ${LATEST}) to dev; awaiting PR review/merge to publish" |