name: Homebrew Cask Bump # Why: keep the stablyai/homebrew-orca tap in sync with GitHub Releases. # Triggers: # - workflow_call → release-cut.yml finished publishing a stable tag # - workflow_dispatch → manual backfill for a specific tag # # The job computes the sha256 of orca-macos-arm64.dmg + orca-macos-x64.dmg # from the release, templates the channel-specific cask in this repo with the # new version and hashes, then PRs the result into stablyai/homebrew-orca. # Stable tags update Casks/orca.rb; RC tags update Casks/orca@rc.rb. # # Why no `release.published` trigger: other products shipped from this repo # (e.g. mobile under `mobile-v*`) also publish GitHub Releases. A blanket # `release.published` trigger would fire this workflow for any of them and # attempt to download `orca-macos-*.dmg` from a non-desktop release, at # best 404ing and at worst (if the filter were weaker) rewriting the cask # to a non-desktop version. The tap is only ever bumped by the desktop # release pipeline calling this workflow directly. on: workflow_call: inputs: tag: description: Release tag to sync (e.g. v1.3.25) required: true type: string workflow_dispatch: inputs: tag: description: Release tag to sync (e.g. v1.3.25) required: true type: string jobs: bump-cask: runs-on: ubuntu-latest # Why: only desktop tags have the macOS DMGs this workflow downloads. # A mobile tag such as `mobile-v0.0.1` must never rewrite either cask. if: > inputs.tag != '' && startsWith(inputs.tag, 'v') permissions: contents: read steps: - name: Checkout orca uses: actions/checkout@v6 - name: Resolve tag and version id: tag env: RELEASE_TAG: ${{ github.event.release.tag_name }} INPUT_TAG: ${{ inputs.tag }} run: | set -euo pipefail if [[ -n "$INPUT_TAG" ]]; then tag="$INPUT_TAG" else tag="$RELEASE_TAG" fi version="${tag#v}" echo "tag=$tag" >>"$GITHUB_OUTPUT" echo "version=$version" >>"$GITHUB_OUTPUT" - name: Resolve cask target id: cask env: TAG: ${{ steps.tag.outputs.tag }} VERSION: ${{ steps.tag.outputs.version }} run: | set -euo pipefail if [[ "$TAG" =~ ^v[0-9]+\.[0-9]+\.[0-9]+-rc\.[0-9]+$ ]]; then token="orca@rc" branch_token="orca-rc" elif [[ "$TAG" =~ ^v[0-9]+\.[0-9]+\.[0-9]+$ ]]; then token="orca" branch_token="orca" else echo "::error::Unsupported desktop tag shape: $TAG" >&2 exit 1 fi path="Casks/${token}.rb" if [[ ! -f "$path" ]]; then echo "::error::Missing cask template: $path" >&2 exit 1 fi echo "token=$token" >>"$GITHUB_OUTPUT" echo "path=$path" >>"$GITHUB_OUTPUT" echo "branch=bump/${branch_token}-${VERSION}" >>"$GITHUB_OUTPUT" echo "title=${token} ${VERSION}" >>"$GITHUB_OUTPUT" - name: Download DMGs and compute sha256 id: hash env: GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} TAG: ${{ steps.tag.outputs.tag }} run: | set -euo pipefail mkdir -p dmgs gh release download "$TAG" \ --repo "${{ github.repository }}" \ --pattern 'orca-macos-arm64.dmg' \ --pattern 'orca-macos-x64.dmg' \ --dir dmgs arm=$(sha256sum dmgs/orca-macos-arm64.dmg | awk '{print $1}') intel=$(sha256sum dmgs/orca-macos-x64.dmg | awk '{print $1}') echo "arm=$arm" >>"$GITHUB_OUTPUT" echo "intel=$intel" >>"$GITHUB_OUTPUT" - name: Render updated cask file env: VERSION: ${{ steps.tag.outputs.version }} ARM_SHA: ${{ steps.hash.outputs.arm }} INTEL_SHA: ${{ steps.hash.outputs.intel }} CASK_PATH: ${{ steps.cask.outputs.path }} run: | set -euo pipefail python3 - <<'PY' import os, re, pathlib p = pathlib.Path(os.environ["CASK_PATH"]) src = p.read_text() src = re.sub(r'version "[^"]+"', f'version "{os.environ["VERSION"]}"', src, count=1) src = re.sub(r'sha256 arm:\s*"[0-9a-f]+",\s*\n\s*intel:\s*"[0-9a-f]+"', 'sha256 arm: "{arm}",\n intel: "{intel}"'.format( arm=os.environ["ARM_SHA"], intel=os.environ["INTEL_SHA"]), src, count=1) p.write_text(src) print(src) PY # Why: reuse the existing buf0-bot GitHub App (also used by # track-community-prs.yaml) instead of minting a new PAT. The app is # installed org-wide, so it has access to stablyai/homebrew-orca # automatically. GITHUB_TOKEN cannot push cross-repo; a short-lived # installation token can. - name: Generate buf0-bot token id: app-token uses: actions/create-github-app-token@v3 with: app-id: 2590194 private-key: ${{ secrets.BUFO_BOT_PRIVATE_KEY }} owner: stablyai repositories: homebrew-orca - name: Checkout tap uses: actions/checkout@v6 with: repository: stablyai/homebrew-orca path: tap token: ${{ steps.app-token.outputs.token }} - name: Copy cask into tap and open PR env: GH_TOKEN: ${{ steps.app-token.outputs.token }} VERSION: ${{ steps.tag.outputs.version }} TAG: ${{ steps.tag.outputs.tag }} CASK_PATH: ${{ steps.cask.outputs.path }} BRANCH: ${{ steps.cask.outputs.branch }} TITLE: ${{ steps.cask.outputs.title }} run: | set -euo pipefail mkdir -p "tap/$(dirname "$CASK_PATH")" cp "$CASK_PATH" "tap/$CASK_PATH" cd tap # Why: commit author must match the buf0-bot app so commits are # attributed to the bot in the tap's history / PR UI. git config user.name "buf0-bot[bot]" git config user.email "252831055+buf0-bot[bot]@users.noreply.github.com" if git diff --quiet; then echo "Cask already up-to-date for $TAG; nothing to do." exit 0 fi branch="$BRANCH" git checkout -b "$branch" git add "$CASK_PATH" git commit -m "$TITLE" git push -u origin "$branch" --force # Why: repeated backfills can race an existing PR branch, so edit # the PR title if creation reports that one already exists. gh pr create \ --title "$TITLE" \ --body "Automated bump of $CASK_PATH from stablyai/orca release $TAG." \ --base main \ --head "$branch" \ || gh pr edit "$branch" --title "$TITLE" # Why: squash-merge immediately rather than --auto. The tap has no # required checks or branch protection, and `gh pr merge --auto` # only activates when there's something to wait on — on a repo # with no gates it silently no-ops, leaving the PR open forever. gh pr merge "$branch" --squash --delete-branch