Skip to content

Commit 5900c22

Browse files
joelhelblingclaude
andcommitted
feat(release): add GoReleaser-based Homebrew release pipeline
Mirror kkullm's release setup: tag-triggered GoReleaser builds linux/darwin x amd64/arm64 binaries, publishes a GitHub release, and updates the shared joelhelbling/homebrew-tap cask, enabling `brew install joelhelbling/tap/tabb`. - .goreleaser.yaml: build matrix, archives, checksums, homebrew cask - .github/workflows/release.yaml: tag-triggered GoReleaser - .github/workflows/ci.yml: build/test/vet/gofmt on PR + main - cmd/tabb/version.go + main.go: version embedding via ldflags and `tabb version` command - install.sh: curl-pipe installer with checksum verification - LICENSE: MIT - Makefile: version ldflags + release-snapshot target - README: Homebrew / install.sh / go install instructions - gofmt alignment fix in internal/protocol/messages.go Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
1 parent f648897 commit 5900c22

10 files changed

Lines changed: 291 additions & 14 deletions

File tree

.github/workflows/ci.yml

Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,37 @@
1+
name: CI
2+
3+
on:
4+
pull_request:
5+
push:
6+
branches:
7+
- main
8+
9+
jobs:
10+
build-test:
11+
runs-on: ubuntu-latest
12+
steps:
13+
- name: Checkout
14+
uses: actions/checkout@v4
15+
16+
- name: Set up Go
17+
uses: actions/setup-go@v5
18+
with:
19+
go-version: '1.26.1'
20+
21+
- name: Build
22+
run: go build ./...
23+
24+
- name: Test
25+
run: go test ./...
26+
27+
- name: Vet
28+
run: go vet ./...
29+
30+
- name: Format check
31+
run: |
32+
fmt=$(gofmt -l .)
33+
if [ -n "$fmt" ]; then
34+
echo "Unformatted files:"
35+
echo "$fmt"
36+
exit 1
37+
fi

.github/workflows/release.yaml

Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,33 @@
1+
name: Release
2+
3+
on:
4+
push:
5+
tags:
6+
- 'v*'
7+
8+
permissions:
9+
contents: write
10+
11+
jobs:
12+
goreleaser:
13+
runs-on: ubuntu-latest
14+
steps:
15+
- name: Checkout
16+
uses: actions/checkout@v4
17+
with:
18+
fetch-depth: 0
19+
20+
- name: Set up Go
21+
uses: actions/setup-go@v5
22+
with:
23+
go-version: '1.26.1'
24+
25+
- name: Run GoReleaser
26+
uses: goreleaser/goreleaser-action@v6
27+
with:
28+
distribution: goreleaser
29+
version: '~> v2'
30+
args: release --clean
31+
env:
32+
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
33+
HOMEBREW_TAP_GITHUB_TOKEN: ${{ secrets.HOMEBREW_TAP_GITHUB_TOKEN }}

.goreleaser.yaml

Lines changed: 83 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,83 @@
1+
# yaml-language-server: $schema=https://goreleaser.com/static/schema.json
2+
version: 2
3+
4+
before:
5+
hooks:
6+
- go mod tidy
7+
8+
builds:
9+
- id: tabb
10+
main: ./cmd/tabb
11+
binary: tabb
12+
env:
13+
- CGO_ENABLED=0
14+
goos:
15+
- linux
16+
- darwin
17+
goarch:
18+
- amd64
19+
- arm64
20+
ldflags:
21+
- -s -w
22+
- -X main.Version={{.Version}}
23+
24+
archives:
25+
- id: tabb
26+
formats:
27+
- tar.gz
28+
name_template: >-
29+
{{ .ProjectName }}_
30+
{{- .Version }}_
31+
{{- .Os }}_
32+
{{- .Arch }}
33+
files:
34+
- README.md
35+
- LICENSE*
36+
37+
checksum:
38+
name_template: 'checksums.txt'
39+
40+
snapshot:
41+
version_template: "{{ incpatch .Version }}-next"
42+
43+
changelog:
44+
sort: asc
45+
filters:
46+
exclude:
47+
- '^docs:'
48+
- '^test:'
49+
- '^ci:'
50+
- Merge pull request
51+
- Merge branch
52+
53+
homebrew_casks:
54+
- name: tabb
55+
ids:
56+
- tabb
57+
binaries:
58+
- tabb
59+
homepage: "https://github.com/joelhelbling/tabb"
60+
description: "Manage Chrome browser tabs from the terminal or an AI assistant"
61+
repository:
62+
owner: joelhelbling
63+
name: homebrew-tap
64+
branch: main
65+
token: "{{ .Env.HOMEBREW_TAP_GITHUB_TOKEN }}"
66+
commit_author:
67+
name: goreleaserbot
68+
email: bot@goreleaser.com
69+
commit_msg_template: "Brew cask update for {{ .ProjectName }} version {{ .Tag }}"
70+
hooks:
71+
post:
72+
install: |
73+
if OS.mac?
74+
system_command "/usr/bin/xattr", args: ["-dr", "com.apple.quarantine", "#{staged_path}/tabb"]
75+
end
76+
77+
release:
78+
github:
79+
owner: joelhelbling
80+
name: tabb
81+
draft: false
82+
prerelease: auto
83+
name_template: "{{.Tag}}"

