Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
107 changes: 107 additions & 0 deletions .github/workflows/homebrew-tap.yml
Original file line number Diff line number Diff line change
@@ -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
16 changes: 16 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
105 changes: 105 additions & 0 deletions docs/RELEASING.md
Original file line number Diff line number Diff line change
@@ -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.
12 changes: 12 additions & 0 deletions homebrew-tap/.gitrepo
Original file line number Diff line number Diff line change
@@ -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
36 changes: 36 additions & 0 deletions homebrew-tap/Formula/git-trees.rb
Original file line number Diff line number Diff line change
@@ -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
14 changes: 14 additions & 0 deletions homebrew-tap/README.md
Original file line number Diff line number Diff line change
@@ -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.
Loading