diff --git a/.github/workflows/homebrew-tap.yml b/.github/workflows/homebrew-tap.yml new file mode 100644 index 0000000..5b9d21b --- /dev/null +++ b/.github/workflows/homebrew-tap.yml @@ -0,0 +1,107 @@ +name: Homebrew tap + +on: + release: + types: [published] + workflow_dispatch: + inputs: + tag: + description: Release tag to publish (e.g. v1.0.3) + required: true + type: string + # Temporary: exercise the tap push from this PR branch (workflow_dispatch + # only works once the workflow exists on the default branch). + push: + branches: [49-homebrew] + +permissions: + contents: write + +jobs: + update: + runs-on: ubuntu-latest + steps: + - name: Check tap token + env: + HOMEBREW_TAP_TOKEN: ${{ secrets.HOMEBREW_TAP_TOKEN }} + run: | + if [ -z "$HOMEBREW_TAP_TOKEN" ]; then + echo "HOMEBREW_TAP_TOKEN secret is not set" >&2 + echo "Create a PAT (contents:write on brightdigit/homebrew-tap and brightdigit/git-trees)" >&2 + echo "and add it as repository secret HOMEBREW_TAP_TOKEN." >&2 + exit 1 + fi + + - uses: actions/checkout@v4 + with: + fetch-depth: 0 + token: ${{ secrets.HOMEBREW_TAP_TOKEN }} + + - name: Install git-subrepo + run: | + git clone --depth 1 https://github.com/ingydotnet/git-subrepo.git /tmp/git-subrepo + echo "/tmp/git-subrepo/lib" >> "$GITHUB_PATH" + + - name: Resolve tag + id: meta + run: | + case "${{ github.event_name }}" in + workflow_dispatch) tag='${{ inputs.tag }}' ;; + release) tag='${{ github.event.release.tag_name }}' ;; + push) tag=$(git tag -l 'v*' --sort=-v:refname | head -n1) ;; + *) + echo "Unsupported event: ${{ github.event_name }}" >&2 + exit 1 + ;; + esac + if [ -z "$tag" ]; then + echo "No tag to publish" >&2 + exit 1 + fi + case "$tag" in + v*) ;; + *) + echo "Expected a v-prefixed tag, got: $tag" >&2 + exit 1 + ;; + esac + url="https://github.com/${{ github.repository }}/archive/refs/tags/${tag}.tar.gz" + echo "tag=$tag" >> "$GITHUB_OUTPUT" + echo "url=$url" >> "$GITHUB_OUTPUT" + + - name: Bump formula url and sha256 + run: | + tag='${{ steps.meta.outputs.tag }}' + url='${{ steps.meta.outputs.url }}' + formula=homebrew-tap/Formula/git-trees.rb + + # curl -f fails on a missing tag instead of hashing a 404 body. + sha=$(curl -fsSL "$url" | sha256sum | awk '{ print $1 }') + echo "sha256=$sha" + + # Pass values via ENV so / in the URL cannot break s/// delimiters. + URL="$url" perl -i -pe 's/^(\s*url\s+)"[^"]*"/$1"$ENV{URL}"/' "$formula" + SHA="$sha" perl -i -pe 's/^(\s*sha256\s+)"[^"]*"/$1"$ENV{SHA}"/' "$formula" + + grep -F "url \"$url\"" "$formula" + grep -F "sha256 \"$sha\"" "$formula" + ruby -c "$formula" + + - name: Commit formula bump + run: | + git config user.name 'github-actions[bot]' + git config user.email '41898282+github-actions[bot]@users.noreply.github.com' + git add homebrew-tap/Formula/git-trees.rb + if git diff --staged --quiet; then + echo "Formula already at ${{ steps.meta.outputs.tag }}; nothing to commit" + else + git commit -m "homebrew: git-trees ${{ steps.meta.outputs.tag }}" + git push + fi + + - name: Push subrepo to brightdigit/homebrew-tap + env: + HOMEBREW_TAP_TOKEN: ${{ secrets.HOMEBREW_TAP_TOKEN }} + run: | + git config --global url."https://x-access-token:${HOMEBREW_TAP_TOKEN}@github.com/".insteadOf "https://github.com/" + git subrepo push homebrew-tap diff --git a/README.md b/README.md index e5588cf..4d6795a 100644 --- a/README.md +++ b/README.md @@ -138,6 +138,22 @@ fi `main` is the stable release. A re-install from these URLs picks up the current stable script and template. +**Homebrew.** Not yet published — the formula lives at +[`homebrew-tap/Formula/git-trees.rb`](homebrew-tap/Formula/git-trees.rb) (a +[git subrepo](https://github.com/ingydotnet/git-subrepo) of +[`brightdigit/homebrew-tap`](https://github.com/brightdigit/homebrew-tap)) and +takes effect once it is pushed to the tap (see +[`docs/RELEASING.md`](docs/RELEASING.md)). After that: + +```bash +brew tap brightdigit/tap +brew install git-trees +``` + +Homebrew cannot write to your home directory, so this path installs the script +but not the agents template. `brew install` prints the one command that puts the +bundled template at `~/.config/git-trees/AGENTS.md`. + Either way, make sure the destination is on your `PATH`: ```bash diff --git a/docs/RELEASING.md b/docs/RELEASING.md new file mode 100644 index 0000000..78d41e0 --- /dev/null +++ b/docs/RELEASING.md @@ -0,0 +1,105 @@ +# Releasing + +There is no release automation for cutting tags — those are still done by hand. +Once a GitHub release is published, `.github/workflows/homebrew-tap.yml` bumps +the formula and pushes it to the tap via the +[`homebrew-tap/`](../homebrew-tap) [git subrepo](https://github.com/ingydotnet/git-subrepo). + +`homebrew-tap/Formula/git-trees.rb` in this repo is the **source of truth** for +the formula. Homebrew installs it from +[`brightdigit/homebrew-tap`](https://github.com/brightdigit/homebrew-tap), which +is what `brew tap brightdigit/tap` clones. + +## One-time setup + +### 1. Embed the tap as a subrepo + +From a commit that already contains `homebrew-tap/Formula/git-trees.rb`: + +```bash +git subrepo init homebrew-tap \ + -r https://github.com/brightdigit/homebrew-tap.git \ + -b main +git subrepo push homebrew-tap +``` + +`init` records `homebrew-tap/.gitrepo`; `push` populates the (possibly empty) +tap remote. Requires push access to `brightdigit/homebrew-tap`. + +### 2. Repository secret + +Add a `HOMEBREW_TAP_TOKEN` secret on `brightdigit/git-trees`: a classic PAT or +fine-grained token with `contents: write` on both `brightdigit/git-trees` and +`brightdigit/homebrew-tap`. The workflow uses it to commit the formula bump here +and to `git subrepo push` the tap. + +## Cut a release + +Update `CHANGELOG.md` with the new version's `## What's Changed` section, then: + +```bash +git tag -a v1.0.3 -m "v1.0.3" +git push origin v1.0.3 +``` + +Create the GitHub release for that tag (or publish from the tag). The +`Homebrew tap` workflow then: + +1. Downloads `https://github.com/brightdigit/git-trees/archive/refs/tags/v1.0.3.tar.gz` +2. Rewrites `url` / `sha256` in `homebrew-tap/Formula/git-trees.rb` +3. Commits and pushes that bump to this repo +4. Runs `git subrepo push homebrew-tap` + +To re-run for an existing tag: **Actions → Homebrew tap → Run workflow** and +pass the tag (e.g. `v1.0.3`). + +## Manual fallback + +If the workflow cannot run, bump and push by hand: + +```bash +tag=v1.0.3 +url="https://github.com/brightdigit/git-trees/archive/refs/tags/${tag}.tar.gz" +sha=$(curl -fsSL "$url" | shasum -a 256 | awk '{ print $1 }') + +# edit homebrew-tap/Formula/git-trees.rb — set url and sha256 together +ruby -c homebrew-tap/Formula/git-trees.rb +brew style homebrew-tap/Formula/git-trees.rb + +# optional: audit requires the formula to live in a tap checkout +git subrepo push homebrew-tap +brew untap brightdigit/tap 2>/dev/null +brew tap brightdigit/tap +brew audit --strict --formula brightdigit/tap/git-trees +``` + +## Verify with brew install + +```bash +brew untap brightdigit/tap 2>/dev/null # ensure a fresh clone +brew tap brightdigit/tap +brew install git-trees +git trees help +``` + +`git trees help` writes its usage to stderr and exits 0. Then leave the machine +as you found it if this was only a verification: + +```bash +brew uninstall git-trees +``` + +## Follow-up: shell completions + +Once shell completions ship (PR #51, targeted at v1.0.3), the completion files +are part of the release tarball and the formula's `install` block should install +them: + +```ruby +bash_completion.install "completions/git-trees.bash" +zsh_completion.install "completions/_git-trees" => "_git-trees" +``` + +Add those lines only in the formula revision whose `url` points at a tag that +actually contains `completions/` — referencing files missing from the tarball +breaks `brew install` outright. diff --git a/homebrew-tap/.gitrepo b/homebrew-tap/.gitrepo new file mode 100644 index 0000000..85c8894 --- /dev/null +++ b/homebrew-tap/.gitrepo @@ -0,0 +1,12 @@ +; DO NOT EDIT (unless you know what you are doing) +; +; This subdirectory is a git "subrepo", and this file is maintained by the +; git-subrepo command. See https://github.com/ingydotnet/git-subrepo#readme +; +[subrepo] + remote = https://github.com/brightdigit/homebrew-tap.git + branch = main + commit = 9c94a73ab51d66c9141a2b3d5bd99c69ac077f58 + method = merge + cmdver = 0.4.9 + parent = 08b6fff8c93b96cf8b676efbd33f00b5ec187277 diff --git a/homebrew-tap/Formula/git-trees.rb b/homebrew-tap/Formula/git-trees.rb new file mode 100644 index 0000000..85821e7 --- /dev/null +++ b/homebrew-tap/Formula/git-trees.rb @@ -0,0 +1,36 @@ +class GitTrees < Formula + desc "Git subcommand for managing a bare-repo plus worktrees layout" + homepage "https://github.com/brightdigit/git-trees" + url "https://github.com/brightdigit/git-trees/archive/refs/tags/v1.0.2.tar.gz" + sha256 "301a8ab3f3a860c8e3125a61d45527f7a2f70a881c41d3d2ea899d47d4b547ef" + license "MIT" + + def install + bin.install "git-trees" + # TREES_AGENTS_TEMPLATE defaults to ~/.config/git-trees/AGENTS.md, which a + # formula must not write. Stage the template in the prefix and let caveats + # tell the user how to put it in place. + pkgshare.install "AGENTS.md.template" + end + + def caveats + <<~EOS + `git trees init` and `git trees root --agents` seed an AGENTS.md at the + container root from TREES_AGENTS_TEMPLATE, which defaults to + ~/.config/git-trees/AGENTS.md. Formulae cannot write there, so copy the + bundled template yourself: + + mkdir -p ~/.config/git-trees + cp #{pkgshare}/AGENTS.md.template ~/.config/git-trees/AGENTS.md + + Or point TREES_AGENTS_TEMPLATE at the bundled copy instead: + + export TREES_AGENTS_TEMPLATE=#{pkgshare}/AGENTS.md.template + EOS + end + + test do + # `help` writes its usage to stderr and exits 0. + assert_match "usage: git trees", shell_output("#{bin}/git-trees help 2>&1") + end +end diff --git a/homebrew-tap/README.md b/homebrew-tap/README.md new file mode 100644 index 0000000..107d428 --- /dev/null +++ b/homebrew-tap/README.md @@ -0,0 +1,14 @@ +# brightdigit/homebrew-tap + +Homebrew tap for [BrightDigit](https://github.com/brightdigit) formulae. + +## Usage + +```bash +brew tap brightdigit/tap +brew install git-trees +``` + +This repository is maintained as a [git subrepo](https://github.com/ingydotnet/git-subrepo) +inside [brightdigit/git-trees](https://github.com/brightdigit/git-trees). Formula bumps +are pushed here automatically on each git-trees GitHub release.