LICENSE

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
MIT License
2+
3+
Copyright (c) 2026 Joel Helbling
4+
5+
Permission is hereby granted, free of charge, to any person obtaining a copy
6+
of this software and associated documentation files (the "Software"), to deal
7+
in the Software without restriction, including without limitation the rights
8+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9+
copies of the Software, and to permit persons to whom the Software is
10+
furnished to do so, subject to the following conditions:
11+
12+
The above copyright notice and this permission notice shall be included in all
13+
copies or substantial portions of the Software.
14+
15+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21+
SOFTWARE.

Makefile

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2,10 +2,10 @@ BINARY := tabb
22
PKG := ./cmd/tabb
33
PREFIX ?= /usr/local
44

5-
.PHONY: build install uninstall test clean
5+
.PHONY: build install uninstall test clean release-snapshot
66

77
build:
8-
go build -o $(BINARY) $(PKG)
8+
go build -ldflags "-X main.Version=dev" -o $(BINARY) $(PKG)
99

1010
install: build
1111
install -d $(PREFIX)/bin
@@ -19,3 +19,6 @@ test:
1919

2020
clean:
2121
rm -f $(BINARY)
22+
23+
release-snapshot:
24+
goreleaser release --snapshot --clean

README.md

Lines changed: 16 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -17,13 +17,27 @@ A thin Chrome extension talks to a Go binary via Chrome's Native Messaging proto
1717

1818
## Install
1919

20-
### 1. Build the binary
20+
### 1. Install the binary
21+
22+
**Homebrew** (macOS / Linux):
23+
24+
```bash
25+
brew install joelhelbling/tap/tabb
26+
```
27+
28+
**Install script** (downloads a release archive, verifies its checksum, installs to `~/.local/bin`):
29+
30+
```bash
31+
curl -fsSL https://raw.githubusercontent.com/joelhelbling/tabb/main/install.sh | sh
32+
```
33+
34+
**From source** (requires Go):
2135

2236
```bash
2337
go install github.com/joelhelbling/tabb/cmd/tabb@latest
2438
```
2539

26-
Or build from source:
40+
Or clone and build:
2741

