Skip to content

ci(release): auto-open Homebrew/core bump PR after npm publish#2087

Merged
dyoshikawa merged 2 commits into
mainfrom
resolve-issue-2074-homebrew-autobump
Jul 1, 2026
Merged

ci(release): auto-open Homebrew/core bump PR after npm publish#2087
dyoshikawa merged 2 commits into
mainfrom
resolve-issue-2074-homebrew-autobump

Conversation

@dyoshikawa

Copy link
Copy Markdown
Owner

Summary

Resolves the Homebrew formula lagging behind GitHub/npm releases (#2074) by automating the formula bump from the release pipeline.

Adds a homebrew job to .github/workflows/publish.yml that runs after the npm publish + GitHub release are finalized and:

  1. Derives the version from the release/v* tag (validated).
  2. Waits for the npm tarball (https://registry.npmjs.org/rulesync/-/rulesync-<version>.tgz) to propagate.
  3. Computes its sha256.
  4. Runs brew bump-formula-pr to open a Homebrew/core update PR (idempotent — refuses duplicates).

⚠️ Requires maintainer action — DO NOT auto-merge

  • Secret required: the job is gated on a maintainer-provisioned HOMEBREW_BUMP_TOKEN secret — a PAT with public_repo scope (the default GITHUB_TOKEN cannot open cross-repository PRs against Homebrew/homebrew-core). When the secret is absent the job is a no-op warning, so merging is safe but inert until the secret is added.
  • Design decision: this keeps Homebrew/core as the canonical install path (per the issue). If you would prefer an official dyoshikawa/homebrew-tap for instant availability instead, that is a separate maintainer choice.

Security

Untrusted workflow_run data (head_branch) is passed via env vars and the tag is regex-validated before use, per .claude/rules/github-actions-security.md. actionlint passes locally.

Notes

This is a GitHub Actions workflow change, so per this repo's autonomous-batch safety policy it is opened for manual maintainer review and merge, not auto-merged.

Closes #2074

Adds a homebrew job to the Publish workflow that, after the npm publish and GitHub release are finalized, derives the version from the release tag, waits for the npm tarball to propagate, computes its sha256, and runs 'brew bump-formula-pr' to open a Homebrew/core update PR. This keeps the Homebrew formula from lagging behind GitHub/npm releases.

The step is gated on a maintainer-provisioned HOMEBREW_BUMP_TOKEN secret (a PAT with public_repo scope; the default GITHUB_TOKEN cannot open cross-repo PRs) and is a no-op warning when the secret is absent. Untrusted workflow_run data is passed via env vars and the tag is validated, per the project's GitHub Actions script-injection guidance.

Closes #2074

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>

@dyoshikawa dyoshikawa left a comment

Copy link
Copy Markdown
Owner Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is a solid idea for automating the Homebrew bump, and the script-injection handling (passing HEAD_BRANCH through env: and regex-validating it) is done right. Two things below look like they'd actually break or misbehave in practice, though, so I'd want those addressed before merging. Also worth noting: since this job only runs on workflow_run after a real release merge, none of these issues would show up in this PR's own CI checks — they'd only surface (or silently misbehave) during an actual release.


# Skip if Homebrew/core already has this exact version or an open bump PR
# for it; brew bump-formula-pr is idempotent and will refuse duplicates.
brew bump-formula-pr rulesync \

Copy link
Copy Markdown
Owner Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

GitHub-hosted ubuntu-latest runners have Homebrew preinstalled but it's not added to PATH by default (see the runner-images README: you need eval "$(/home/linuxbrew/.linuxbrew/bin/brew shellenv)" or the Homebrew/actions/setup-homebrew action first). As written, this step will very likely fail with brew: command not found, meaning the whole point of this job wouldn't actually work on a real release run.

Comment thread .github/workflows/publish.yml Outdated
needs: publish
runs-on: ubuntu-latest
# Mirror the publish job's gating so the bump only runs for real releases.
if: github.event.workflow_run.conclusion == 'success' && github.event.workflow_run.event == 'pull_request' && startsWith(github.event.workflow_run.head_branch, 'release/v')

Copy link
Copy Markdown
Owner Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Heads up: once a job defines its own if: that doesn't reference success() or needs.publish.result, that condition replaces the implicit "only run if publish succeeded" gate that needs: publish would otherwise provide — it doesn't add to it. This if: only re-checks the upstream workflow_run trigger fields, not whether the sibling publish job actually succeeded. So if publish fails after pnpm publish already succeeded (e.g. the gh release edit step errors out), this job would still run and could open a public Homebrew/core PR pointing at a release GitHub still shows as an unpublished draft. Adding success() && (or needs.publish.result == 'success' &&) to this condition would close that gap.

fi
gh release edit "$TAG" --draft=false --latest

homebrew:

Copy link
Copy Markdown
Owner Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Minor: this job doesn't check out code or touch GITHUB_TOKEN/OIDC (it uses its own HOMEBREW_BUMP_TOKEN), but it still inherits the workflow-level permissions: id-token: write / contents: write. Might be worth adding permissions: {} here for least privilege, similar to the per-job overrides already used in draft-release.yml.

exit 0
fi

TAG="${HEAD_BRANCH#release/}"

Copy link
Copy Markdown
Owner Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Small DRY note: this TAG="${HEAD_BRANCH#release/}" + regex-validation block is now duplicated a third time (it already exists in the publish job above and in publish-assets.yml). Not blocking, but worth centralizing at some point so the three copies don't drift.

…vilege)

Address review findings on the new homebrew job:

- The custom `if:` replaced the implicit `needs: publish` success gate, so
  the job could still run when publish failed after the npm tarball already
  existed (e.g. `gh release edit` failing) — opening a Homebrew/core PR that
  references an unpublished draft release. Add `success()` to restore the gate.
- GitHub-hosted runners ship Homebrew preinstalled but do not add it to PATH,
  so `brew bump-formula-pr` would fail with command-not-found. Load Homebrew's
  shell environment via `brew shellenv` before invoking brew.
- Scope the job to `permissions: {}`; it only opens a cross-repo PR with its
  own HOMEBREW_BUMP_TOKEN and needs none of the workflow-level contents/id-token
  grants.

Allowlist `linuxbrew` and `shellenv` in cspell for the new shellenv command.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
@dyoshikawa

Copy link
Copy Markdown
Owner Author

Pushed 26336e1 to address the review findings:

  • Success gate: added success() && to the job's if: so it no longer replaces the implicit needs: publish gate — the bump won't run when publish failed after the tarball was already published.
  • brew on PATH: added eval "$(/home/linuxbrew/.linuxbrew/bin/brew shellenv)" before the brew bump-formula-pr call, since GitHub-hosted runners ship Homebrew preinstalled but not on PATH.
  • Least privilege: scoped the job to permissions: {} (it only opens a cross-repo PR with its own HOMEBREW_BUMP_TOKEN).

Also allowlisted linuxbrew/shellenv in cspell. Ran pnpm cicheck locally (all green). Left the TAG-derivation DRY cleanup and the HOMEBREW_BUMP_TOKEN fine-grained-PAT suggestion out of scope for a follow-up.

@rudironsoni

Copy link
Copy Markdown
Contributor

Thanks, dyoshikawa

@dyoshikawa
dyoshikawa merged commit 56883b1 into main Jul 1, 2026
10 checks passed
@dyoshikawa
dyoshikawa deleted the resolve-issue-2074-homebrew-autobump branch July 1, 2026 08:11
@rudironsoni

Copy link
Copy Markdown
Contributor

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Homebrew formula is behind latest GitHub release

3 participants