From 785d1bbd8e09b05bf2310db9bdb88a290513816f Mon Sep 17 00:00:00 2001 From: Jonathan Borgwing Date: Fri, 17 Jul 2026 14:06:14 -0400 Subject: [PATCH] chore: protect main, Homebrew install path, and PR title checks --- .github/CODEOWNERS | 8 ++ .github/workflows/ci.yml | 3 + .github/workflows/pr-title.yml | 34 +++++++++ .github/workflows/release.yml | 48 ++++++++++-- CONTRIBUTING.md | 10 ++- Formula/microbridge.rb | 21 +++-- INSTALL.md | 135 ++++++++++++++++++--------------- README.md | 22 +++--- docs/governance.md | 62 +++++++++++++++ scripts/bump-formula.sh | 29 +++++++ 10 files changed, 288 insertions(+), 84 deletions(-) create mode 100644 .github/CODEOWNERS create mode 100644 .github/workflows/pr-title.yml create mode 100644 docs/governance.md create mode 100755 scripts/bump-formula.sh diff --git a/.github/CODEOWNERS b/.github/CODEOWNERS new file mode 100644 index 0000000..71981d7 --- /dev/null +++ b/.github/CODEOWNERS @@ -0,0 +1,8 @@ +# Default owners for review routing (optional reviews; PRs still required on main). +* @DevVig + +/crates/ @DevVig +/apps/microbridge-ui/ @DevVig +/adapters/ @DevVig +/Formula/ @DevVig +/.github/ @DevVig diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 1e49b94..2110674 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -41,3 +41,6 @@ jobs: cache-dependency-path: apps/microbridge-ui/package-lock.json - run: npm ci - run: npm run build + + # Keep required-check names stable for the main branch ruleset. + # Job names above are what GitHub shows as status contexts. diff --git a/.github/workflows/pr-title.yml b/.github/workflows/pr-title.yml new file mode 100644 index 0000000..bfd2f7e --- /dev/null +++ b/.github/workflows/pr-title.yml @@ -0,0 +1,34 @@ +name: PR title + +on: + pull_request_target: + types: [opened, edited, synchronize, reopened] + +permissions: + pull-requests: read + +jobs: + lint: + name: conventional title + runs-on: ubuntu-latest + steps: + - uses: amannn/action-semantic-pull-request@v5 + env: + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + with: + types: | + feat + fix + docs + style + refactor + perf + test + build + ci + chore + adapter + revert + requireScope: false + subjectPattern: .+ + wip: true diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index 0af9174..370f894 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -7,6 +7,7 @@ on: permissions: contents: write + pull-requests: write jobs: build: @@ -18,7 +19,7 @@ jobs: include: - os: macos-latest target: aarch64-apple-darwin - - os: macos-13 + - os: macos-latest target: x86_64-apple-darwin - os: ubuntu-latest target: x86_64-unknown-linux-gnu @@ -29,8 +30,7 @@ jobs: targets: ${{ matrix.target }} - uses: Swatinem/rust-cache@v2 - name: Build - run: | - cargo build --release -p microbridged -p microbridgectl --target ${{ matrix.target }} + run: cargo build --release -p microbridged -p microbridgectl --target ${{ matrix.target }} - name: Package run: | STAGE=microbridge-${{ github.ref_name }}-${{ matrix.target }} @@ -64,10 +64,46 @@ jobs: generate_release_notes: true files: release-assets/* body: | - ## Install + ## Install (macOS) ```sh - ./scripts/install-from-release.sh ${{ github.ref_name }} + brew tap DevVig/microbridge https://github.com/DevVig/microbridge + brew install microbridge + brew services start microbridge ``` - Or from source: see [INSTALL.md](INSTALL.md). + Upgrade later: `brew update && brew upgrade microbridge` + + Binary archive: `./scripts/install-from-release.sh ${{ github.ref_name }}` + + Full guide: [INSTALL.md](INSTALL.md). + + bump-formula: + name: bump Homebrew formula + needs: publish + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v5 + with: + ref: main + - name: Bump Formula url + sha256 + env: + GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} + run: | + chmod +x scripts/bump-formula.sh + ./scripts/bump-formula.sh "${{ github.ref_name }}" + - name: Open PR + uses: peter-evans/create-pull-request@v7 + with: + token: ${{ secrets.GITHUB_TOKEN }} + commit-message: "chore(brew): bump formula to ${{ github.ref_name }}" + title: "chore(brew): bump formula to ${{ github.ref_name }}" + body: | + Auto-bump `Formula/microbridge.rb` after release `${{ github.ref_name }}`. + + After merge, users get the new version via: + ```sh + brew update && brew upgrade microbridge + ``` + branch: chore/brew-${{ github.ref_name }} + delete-branch: true diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 9b3ea48..1b1d7ed 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -53,12 +53,18 @@ Assets are attached to the GitHub Release; users can run ## Commits and PRs -- Conventional commits (`feat:`, `fix:`, `docs:`, `adapter:` for adapter - work). +`main` is protected — **no direct pushes**. Open a PR; squash-merge only. + +- PR titles must be Conventional Commits (`feat:`, `fix:`, `docs:`, + `adapter:`, …) — enforced by CI (`PR title` workflow). +- Required checks: `rust (ubuntu-latest)`, `rust (macos-latest)`, `ui`. +- Resolve review threads before merge. - One logical change per PR; small PRs merge fast here. - PRs must say how they were tested — "ran the reference adapter against the daemon and watched the frames" is a fine answer at this stage. +See [docs/governance.md](docs/governance.md). + ## Reporting security issues Privately, please — see [SECURITY.md](SECURITY.md). diff --git a/Formula/microbridge.rb b/Formula/microbridge.rb index 6b32ce0..468aadd 100644 --- a/Formula/microbridge.rb +++ b/Formula/microbridge.rb @@ -1,11 +1,21 @@ -# Homebrew formula (build-from-source until a tap/bottle is published). +# typed: false +# frozen_string_literal: true + +# Homebrew formula for Microbridge (source build — fast enough for alpha). # -# brew install --build-from-source ./Formula/microbridge.rb +# brew tap DevVig/microbridge https://github.com/DevVig/microbridge +# brew install microbridge # brew services start microbridge # +# Upgrade (auto-update path): +# brew update && brew upgrade microbridge +# brew autoupdate start --upgrade --cleanup # optional background updates +# class Microbridge < Formula desc "Open-source control plane for the Codex Micro" homepage "https://github.com/DevVig/microbridge" + url "https://github.com/DevVig/microbridge/archive/refs/tags/v0.0.1.tar.gz" + sha256 "f171c275890add016045b0bbde54330f104b6d5db3a9d16c8d366cd5fcdde599" license any_of: ["MIT", "Apache-2.0"] head "https://github.com/DevVig/microbridge.git", branch: "main" @@ -28,9 +38,10 @@ def install def caveats <<~EOS - Config and socket live in ~/.microbridge/ - Check the bus with: microbridgectl status - Full install notes: #{doc}/INSTALL.md (or INSTALL.md in the repo) + Config and socket: ~/.microbridge/ + Status: microbridgectl status + Upgrade: brew update && brew upgrade microbridge + Background updates: brew autoupdate start --upgrade --cleanup EOS end diff --git a/INSTALL.md b/INSTALL.md index b541585..58928df 100644 --- a/INSTALL.md +++ b/INSTALL.md @@ -1,111 +1,122 @@ # Installing Microbridge Microbridge is a local daemon plus an optional companion UI. There is **no -network** and **no cloud account** — install puts binaries on your machine and -(on macOS) a per-user launchd agent. +cloud account** — install puts binaries on your machine and runs a user-level +service. -## Requirements - -| Piece | Need | -|---|---| -| Daemon | Rust stable (`rustup`), macOS 13+ or Linux | -| Companion UI (optional) | Node ≥ 20; full `.app` also needs Xcode CLT | -| Hardware LEDs | Codex Micro over USB (HID packing still landing — mock works without hardware) | +## Recommended on macOS: Homebrew (with updates) -## Quick install (macOS, from source) +This is the easy path. You do **not** need to clone the repo. Homebrew owns +install, upgrades, and the launchd service. ```sh -git clone https://github.com/DevVig/microbridge.git -cd microbridge -./scripts/install.sh +brew tap DevVig/microbridge https://github.com/DevVig/microbridge +brew install microbridge +brew services start microbridge +microbridgectl status ``` -This will: - -1. `cargo build --release` for `microbridged` and `microbridgectl` -2. Install them to `~/.local/bin` (override with `MICROBRIDGE_BIN=…`) -3. Ensure `~/.local/bin` is on your PATH (prints a hint if not) -4. Install and start the launchd agent `ai.microbridge.daemon` -5. Write config defaults under `~/.microbridge/` - -Verify: +### Updates ```sh -microbridgectl status -# or: -tail -f ~/.microbridge/daemon.log +brew update && brew upgrade microbridge +brew services restart microbridge ``` -### Optional companion UI +Optional **background** upgrades (Homebrew’s autoupdate): ```sh -./scripts/install.sh --with-ui -# web preview during development: -cd apps/microbridge-ui && npm install && npm run dev +brew autoupdate start --upgrade --cleanup --immediate +# later: brew autoupdate status / brew autoupdate stop ``` -`--with-ui` installs frontend deps and, when Tauri/Xcode tooling is available, -attempts `npm run tauri build`. You can always run the Vite UI against a live -daemon without bundling an `.app`. +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. -## Linux (from source) +Uninstall: ```sh -./scripts/install.sh --no-launchd -# run in the foreground, or add your own systemd --user unit: -microbridged +brew services stop microbridge +brew uninstall microbridge +# optional: brew untap DevVig/microbridge ``` -A sample user unit is in [`scripts/microbridge.service`](scripts/microbridge.service). +Governance / why this path: [docs/governance.md](docs/governance.md). -## Homebrew (skeleton) +--- + +## Requirements + +| Piece | Need | +|---|---| +| Daemon (Homebrew) | Homebrew; Rust pulled in as a build dependency | +| Daemon (from source) | Rust stable (`rustup`), macOS 13+ or Linux | +| Companion UI (optional) | Node ≥ 20; full `.app` also needs Xcode CLT | +| Hardware LEDs | Codex Micro over USB (HID packing still landing — mock works without hardware) | + +## From source (developers) ```sh -brew install --build-from-source ./Formula/microbridge.rb -brew services start microbridge # when using the formula's service block +git clone https://github.com/DevVig/microbridge.git +cd microbridge +./scripts/install.sh # macOS: binaries + launchd +# ./scripts/install.sh --with-ui +# ./scripts/install-linux-systemd.sh +microbridgectl status ``` -A published tap/bottle is not available yet — use `./scripts/install.sh` for -day-to-day installs. - -## Install from a GitHub Release +Uninstall: `./scripts/uninstall.sh` (add `--purge` to remove `~/.microbridge`). -When a `v*` tag is pushed, CI attaches platform archives. Then: +### Optional companion UI ```sh -./scripts/install-from-release.sh v0.0.1 -# or latest: -./scripts/install-from-release.sh +./scripts/install.sh --with-ui +# or during development: +cd apps/microbridge-ui && npm install && npm run dev ``` -## Uninstall +## Linux ```sh -./scripts/uninstall.sh +./scripts/install-linux-systemd.sh +# or: +./scripts/install.sh --no-launchd && microbridged ``` -Removes the launchd agent, binaries from `MICROBRIDGE_BIN` / `~/.local/bin`, -and optionally (`--purge`) `~/.microbridge/` (config, socket, logs). +Sample unit: [`scripts/microbridge.service`](scripts/microbridge.service). + +## Install from a GitHub Release (binaries) + +When a `v*` tag is published, CI attaches platform archives: + +```sh +./scripts/install-from-release.sh # latest +./scripts/install-from-release.sh v0.0.1 +``` ## 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 | ## Troubleshooting -**`microbridgectl: connect …`** — daemon not running. On macOS: -`launchctl kickstart -k gui/$(id -u)/ai.microbridge.daemon`. +**`microbridgectl: connect …`** — daemon not running. + +```sh +brew services restart microbridge +# or: +launchctl kickstart -k "gui/$(id -u)/ai.microbridge.daemon" +``` **LEDs stay dark** — HID packing is still best-effort; ChatGPT desktop may -also own the device. Pause that app or use Settings → Pause LEDs while testing -the mock path (`microbridgectl status` still works). +also own the device. -**PATH** — add `export PATH="$HOME/.local/bin:$PATH"` to your shell rc if -`microbridgectl` is not found. +**Homebrew can’t fetch (private repo)** — `gh auth login`, or set +`HOMEBREW_GITHUB_API_TOKEN` to a PAT with `repo` scope. diff --git a/README.md b/README.md index add9acb..613698d 100644 --- a/README.md +++ b/README.md @@ -82,20 +82,24 @@ docs/ protocol, architecture, adapter guide, design, HID notes ## Install -Full guide: **[INSTALL.md](INSTALL.md)**. +Full guide: **[INSTALL.md](INSTALL.md)**. Governance / branch rules: **[docs/governance.md](docs/governance.md)**. + +**macOS (recommended — Homebrew, with upgrades):** ```sh -git clone https://github.com/DevVig/microbridge.git -cd microbridge -./scripts/install.sh # macOS: binaries + launchd -# ./scripts/install.sh --with-ui # also build companion UI -# ./scripts/install-linux-systemd.sh # Linux systemd --user -microbridgectl status +brew tap DevVig/microbridge https://github.com/DevVig/microbridge +brew install microbridge +brew services start microbridge +# updates: brew update && brew upgrade microbridge +# optional background updates: brew autoupdate start --upgrade --cleanup ``` -Uninstall: `./scripts/uninstall.sh` (add `--purge` to remove `~/.microbridge`). +From source / Linux: -From a GitHub Release (after a `v*` tag): `./scripts/install-from-release.sh`. +```sh +./scripts/install.sh # macOS launchd +./scripts/install-linux-systemd.sh # Linux systemd --user +``` ## Building (dev) diff --git a/docs/governance.md b/docs/governance.md new file mode 100644 index 0000000..30e621b --- /dev/null +++ b/docs/governance.md @@ -0,0 +1,62 @@ +# Repository governance + +How changes land on `main`, and how macOS users install/update without cloning. + +## Branch protection (`main`) + +Enforced via GitHub **rulesets** (Settings → Rules): + +| Rule | Setting | +|---|---| +| 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) | +| Conversations | Must be resolved | +| Approvals | 0 required (solo-friendly); stale reviews dismissed | +| Admin bypass | Via pull request only | + +Release tags matching `v*` cannot be deleted or force-moved (admin bypass allowed). + +Repo merge defaults: squash title = PR title, body = PR body, delete head branch +on merge, auto-merge enabled. + +## Commit / PR conventions + +- PR titles must be [Conventional Commits](https://www.conventionalcommits.org/) + (`feat:`, `fix:`, `docs:`, `adapter:`, …) — enforced by the **PR title** workflow. +- Squash merge uses the PR title as the commit subject, so `main` history stays + conventional without a commit-message ruleset (unavailable on this plan). +- See [CONTRIBUTING.md](../CONTRIBUTING.md) and the PR template. + +## Releases + +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`. + +## macOS install + auto-update (Homebrew) + +**This is the supported consumer path** — not cloning the git repo. + +```sh +brew tap DevVig/microbridge https://github.com/DevVig/microbridge +brew install microbridge +brew services start microbridge +``` + +Updates: + +```sh +brew update && brew upgrade microbridge +brew services restart microbridge # if the formula changed +``` + +Optional background updates (Homebrew’s own updater): + +```sh +brew autoupdate start --upgrade --cleanup --immediate +``` + +Details: [INSTALL.md](../INSTALL.md#homebrew-recommended-on-macos). diff --git a/scripts/bump-formula.sh b/scripts/bump-formula.sh new file mode 100755 index 0000000..ca67d13 --- /dev/null +++ b/scripts/bump-formula.sh @@ -0,0 +1,29 @@ +#!/usr/bin/env bash +# Bump Formula/microbridge.rb to a new tag and GitHub archive sha256. +# Usage: ./scripts/bump-formula.sh v0.0.2 +set -euo pipefail + +ROOT="$(cd "$(dirname "$0")/.." && pwd)" +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" + +echo "==> Fetching ${URL}" +TMP="$(mktemp)" +if command -v gh >/dev/null; then + gh api "repos/DevVig/microbridge/tarball/${FULL}" >"$TMP" +else + curl -fsSL "$URL" -o "$TMP" +fi +SHA="$(shasum -a 256 "$TMP" | awk '{print $1}')" +rm -f "$TMP" + +echo "==> Updating formula → ${FULL} sha256=${SHA}" +# Portable in-place edit +perl -0pi -e "s#url \"https://github.com/DevVig/microbridge/archive/refs/tags/v[^\"]+\"#url \"${URL}\"#" "$FORMULA" +perl -0pi -e "s#sha256 \"[a-f0-9]+\"#sha256 \"${SHA}\"#" "$FORMULA" + +echo "Updated $FORMULA" +grep -E 'url |sha256 ' "$FORMULA"