2842
```bash
2943
git clone https://github.com/joelhelbling/tabb.git

cmd/tabb/main.go

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -42,6 +42,8 @@ func main() {
4242
err = runProfiles()
4343
case "setup":
4444
err = runSetup()
45+
case "version", "--version":
46+
fmt.Println(Version)
4547
case "help", "--help", "-h":
4648
printUsage()
4749
default:
@@ -87,6 +89,7 @@ Usage:
8789
tabb profiles List configured profiles
8890
tabb mcp Run as MCP stdio server
8991
tabb setup Install Native Messaging host manifest
92+
tabb version Print the tabb version
9093
tabb help Show this help
9194
9295
Environment:

cmd/tabb/version.go

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
package main
2+
3+
// Version is the build version, overridden at release time via ldflags:
4+
//
5+
// -X main.Version={{.Version}}
6+
var Version = "dev"

install.sh

Lines changed: 77 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,77 @@
1+
#!/bin/sh
2+
# Install tabb from GitHub releases.
3+
# Usage: curl -fsSL https://raw.githubusercontent.com/joelhelbling/tabb/main/install.sh | sh
4+
# Env:
5+
# TABB_VERSION pin a version (e.g. v0.1.0); default: latest
6+
# INSTALL_DIR install location; default: ~/.local/bin
7+
set -eu
8+
9+
REPO="joelhelbling/tabb"
10+
INSTALL_DIR="${INSTALL_DIR:-$HOME/.local/bin}"
11+
12+
# --- detect OS ---
13+
os="$(uname -s)"
14+
case "$os" in
15+
Linux) os="linux" ;;
16+
Darwin) os="darwin" ;;
17+
*) echo "tabb: unsupported OS: $os" >&2; exit 1 ;;
18+
esac
19+
20+
# --- detect arch ---
21+
arch="$(uname -m)"
22+
case "$arch" in
23+
x86_64|amd64) arch="amd64" ;;
24+
arm64|aarch64) arch="arm64" ;;
25+
*) echo "tabb: unsupported architecture: $arch" >&2; exit 1 ;;
26+
esac
27+
28+
# --- resolve version ---
29+
version="${TABB_VERSION:-}"
30+
if [ -z "$version" ]; then
31+
version="$(curl -fsSL "https://api.github.com/repos/$REPO/releases/latest" \
32+
| grep '"tag_name":' | head -n1 | sed -E 's/.*"tag_name": *"([^"]+)".*/\1/')"
33+
fi
34+
if [ -z "$version" ]; then
35+
echo "tabb: could not resolve release version" >&2
36+
exit 1
37+
fi
38+
39+
# goreleaser strips the leading 'v' from the version in archive names.
40+
ver_no_v="${version#v}"
41+
archive="tabb_${ver_no_v}_${os}_${arch}.tar.gz"
42+
base="https://github.com/$REPO/releases/download/$version"
43+
44+
tmp="$(mktemp -d)"
45+
trap 'rm -rf "$tmp"' EXIT
46+
47+
echo "tabb: downloading $archive ($version)"
48+
curl -fsSL "$base/$archive" -o "$tmp/$archive"
49+
curl -fsSL "$base/checksums.txt" -o "$tmp/checksums.txt"
50+
51+
# --- verify checksum ---
52+
echo "tabb: verifying checksum"
53+
expected="$(grep " $archive\$" "$tmp/checksums.txt" | awk '{print $1}')"
54+
if [ -z "$expected" ]; then
55+
echo "tabb: no checksum found for $archive" >&2
56+
exit 1
57+
fi
58+
if command -v sha256sum >/dev/null 2>&1; then
59+
actual="$(sha256sum "$tmp/$archive" | awk '{print $1}')"
60+
else
61+
actual="$(shasum -a 256 "$tmp/$archive" | awk '{print $1}')"
62+
fi
63+
if [ "$expected" != "$actual" ]; then
64+
echo "tabb: checksum mismatch (expected $expected, got $actual)" >&2
65+
exit 1
66+
fi
67+
68+
# --- extract and install ---
69+
tar -xzf "$tmp/$archive" -C "$tmp"
70+
mkdir -p "$INSTALL_DIR"
71+
install -m 0755 "$tmp/tabb" "$INSTALL_DIR/tabb"
72+
73+
echo "tabb: installed $version to $INSTALL_DIR/tabb"
74+
case ":$PATH:" in
75+
*":$INSTALL_DIR:"*) ;;
76+
*) echo "tabb: note — $INSTALL_DIR is not on your PATH" >&2 ;;
77+
esac

internal/protocol/messages.go

Lines changed: 10 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -20,17 +20,17 @@ type Response struct {
2020

2121
// Tab represents metadata for a single browser tab.
2222
type Tab struct {
23-
ID int `json:"id"`
24-
WindowID int `json:"windowId"`
25-
Title string `json:"title"`
26-
URL string `json:"url"`
27-
Status string `json:"status"`
28-
Active bool `json:"active"`
29-
Pinned bool `json:"pinned"`
30-
Audible bool `json:"audible"`
31-
Discarded bool `json:"discarded"`
23+
ID int `json:"id"`
24+
WindowID int `json:"windowId"`
25+
Title string `json:"title"`
26+
URL string `json:"url"`
27+
Status string `json:"status"`
28+
Active bool `json:"active"`
29+
Pinned bool `json:"pinned"`
30+
Audible bool `json:"audible"`
31+
Discarded bool `json:"discarded"`
3232
FavIconURL string `json:"favIconUrl,omitempty"`
33-
Index int `json:"index"`
33+
Index int `json:"index"`
3434
}
3535

3636
// TabContent represents the full content of a tab, returned by show_tab.

0 commit comments

Comments
 (0)