AikidoSec-safe-chain/docs/homebrew.md
Ethan Setnik e976d100f3 Add Homebrew tap for safe-chain (closes #372)
Adds a publish-homebrew job to the release workflow that renders
Formula/safe-chain.rb from a template (substituting the released
version + per-platform SHA256s parsed from the install script asset)
and pushes it to AikidoSec/homebrew-tap on every non-prerelease.

Users can then install via:

  brew install AikidoSec/tap/safe-chain
  safe-chain setup

The formula downloads the existing prebuilt single-file binaries
from the GitHub release (the same ones the install script uses),
so there is no extra build work in this pipeline.

One-time maintainer setup (creating the AikidoSec/homebrew-tap repo
and adding HOMEBREW_TAP_TOKEN as a secret on safe-chain) is documented
in docs/homebrew.md.

Tested locally on macOS arm64 with Homebrew 5.1.11:
  - brew style: 0 offenses
  - brew install --build-from-source: success
  - brew test: 2 assertions pass (--version + help)
  - brew audit --new: 0 offenses

This PR addresses item 1 of #372 (Homebrew only). The integrity-check
piece in item 2 has already shipped — install-safe-chain.sh already
calls verify_checksum() against the baked-in SHA256s. winget and
Chocolatey are not in scope here; see docs/homebrew.md for notes on
why they belong in separate PRs.
2026-05-15 11:36:57 -04:00

6.7 KiB
Raw Blame History

Homebrew Tap

End-user install instructions live in the top-level README.md. This page is for maintainers: how the tap is wired up and what one-time setup is required.

Architecture

AikidoSec/safe-chain                    AikidoSec/homebrew-tap
┌──────────────────────────┐           ┌──────────────────────────┐
│ tag push (vX.Y.Z)        │           │ Formula/                 │
│   → create-binaries      │           │   safe-chain.rb          │
│   → publish-binaries     │           │     ^                    │
│     (draft release +     │           │     │                    │
│      install-*.sh with   │           │     │                    │
│      baked SHA256s)      │           │     │ git push           │
│                          │           │     │ on each release    │
│ release.published        │ ────────► │     │                    │
│   → publish-homebrew     │           │                          │
│   → publish-npm          │           │                          │
└──────────────────────────┘           └──────────────────────────┘

The publish-homebrew job in .github/workflows/build-and-release.yml fires on release.published (i.e. when a maintainer publishes the draft release that publish-binaries created on the tag push). It:

  1. Downloads the release's install-safe-chain.sh asset (already has per-platform SHA256s baked in by the earlier publish-binaries step — see lines 7686 of the workflow).
  2. Parses the four SHA256s it needs (macOS x64/arm64, Linux x64/arm64) out of the install script.
  3. Renders Formula/safe-chain.rb from a quoted heredoc template, substituting the new version and SHAs.
  4. Pushes the rendered formula to the main branch of AikidoSec/homebrew-tap.

Prereleases are skipped (if: ... && !github.event.release.prerelease) so beta tags like 0.0.1-sha256-in-installer-beta don't update the stable tap.

One-time setup

This needs to happen exactly once. Until it's done, the publish-homebrew job will fail on the next release with a 404 on the tap-repo checkout (which is harmless — it doesn't block npm publish — but the job will appear red).

1. Create the tap repository

Create a new public repository under the AikidoSec organisation called homebrew-tap (the homebrew- prefix is required by brew tap; users will run brew tap AikidoSec/tap which expands to AikidoSec/homebrew-tap).

Owner:        AikidoSec
Repository:   homebrew-tap
Visibility:   Public
Initialize:   Yes (add a README — anything will do, the workflow only touches Formula/safe-chain.rb)
Default branch: main

No further repo configuration is required. The Formula/ directory will be created by the workflow on the first publish.

2. Create the token used by the workflow

The workflow needs to push to AikidoSec/homebrew-tap from AikidoSec/safe-chain. The default GITHUB_TOKEN is scoped to the safe-chain repo only and cannot push cross-repo, so we need a token that grants write access to the tap repo.

Recommended: a fine-grained personal access token from a maintenance bot account (or a deploy-token-style PAT), scoped to AikidoSec/homebrew-tap only.

  • Resource owner: AikidoSec
  • Repository access: Only select repositoriesAikidoSec/homebrew-tap
  • Repository permissions: Contents: Read and write
  • Expiration: 1 year (set a calendar reminder; expired tokens silently break releases)

Alternative: a GitHub App installation with contents:write on the tap repo and a step that exchanges the App's private key for an installation token. More secure for an org-owned automation but more setup. The PAT path is fine for a low-blast-radius tap.

3. Add the token as a secret on AikidoSec/safe-chain

Settings → Secrets and variables → Actions → New repository secret:

  • Name: HOMEBREW_TAP_TOKEN
  • Value: the PAT (or App installation token) from step 2

That's it. The next release published from main will populate Formula/safe-chain.rb in the tap.

4. (Optional) Bootstrap with the current release

If you want users to be able to brew install AikidoSec/tap/safe-chain immediately rather than waiting for the next release, manually trigger the workflow against the current tag:

gh workflow run "Create Release" --repo AikidoSec/safe-chain --ref <current-tag>

Or simply cut a patch release; the next normal release will populate the tap.

How users install

brew install AikidoSec/tap/safe-chain
safe-chain setup

brew install downloads the prebuilt platform-specific binary from the GitHub release (verified against the SHA256 in the formula) and places it on PATH as safe-chain. The user then runs safe-chain setup (or safe-chain setup-ci) to install the shell aliases that wrap npm, yarn, pnpm, pip, etc. This second step is the same as the one in the curl | sh install path; Homebrew doesn't (and shouldn't) modify user shell rc files at install time.

How users upgrade

brew upgrade safe-chain

The formula's livecheck block uses GitHub's releases/latest strategy, so brew outdated and Homebrew's auto-bump tooling pick up new versions automatically once the tap repo has been updated by the release workflow.

Testing the formula locally

brew tap AikidoSec/tap
brew install --build-from-source AikidoSec/tap/safe-chain
brew test AikidoSec/tap/safe-chain
brew style $(brew --repository)/Library/Taps/aikidosec/homebrew-tap/Formula/safe-chain.rb
brew audit --new --formula AikidoSec/tap/safe-chain

The PR that wired this up (#TBD) validated all four of these against the 1.5.3 release on macOS arm64 (Homebrew 5.1.11).

Future work

  • Submission to homebrew-core: A custom tap is the lowest-friction path. If we ever want brew install safe-chain (no tap prefix) to work, the formula needs to be submitted to Homebrew's central homebrew-core repo. That requires the project to meet Homebrew's notability criteria and the formula to build from source (no prebuilt binaries) — which would mean either compiling the Node + pkg bundling in-formula or rewriting the wrapper in a compiled language. Not in scope right now.
  • winget and Chocolatey: Also requested in #372. Each has its own manifest format and release-time automation; they should be separate PRs.