chore: protect main, Homebrew install path, and PR title checks - #11
Conversation
📝 WalkthroughWalkthroughRepository governance and review workflows were added, installation documentation and Homebrew metadata were revised, and the release workflow now automates Homebrew formula updates through a generated pull request. ChangesRepository governance and review controls
Installation and Homebrew packaging
Release and formula automation
Estimated code review effort: 3 (Moderate) | ~20 minutes Sequence Diagram(s)sequenceDiagram
participant ReleaseTag
participant ReleaseWorkflow
participant BumpFormula
participant FormulaPR
ReleaseTag->>ReleaseWorkflow: triggers release build and publish
ReleaseWorkflow->>BumpFormula: runs bump-formula.sh with release tag
BumpFormula->>BumpFormula: downloads tarball and computes SHA-256
BumpFormula->>FormulaPR: updates Homebrew formula
FormulaPR->>FormulaPR: opens pull request against main
Suggested reviewers: Poem
🚥 Pre-merge checks | ✅ 4 | ❌ 1❌ Failed checks (1 warning)
✅ Passed checks (4 passed)
✨ Finishing Touches📝 Generate docstrings
Comment |
There was a problem hiding this comment.
Pull request overview
This PR formalizes repository governance and main-branch protection expectations, shifts macOS installation guidance toward Homebrew (including upgrade paths), and extends CI/workflows to enforce conventional PR titles and automate Homebrew formula bumps after tagged releases.
Changes:
- Document governance/rulesets and add CODEOWNERS + conventional PR title linting workflow.
- Update README/INSTALL docs to recommend Homebrew for macOS installs/upgrades, while keeping source/Linux paths documented.
- Extend release workflow to open an automated PR bumping
Formula/microbridge.rbafter eachv*tag, supported by a new bump script.
Reviewed changes
Copilot reviewed 10 out of 10 changed files in this pull request and generated 3 comments.
Show a summary per file
| File | Description |
|---|---|
| scripts/bump-formula.sh | Adds automation to update formula URL + sha256 for a tagged release. |
| README.md | Updates install section to recommend Homebrew on macOS and separates source/Linux instructions. |
| INSTALL.md | Reworks install guide around Homebrew as the primary macOS path and clarifies other install modes. |
| Formula/microbridge.rb | Converts formula into a tag-pinned source build with service + caveats geared toward upgrades. |
| docs/governance.md | Documents branch protection/rulesets, PR title conventions, and Homebrew update flow. |
| CONTRIBUTING.md | Aligns contribution guidance with protected main and required CI/title conventions. |
| .github/workflows/release.yml | Updates release notes install instructions and adds a job to PR-bump the Homebrew formula. |
| .github/workflows/pr-title.yml | Adds Conventional Commits-style PR title enforcement via CI. |
| .github/workflows/ci.yml | Adds clarifying comments about keeping required-check names stable for rulesets. |
| .github/CODEOWNERS | Adds default code ownership mapping for review routing. |
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
| if command -v gh >/dev/null; then | ||
| gh api "repos/DevVig/microbridge/tarball/${FULL}" >"$TMP" | ||
| else | ||
| curl -fsSL "$URL" -o "$TMP" | ||
| fi |
| brew autoupdate start --upgrade --cleanup --immediate | ||
| ``` | ||
|
|
||
| Details: [INSTALL.md](../INSTALL.md#homebrew-recommended-on-macos). |
| | `$(brew --prefix)/bin/microbridged` | Daemon (Homebrew) | | ||
| | `~/.local/bin/microbridged` | Daemon (source install script) | |
There was a problem hiding this comment.
Actionable comments posted: 9
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
Inline comments:
In @.github/workflows/release.yml:
- Line 10: Restrict pull-request write access from the workflow-wide permissions
to the bump-formula job only. Remove the global pull-requests write permission
and add it under the bump-formula job’s permissions, preserving all other job
permissions and workflow behavior.
- Around line 86-88: Update the actions/checkout@v5 step in the release workflow
to set persist-credentials to false alongside the existing ref: main
configuration, while leaving the explicit pull-request token handling unchanged.
- Line 94: Update the release workflow invocation of bump-formula.sh to pass
github.ref_name through an environment variable instead of interpolating it into
shell code. In scripts/bump-formula.sh, validate the received tag before using
it in URLs or embedded Perl code, and reject invalid values.
In `@docs/governance.md`:
- Around line 34-37: Update the release steps in governance.md to state that the
automated Homebrew formula-update PR opened by the Release workflow must pass CI
and be merged before users can receive the new version through Homebrew.
- Around line 11-14: Update the “Direct pushes” entry in the governance table to
list pull-request enforcement separately from non-fast-forward protection, and
keep “Force push / delete” as the distinct force-push protection entry. Preserve
the existing blocked status and other governance requirements.
In `@INSTALL.md`:
- Around line 98-106: Update the “Layout after install” table in INSTALL.md to
distinguish service log locations: qualify ~/.microbridge/daemon.log as
source-install-only and add the Homebrew-prefix var/log/microbridge.log path for
Homebrew installations.
- Around line 82-85: Update the Linux foreground installation example in
INSTALL.md to invoke microbridged using its documented installed path under
~/.local/bin, rather than relying on the parent shell’s PATH after running
install.sh.
- Around line 33-35: Update the private tap authentication guidance in
INSTALL.md to recommend a fine-grained personal access token scoped to this
repository with Contents: read, rather than a classic repo-scoped PAT, while
retaining HOMEBREW_GITHUB_API_TOKEN for private tap tarball downloads. Apply the
same corrected guidance to the troubleshooting note near the later
authentication instructions.
In `@scripts/bump-formula.sh`:
- Around line 7-11: Validate TAG against the expected release-tag grammar before
applying the ${TAG#v} transformation or interpolating it into URLs and Perl
commands. Require the intended vX.Y.Z format, reject malformed or unexpected
characters immediately with a clear usage error, and preserve the existing FULL,
FORMULA, and URL derivation for valid tags.
🪄 Autofix (Beta)
Fix all unresolved CodeRabbit comments on this PR:
- Push a commit to this branch (recommended)
- Create a new PR with the fixes
ℹ️ Review info
⚙️ Run configuration
Configuration used: Organization UI
Review profile: CHILL
Plan: Pro
Run ID: 1d1ed25e-a05d-4f33-a2b3-209c0a9a2df6
📒 Files selected for processing (10)
.github/CODEOWNERS.github/workflows/ci.yml.github/workflows/pr-title.yml.github/workflows/release.ymlCONTRIBUTING.mdFormula/microbridge.rbINSTALL.mdREADME.mddocs/governance.mdscripts/bump-formula.sh
|
|
||
| permissions: | ||
| contents: write | ||
| pull-requests: write |
There was a problem hiding this comment.
🔒 Security & Privacy | 🟠 Major | ⚡ Quick win
Scope pull-request write permission to bump-formula.
The workflow-level permission exposes PR write access to build and publish jobs that do not need it. A compromised build step could otherwise create or modify pull requests.
🧰 Tools
🪛 zizmor (1.26.1)
[error] 10-10: overly broad permissions (excessive-permissions): pull-requests: write is overly broad at the workflow level
(excessive-permissions)
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
In @.github/workflows/release.yml at line 10, Restrict pull-request write access
from the workflow-wide permissions to the bump-formula job only. Remove the
global pull-requests write permission and add it under the bump-formula job’s
permissions, preserving all other job permissions and workflow behavior.
Source: Linters/SAST tools
| - uses: actions/checkout@v5 | ||
| with: | ||
| ref: main |
There was a problem hiding this comment.
🔒 Security & Privacy | 🟠 Major | ⚡ Quick win
Disable persisted checkout credentials.
actions/checkout persists the workflow token in local Git configuration by default, while the pull-request action already receives the token explicitly. Set persist-credentials: false to reduce exposure to commands executed in this job.
🔐 Proposed fix
- uses: actions/checkout@v5
with:
ref: main
+ persist-credentials: false📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
| - uses: actions/checkout@v5 | |
| with: | |
| ref: main | |
| - uses: actions/checkout@v5 | |
| with: | |
| ref: main | |
| persist-credentials: false |
🧰 Tools
🪛 zizmor (1.26.1)
[warning] 86-88: credential persistence through GitHub Actions artifacts (artipacked): does not set persist-credentials: false
(artipacked)
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
In @.github/workflows/release.yml around lines 86 - 88, Update the
actions/checkout@v5 step in the release workflow to set persist-credentials to
false alongside the existing ref: main configuration, while leaving the explicit
pull-request token handling unchanged.
Source: Linters/SAST tools
| GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} | ||
| run: | | ||
| chmod +x scripts/bump-formula.sh | ||
| ./scripts/bump-formula.sh "${{ github.ref_name }}" |
There was a problem hiding this comment.
🔒 Security & Privacy | 🟠 Major | ⚡ Quick win
Do not interpolate the release tag directly into shell code.
${{ github.ref_name }} is expanded before Bash parses the script. Pass it through env, invoke the environment variable, and validate the tag in scripts/bump-formula.sh before using it in URLs or embedded Perl code.
🛡️ Proposed fix
env:
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
+ RELEASE_TAG: ${{ github.ref_name }}
run: |
chmod +x scripts/bump-formula.sh
- ./scripts/bump-formula.sh "${{ github.ref_name }}"
+ ./scripts/bump-formula.sh "$RELEASE_TAG"🧰 Tools
🪛 zizmor (1.26.1)
[error] 94-94: code injection via template expansion (template-injection): may expand into attacker-controllable code
(template-injection)
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
In @.github/workflows/release.yml at line 94, Update the release workflow
invocation of bump-formula.sh to pass github.ref_name through an environment
variable instead of interpolating it into shell code. In
scripts/bump-formula.sh, validate the received tag before using it in URLs or
embedded Perl code, and reject invalid values.
Source: Linters/SAST tools
| | Direct pushes | Blocked (`non_fast_forward` + PR required) | | ||
| | Force push / delete | Blocked | | ||
| | Merge method | **Squash only** | | ||
| | Status checks | `rust (ubuntu-latest)`, `rust (macos-latest)`, `ui` (strict) | |
There was a problem hiding this comment.
🔒 Security & Privacy | 🟡 Minor | ⚡ Quick win
Separate PR protection from force-push protection.
non_fast_forward blocks force pushes; requiring a pull request is the separate rule that prevents direct updates. Combining them makes future governance audits ambiguous. (docs.github.com)
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
In `@docs/governance.md` around lines 11 - 14, Update the “Direct pushes” entry in
the governance table to list pull-request enforcement separately from
non-fast-forward protection, and keep “Force push / delete” as the distinct
force-push protection entry. Preserve the existing blocked status and other
governance requirements.
| 1. Land changes on `main` via PR. | ||
| 2. Tag `vX.Y.Z` and push the tag → **Release** workflow builds archives and | ||
| updates the Homebrew formula checksums. | ||
| 3. Users upgrade with `brew update && brew upgrade microbridge`. |
There was a problem hiding this comment.
🗄️ Data Integrity & Integration | 🟡 Minor | ⚡ Quick win
Document the formula PR merge step.
The release workflow opens an automated formula-update PR; it does not update main during the release run. State that this PR must pass CI and be merged before users receive the new version through Homebrew.
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
In `@docs/governance.md` around lines 34 - 37, Update the release steps in
governance.md to state that the automated Homebrew formula-update PR opened by
the Release workflow must pass CI and be merged before users can receive the new
version through Homebrew.
| Private tap note: if the GitHub repo is private, authenticate once | ||
| (`gh auth login` or a `HOMEBREW_GITHUB_API_TOKEN`) so `brew` can fetch the | ||
| tarball. |
There was a problem hiding this comment.
🔒 Security & Privacy | 🟡 Minor | ⚡ Quick win
🧩 Analysis chain
🏁 Script executed:
set -euo pipefail
printf '\n## INSTALL.md excerpt\n'
nl -ba INSTALL.md | sed -n '1,160p'
printf '\n## Search for Homebrew/GitHub token references\n'
rg -n "HOMEBREW_GITHUB_API_TOKEN|repo-scoped|fine-grained|Contents: read|gh auth login|private repo|tarball|brew" -S .Repository: DevVig/microbridge
Length of output: 219
🌐 Web query:
GitHub fine-grained personal access token repository archives Contents: read HOMEBREW_GITHUB_API_TOKEN Homebrew private tap tarball
💡 Result:
GitHub fine-grained personal access tokens (PATs) can be used to download private repository archives (tarballs) via the GitHub REST API, but they require specific configurations and permissions. Permissions: To download a repository tarball, a fine-grained PAT requires "Contents: read" repository permissions [1][2][3]. API Endpoint: While classic PATs could often access archives directly via the github.com web URL, fine-grained PATs are generally required to use the official GitHub REST API endpoint: GET /repos/{owner}/{repo}/tarball/{ref} [4][5]. Authentication Scheme: A known quirk exists where some tools using hard-coded "Bearer" authentication schemes with fine-grained PATs may fail with a 401 Unauthorized error when accessing the tarball endpoint [3]. Using the "token" authentication scheme (e.g., Authorization: token ) instead of "Bearer" is often necessary to successfully authorize the request [3]. Homebrew Context: Homebrew supports using fine-grained PATs via the HOMEBREW_GITHUB_API_TOKEN environment variable. Historically, Homebrew's internal regex for validating tokens did not recognize the github_pat_ prefix used by fine-grained PATs, but this has been updated [6][7]. Additionally, recent changes in Homebrew (as of mid-2026) have improved how the application handles environment variable scrubbing during formula/cask evaluation, ensuring that HOMEBREW_GITHUB_API_TOKEN remains available to private taps so they can resolve authenticated assets [8][9]. Users experiencing issues with private taps downloading artifacts should ensure their Homebrew installation is up to date, as these updates were specifically designed to preserve token availability during the evaluation process [9].
Citations:
- 1: https://docs.github.com/en/authentication/keeping-your-account-and-data-secure/managing-your-personal-access-tokens?WT.mc_id=25daysofserverless-github-jopapa
- 2: https://docs.github.com/en/authentication/keeping-your-account-and-data-secure/managing-your-personal-access-tokens.md
- 3: Hard-coded
Bearerauth scheme breaks fine-grained PATs on GitHub tarball downloads unjs/giget#263 - 4: fetchFromGitHub: use the API tarball endpoint for
privateNixOS/nixpkgs#321484 - 5: https://docs.github.com/en/rest/authentication/endpoints-available-for-fine-grained-personal-access-tokens?apiVersion=2022-11-28
- 6: Fine-grained PATs do not work when used as $HOMEBREW_GITHUB_API_TOKEN Homebrew/brew#16978
- 7: u/g/api.rb: support GitHub App JWTs Homebrew/brew#23017
- 8: env scrubbing breaks private-tap downloads Homebrew/brew#22430
- 9: Preserve GitHub token during eval Homebrew/brew#22431
🏁 Script executed:
set -euo pipefail
printf '\n## INSTALL.md lines 1-170\n'
nl -ba INSTALL.md | sed -n '1,170p'
printf '\n## Token-related references in repository\n'
rg -n "HOMEBREW_GITHUB_API_TOKEN|fine-grained|repo-scoped|Contents: read|private repo|tarball" -S .Repository: DevVig/microbridge
Length of output: 223
🏁 Script executed:
set -euo pipefail
printf '\n## INSTALL.md excerpt\n'
sed -n '1,170p' INSTALL.md | cat -n
printf '\n## Token-related references in repository\n'
rg -n "HOMEBREW_GITHUB_API_TOKEN|fine-grained|repo-scoped|Contents: read|private repo|tarball|gh auth login" -S .Repository: DevVig/microbridge
Length of output: 4601
Prefer a fine-grained PAT here
Use a fine-grained token limited to this repository with Contents: read instead of a classic repo PAT; HOMEBREW_GITHUB_API_TOKEN can be used for private tap tarball downloads. Also update the troubleshooting note at lines 121-122.
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
In `@INSTALL.md` around lines 33 - 35, Update the private tap authentication
guidance in INSTALL.md to recommend a fine-grained personal access token scoped
to this repository with Contents: read, rather than a classic repo-scoped PAT,
while retaining HOMEBREW_GITHUB_API_TOKEN for private tap tarball downloads.
Apply the same corrected guidance to the troubleshooting note near the later
authentication instructions.
| ./scripts/install-linux-systemd.sh | ||
| # or: | ||
| ./scripts/install.sh --no-launchd && microbridged | ||
| ``` |
There was a problem hiding this comment.
🎯 Functional Correctness | 🟡 Minor | ⚡ Quick win
Use the installed binary path in the Linux foreground example.
The layout documents the source binary at ~/.local/bin/microbridged, but a child install script cannot modify the parent shell’s PATH. The chained microbridged command can therefore fail with “command not found.”
🛠️ Proposed fix
-./scripts/install.sh --no-launchd && microbridged
+./scripts/install.sh --no-launchd
+"$HOME/.local/bin/microbridged"📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
| ./scripts/install-linux-systemd.sh | |
| # or: | |
| ./scripts/install.sh --no-launchd && microbridged | |
| ``` | |
| ./scripts/install-linux-systemd.sh | |
| # or: | |
| ./scripts/install.sh --no-launchd | |
| "$HOME/.local/bin/microbridged" |
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
In `@INSTALL.md` around lines 82 - 85, Update the Linux foreground installation
example in INSTALL.md to invoke microbridged using its documented installed path
under ~/.local/bin, rather than relying on the parent shell’s PATH after running
install.sh.
| ## Layout after install | ||
|
|
||
| | Path | Purpose | | ||
| |---|---| | ||
| | `~/.local/bin/microbridged` | Daemon | | ||
| | `~/.local/bin/microbridgectl` | CLI | | ||
| | `~/Library/LaunchAgents/ai.microbridge.daemon.plist` | macOS autostart | | ||
| | `$(brew --prefix)/bin/microbridged` | Daemon (Homebrew) | | ||
| | `~/.local/bin/microbridged` | Daemon (source install script) | | ||
| | `~/.microbridge/microbridged.sock` | Local NDJSON socket | | ||
| | `~/.microbridge/config.toml` | Key source, lighting, appearance | | ||
| | `~/.microbridge/daemon.log` | launchd stdout/stderr | | ||
| | `~/.microbridge/daemon.log` | launchd / service logs | |
There was a problem hiding this comment.
🎯 Functional Correctness | 🟡 Minor | ⚡ Quick win
Document the Homebrew log location separately.
The formula writes service logs to var/log/microbridge.log, not ~/.microbridge/daemon.log. Qualify the latter as source-install-only and add the Homebrew-prefix path so troubleshooting points users to a real file.
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
In `@INSTALL.md` around lines 98 - 106, Update the “Layout after install” table in
INSTALL.md to distinguish service log locations: qualify
~/.microbridge/daemon.log as source-install-only and add the Homebrew-prefix
var/log/microbridge.log path for Homebrew installations.
| TAG="${1:?usage: $0 vX.Y.Z}" | ||
| TAG="${TAG#v}" | ||
| FULL="v${TAG}" | ||
| FORMULA="$ROOT/Formula/microbridge.rb" | ||
| URL="https://github.com/DevVig/microbridge/archive/refs/tags/${FULL}.tar.gz" |
There was a problem hiding this comment.
🔒 Security & Privacy | 🟠 Major | ⚡ Quick win
Validate the release tag before interpolation.
The tag is used in API paths, URLs, and later inside a Perl program. Reject anything outside the intended version-tag grammar before stripping v; otherwise malformed refs can break formula updates or enable injection when called by the release workflow.
🛡️ Proposed fix
TAG="${1:?usage: $0 vX.Y.Z}"
+if [[ ! "$TAG" =~ ^v[0-9]+\.[0-9]+\.[0-9]+([-+][0-9A-Za-z.-]+)?$ ]]; then
+ echo "invalid release tag: $TAG" >&2
+ exit 2
+fi
TAG="${TAG#v}"📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
| TAG="${1:?usage: $0 vX.Y.Z}" | |
| TAG="${TAG#v}" | |
| FULL="v${TAG}" | |
| FORMULA="$ROOT/Formula/microbridge.rb" | |
| URL="https://github.com/DevVig/microbridge/archive/refs/tags/${FULL}.tar.gz" | |
| TAG="${1:?usage: $0 vX.Y.Z}" | |
| if [[ ! "$TAG" =~ ^v[0-9]+\.[0-9]+\.[0-9]+([-+][0-9A-Za-z.-]+)?$ ]]; then | |
| echo "invalid release tag: $TAG" >&2 | |
| exit 2 | |
| fi | |
| TAG="${TAG#v}" | |
| FULL="v${TAG}" | |
| FORMULA="$ROOT/Formula/microbridge.rb" | |
| URL="https://github.com/DevVig/microbridge/archive/refs/tags/${FULL}.tar.gz" |
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
In `@scripts/bump-formula.sh` around lines 7 - 11, Validate TAG against the
expected release-tag grammar before applying the ${TAG#v} transformation or
interpolating it into URLs and Perl commands. Require the intended vX.Y.Z
format, reject malformed or unexpected characters immediately with a clear usage
error, and preserve the existing FULL, FORMULA, and URL derivation for valid
tags.
Source: Linters/SAST tools
Summary
main(PR-only, squash, required CI)brew upgrade/brew autoupdate)Formula/microbridge.rbvia PR after eachv*tagTest plan
Protect main,Protect release tags)rust,ui,conventional title)mainMade with Cursor
Summary by CodeRabbit
New Features
Documentation
Release Improvements