mirror of
https://github.com/AikidoSec/safe-chain.git
synced 2026-05-26 12:10:49 +00:00
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.
114 lines
6.7 KiB
Markdown
114 lines
6.7 KiB
Markdown
# Homebrew Tap
|
||
|
||
End-user install instructions live in the top-level [`README.md`](../README.md#macos--linux-homebrew). 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`](../.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 76–86 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 repositories** → `AikidoSec/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:
|
||
|
||
```sh
|
||
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
|
||
|
||
```sh
|
||
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
|
||
|
||
```sh
|
||
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
|
||
|
||
```sh
|
||
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](https://docs.brew.sh/Acceptable-Formulae) 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](https://github.com/AikidoSec/safe-chain/issues/372). Each has its own manifest format and release-time automation; they should be separate PRs.
|