From f41b0c8a587070a7d933c7430a1511022a0db0fc Mon Sep 17 00:00:00 2001 From: "Shea Lewis (Kai)" <355659+kaidesu@users.noreply.github.com> Date: Sun, 30 Aug 2026 16:51:35 -0700 Subject: [PATCH] Move the cask to the org-wide tap and fail loudly without the token Lumen is about to ship a cask too, and a tap per project means a user tapping twice for two projects from the same org. `brew tap ghost-language/tap` now covers both. The silent skip goes with it. Deriving skip_upload from the presence of HOMEBREW_TAP_TOKEN meant a release with no token produced a green run, published binaries, and a tap that was never touched - which is exactly what v1.0.0-beta.3 did, and it was invisible until someone ran `brew install` and found nothing there. The token is now checked in verify, before any archive is built, so the failure is loud, early, and free. Existing users of ghost-language/homebrew-ghost keep the beta.3 cask that is already there; they need to re-tap to get anything newer. Co-Authored-By: Claude Opus 5 --- .github/workflows/release.yml | 22 ++++++++++++++++++++-- .goreleaser.yml | 14 +++++--------- 2 files changed, 25 insertions(+), 11 deletions(-) diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index 80cef12..9fd4ebc 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -60,6 +60,23 @@ jobs: fi echo "$TAG matches version/version.go" + # The cask is pushed at the end of the release, once the binaries are + # already published. A token discovered missing at that point leaves a + # green release pointing at a stale tap - which is what v1.0.0-beta.3 did + # - so it is checked here, before anything is built. + - name: Check the Homebrew tap token is present + if: github.event_name == 'release' + env: + HOMEBREW_TAP_TOKEN: ${{ secrets.HOMEBREW_TAP_TOKEN }} + run: | + if [ -z "$HOMEBREW_TAP_TOKEN" ]; then + echo "::error::HOMEBREW_TAP_TOKEN is not set on this repository." + echo "The cask in ghost-language/homebrew-tap cannot be updated without it." + echo "Add a token with contents:write on ghost-language/homebrew-tap, then re-run." + exit 1 + fi + echo "HOMEBREW_TAP_TOKEN is present" + # fmt, vet, test and bench - the same gate a pull request has to pass. - name: Check run: make check @@ -98,8 +115,9 @@ jobs: ${{ github.event_name == 'workflow_dispatch' && '--snapshot --skip=publish,announce' || '' }} env: GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} - # Optional. Absent, GoReleaser skips the Homebrew formula rather than - # failing the release - see skip_upload in .goreleaser.yml. + # Required for a real release: GoReleaser pushes the cask to + # ghost-language/homebrew-tap with it. Verify has already checked it + # is present, so a failure here is a real failure. HOMEBREW_TAP_TOKEN: ${{ secrets.HOMEBREW_TAP_TOKEN }} - name: Upload the dry run's archives diff --git a/.goreleaser.yml b/.goreleaser.yml index a977686..f7fb98e 100644 --- a/.goreleaser.yml +++ b/.goreleaser.yml @@ -72,25 +72,21 @@ homebrew_casks: - name: ghost repository: owner: ghost-language - name: homebrew-ghost + # The org-wide tap, shared with Lumen and anything else that ships a + # binary, rather than a tap per project: `brew tap ghost-language/tap` + # once covers all of them. + name: homebrew-tap # The tap is a separate repository, so the workflow's automatic # GITHUB_TOKEN cannot push to it. This needs a personal access token - # with contents:write on ghost-language/homebrew-ghost, stored as the + # with contents:write on ghost-language/homebrew-tap, stored as the # HOMEBREW_TAP_TOKEN secret. token: "{{ .Env.HOMEBREW_TAP_TOKEN }}" - # Without the token the cask cannot be pushed. Skipping that one step is - # better than failing a release whose binaries are already published. - skip_upload: '{{ if .Env.HOMEBREW_TAP_TOKEN }}false{{ else }}true{{ end }}' commit_author: name: github-actions[bot] email: 41898282+github-actions[bot]@users.noreply.github.com homepage: "https://github.com/ghost-language/ghost" description: "Ghost, a dynamically typed scripting language." license: "MIT" - # No conflict is declared against the formula this cask replaces, even - # though both install the same binary: a cask can only conflict with - # another cask. Formula/ghost.rb has to be deleted from the tap by hand - # once the first cask release has landed. hooks: post: # The binaries are not signed or notarised, so macOS quarantines them