From e74f26af65333b85f921d41834f3c775072d7681 Mon Sep 17 00:00:00 2001 From: Chai Landau Date: Tue, 16 Jun 2026 14:47:00 -0400 Subject: [PATCH 1/3] ci: trigger block/homebrew-tap formula bump on release Mirror block/sessh: after publishing the release tarball, mint a scoped GitHub App token for block/homebrew-tap and dispatch its bump-formula.yaml so each anarchitecture-ghost@X.Y.Z release auto-opens a formula bump PR. Gated on BLOCK_HOMEBREW_TAP_APP_ID so forks/unconfigured repos skip it. --- .github/workflows/release-tarball.yml | 54 +++++++++++++++++++++++++++ 1 file changed, 54 insertions(+) diff --git a/.github/workflows/release-tarball.yml b/.github/workflows/release-tarball.yml index d90ce2f..6d887c6 100644 --- a/.github/workflows/release-tarball.yml +++ b/.github/workflows/release-tarball.yml @@ -19,6 +19,7 @@ on: permissions: contents: write + id-token: write concurrency: group: tarball-release-${{ github.ref }} @@ -29,6 +30,10 @@ jobs: name: Pack and release runs-on: ubuntu-latest timeout-minutes: 10 + env: + # `secrets` context can't be used in step-level `if:`, so surface a + # presence flag here. Skips the tap bump on forks / unconfigured repos. + HAS_TAP_APP: ${{ secrets.BLOCK_HOMEBREW_TAP_APP_ID != '' }} steps: - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 @@ -80,3 +85,52 @@ jobs: --title "$TAG" \ --generate-notes \ dist-tarball/*.tgz + + # Collect the artifact URL + sha256 so the Homebrew tap can pin the + # exact release tarball. The packed filename is dynamic + # (anarchitecture-ghost-X.Y.Z.tgz), so resolve it from the staging dir. + - name: Collect release metadata + id: release + env: + TAG: ${{ steps.tag.outputs.tag }} + run: | + set -euo pipefail + archive="$(ls dist-tarball/*.tgz | head -n 1)" + filename="$(basename "$archive")" + sha256="$(shasum -a 256 "$archive" | awk '{print $1}')" + artifact_url="https://github.com/$GITHUB_REPOSITORY/releases/download/$TAG/$filename" + { + echo "sha256=$sha256" + echo "artifact_url=$artifact_url" + } >> "$GITHUB_OUTPUT" + + # Mirror block/sessh: mint a scoped token for the OSS Homebrew tap and + # trigger its bump-formula workflow. block -> block, no Square refs. + # Requires the BLOCK_HOMEBREW_TAP_* secrets (GitHub App installed on + # this repo). The step is skipped if the secret is absent so forks and + # unconfigured environments don't fail the release. + - name: Generate token for tap release + id: generate_token + if: ${{ env.HAS_TAP_APP == 'true' }} + uses: actions/create-github-app-token@a8d616148505b5069dccd32f177bb87d7f39123b # v2.1.1 + with: + app-id: ${{ secrets.BLOCK_HOMEBREW_TAP_APP_ID }} + private-key: ${{ secrets.BLOCK_HOMEBREW_TAP_PRIVATE_KEY }} + owner: block + repositories: homebrew-tap + + - name: Trigger Homebrew formula bump + if: ${{ env.HAS_TAP_APP == 'true' }} + env: + GH_TOKEN: ${{ steps.generate_token.outputs.token }} + TAG: ${{ steps.tag.outputs.tag }} + ARTIFACT_URL: ${{ steps.release.outputs.artifact_url }} + SHA256: ${{ steps.release.outputs.sha256 }} + run: | + gh workflow run bump-formula.yaml \ + --repo block/homebrew-tap \ + -f repo=block/ghost \ + -f formula=ghost \ + -f "tag=$TAG" \ + -f "artifact_url=$ARTIFACT_URL" \ + -f "sha256=$SHA256" From 89ed4aa97ccd2ed3d215eac39381c939f68599e4 Mon Sep 17 00:00:00 2001 From: Chai Landau Date: Tue, 16 Jun 2026 18:03:38 -0400 Subject: [PATCH 2/3] ci: hook Homebrew tap autobump into release.yml publish path Move the tap-bump from the now dispatch-only release-tarball.yml onto release.yml's automatic publish path, so brew install block/tap/ghost tracks every npm release. Gated on an actual Changesets publish and the BLOCK_HOMEBREW_TAP_* secrets. --- .github/workflows/release-tarball.yml | 58 --------------------------- .github/workflows/release.yml | 50 +++++++++++++++++++++++ 2 files changed, 50 insertions(+), 58 deletions(-) diff --git a/.github/workflows/release-tarball.yml b/.github/workflows/release-tarball.yml index 6d887c6..30db4b1 100644 --- a/.github/workflows/release-tarball.yml +++ b/.github/workflows/release-tarball.yml @@ -19,7 +19,6 @@ on: permissions: contents: write - id-token: write concurrency: group: tarball-release-${{ github.ref }} @@ -30,10 +29,6 @@ jobs: name: Pack and release runs-on: ubuntu-latest timeout-minutes: 10 - env: - # `secrets` context can't be used in step-level `if:`, so surface a - # presence flag here. Skips the tap bump on forks / unconfigured repos. - HAS_TAP_APP: ${{ secrets.BLOCK_HOMEBREW_TAP_APP_ID != '' }} steps: - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 @@ -60,10 +55,6 @@ jobs: pnpm --filter @anarchitecture/ghost pack --pack-destination "$GITHUB_WORKSPACE/dist-tarball" ls -la dist-tarball - # Resolve the release tag. Inputs from workflow_dispatch are attacker- - # controlled (anyone with Actions write can trigger). Pass them in via - # `env:` and reference as shell variables so they can't be interpolated - # as shell syntax — that's what the semgrep shell-injection rule wants. # Inputs from workflow_dispatch are attacker-controlled (anyone with # Actions write can trigger). Pass them in via `env:` and reference as # shell variables so they can't be interpolated as shell syntax — that's @@ -85,52 +76,3 @@ jobs: --title "$TAG" \ --generate-notes \ dist-tarball/*.tgz - - # Collect the artifact URL + sha256 so the Homebrew tap can pin the - # exact release tarball. The packed filename is dynamic - # (anarchitecture-ghost-X.Y.Z.tgz), so resolve it from the staging dir. - - name: Collect release metadata - id: release - env: - TAG: ${{ steps.tag.outputs.tag }} - run: | - set -euo pipefail - archive="$(ls dist-tarball/*.tgz | head -n 1)" - filename="$(basename "$archive")" - sha256="$(shasum -a 256 "$archive" | awk '{print $1}')" - artifact_url="https://github.com/$GITHUB_REPOSITORY/releases/download/$TAG/$filename" - { - echo "sha256=$sha256" - echo "artifact_url=$artifact_url" - } >> "$GITHUB_OUTPUT" - - # Mirror block/sessh: mint a scoped token for the OSS Homebrew tap and - # trigger its bump-formula workflow. block -> block, no Square refs. - # Requires the BLOCK_HOMEBREW_TAP_* secrets (GitHub App installed on - # this repo). The step is skipped if the secret is absent so forks and - # unconfigured environments don't fail the release. - - name: Generate token for tap release - id: generate_token - if: ${{ env.HAS_TAP_APP == 'true' }} - uses: actions/create-github-app-token@a8d616148505b5069dccd32f177bb87d7f39123b # v2.1.1 - with: - app-id: ${{ secrets.BLOCK_HOMEBREW_TAP_APP_ID }} - private-key: ${{ secrets.BLOCK_HOMEBREW_TAP_PRIVATE_KEY }} - owner: block - repositories: homebrew-tap - - - name: Trigger Homebrew formula bump - if: ${{ env.HAS_TAP_APP == 'true' }} - env: - GH_TOKEN: ${{ steps.generate_token.outputs.token }} - TAG: ${{ steps.tag.outputs.tag }} - ARTIFACT_URL: ${{ steps.release.outputs.artifact_url }} - SHA256: ${{ steps.release.outputs.sha256 }} - run: | - gh workflow run bump-formula.yaml \ - --repo block/homebrew-tap \ - -f repo=block/ghost \ - -f formula=ghost \ - -f "tag=$TAG" \ - -f "artifact_url=$ARTIFACT_URL" \ - -f "sha256=$SHA256" diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index 81d87d4..993ff06 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -19,6 +19,10 @@ jobs: name: Version or publish runs-on: ubuntu-latest timeout-minutes: 15 + env: + # `secrets` context can't be used in step-level `if:`, so surface a + # presence flag here. Skips the tap bump on forks / unconfigured repos. + HAS_TAP_APP: ${{ secrets.BLOCK_HOMEBREW_TAP_APP_ID != '' }} steps: - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 with: @@ -55,11 +59,13 @@ jobs: # tarball distribution channel stays in sync with npm — no separate tag # push required. - name: Attach tarball to GitHub Release + id: tarball if: steps.changesets.outputs.published == 'true' env: GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} PUBLISHED_PACKAGES: ${{ steps.changesets.outputs.publishedPackages }} run: | + set -euo pipefail VERSION="$(echo "$PUBLISHED_PACKAGES" \ | python3 -c "import json,sys; pkgs=json.load(sys.stdin); print(next(p['version'] for p in pkgs if p['name']=='@anarchitecture/ghost'))")" TAG="anarchitecture-ghost@${VERSION}" @@ -73,3 +79,47 @@ jobs: gh release create "$TAG" --title "$TAG" --generate-notes fi gh release upload "$TAG" dist-tarball/*.tgz --clobber + # Export the tag, sha256, and artifact URL so the Homebrew tap can + # pin the exact tarball. The packed filename is dynamic + # (anarchitecture-ghost-X.Y.Z.tgz), so resolve it from the staging dir. + archive="$(ls dist-tarball/*.tgz | head -n 1)" + filename="$(basename "$archive")" + sha256="$(shasum -a 256 "$archive" | awk '{print $1}')" + artifact_url="https://github.com/$GITHUB_REPOSITORY/releases/download/$TAG/$filename" + { + echo "tag=$TAG" + echo "sha256=$sha256" + echo "artifact_url=$artifact_url" + } >> "$GITHUB_OUTPUT" + + # Mirror block/sessh: mint a scoped token for the OSS Homebrew tap and + # trigger its bump-formula workflow so `brew install block/tap/ghost` + # tracks releases automatically. block -> block, no Square refs. Gated on + # an actual publish and on the BLOCK_HOMEBREW_TAP_* secrets (GitHub App + # installed on this repo), so forks and unconfigured environments skip it + # cleanly instead of failing the release. + - name: Generate token for tap release + id: generate_token + if: ${{ steps.changesets.outputs.published == 'true' && env.HAS_TAP_APP == 'true' }} + uses: actions/create-github-app-token@a8d616148505b5069dccd32f177bb87d7f39123b # v2.1.1 + with: + app-id: ${{ secrets.BLOCK_HOMEBREW_TAP_APP_ID }} + private-key: ${{ secrets.BLOCK_HOMEBREW_TAP_PRIVATE_KEY }} + owner: block + repositories: homebrew-tap + + - name: Trigger Homebrew formula bump + if: ${{ steps.changesets.outputs.published == 'true' && env.HAS_TAP_APP == 'true' }} + env: + GH_TOKEN: ${{ steps.generate_token.outputs.token }} + TAG: ${{ steps.tarball.outputs.tag }} + ARTIFACT_URL: ${{ steps.tarball.outputs.artifact_url }} + SHA256: ${{ steps.tarball.outputs.sha256 }} + run: | + gh workflow run bump-formula.yaml \ + --repo block/homebrew-tap \ + -f repo=block/ghost \ + -f formula=ghost \ + -f "tag=$TAG" \ + -f "artifact_url=$ARTIFACT_URL" \ + -f "sha256=$SHA256" From 95e045e8b331ea69a013b6d5384cbb3656818d68 Mon Sep 17 00:00:00 2001 From: Chai Landau Date: Wed, 17 Jun 2026 09:59:21 -0400 Subject: [PATCH 3/3] chore: add changeset for Homebrew tap autobump --- .changeset/realign-release-channels.md | 5 +++++ 1 file changed, 5 insertions(+) create mode 100644 .changeset/realign-release-channels.md diff --git a/.changeset/realign-release-channels.md b/.changeset/realign-release-channels.md new file mode 100644 index 0000000..b2c25d8 --- /dev/null +++ b/.changeset/realign-release-channels.md @@ -0,0 +1,5 @@ +--- +"@anarchitecture/ghost": patch +--- + +Autobump the Homebrew tap on npm publish so `brew install block/tap/ghost` tracks releases automatically.