Skip to content

Commit 70d706a

Browse files
authored
feat(release): add GoReleaser and Homebrew publishing automation (#19)
* feat(release): add GoReleaser and Homebrew publishing automation This commit adds automated Homebrew publishing using GoReleaser. The workflow maintains the existing GitHub Release process while automating binary builds and Homebrew formula publishing. Changes: - Add .goreleaser.yaml with Homebrew tap configuration and release mode set to 'keep-existing' to preserve manually created release notes - Add .github/workflows/release.yml for automated releases triggered by version tags - Update Makefile with release-test and release-clean targets - Update CLAUDE.md with comprehensive release process documentation including PAT setup - Update planning/use-goreleaser-for-brew-publish.md with implementation details Workflow: 1. Create GitHub Release first using 'gh release create' with detailed notes 2. GoReleaser automatically adds binaries to the release 3. Homebrew formula is published to erraggy/homebrew-oastools Prerequisites: - HOMEBREW_TAP_TOKEN secret configured in repository (required for cross-repo publishing) - Commit author email in .goreleaser.yaml matches verified GitHub email * fix(release): add architecture specification to GoReleaser config Address critical issue found in PR review: GoReleaser builds section was missing goarch specification, which would cause it to build only for the host architecture. Changes: - Add goarch specification with amd64, arm64, and 386 support - Add ignore rules to exclude unsupported combinations (darwin/386, windows/arm64) This ensures cross-platform builds for: - Linux: amd64, arm64, 386 - macOS: amd64, arm64 - Windows: amd64, 386 Resolves critical issue in PR #19
1 parent fe38724 commit 70d706a

5 files changed

Lines changed: 595 additions & 85 deletions

File tree

.github/workflows/release.yml

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+
# This is required for GoReleaser to push to the homebrew-oastools tap
11+
12+
jobs:
13+
goreleaser:
14+
runs-on: ubuntu-latest
15+
steps:
16+
- name: Checkout
17+
uses: actions/checkout@v5
18+
with:
19+
fetch-depth: 0
20+
21+
- name: Set up Go
22+
uses: actions/setup-go@v6
23+
with:
24+
go-version: '1.24'
25+
26+
- name: Run GoReleaser
27+
uses: goreleaser/goreleaser-action@v6
28+
with:
29+
distribution: goreleaser
30+
version: '~> v2'
31+
args: release --clean
32+
env:
33+
GITHUB_TOKEN: ${{ secrets.HOMEBREW_TAP_TOKEN }}

.goreleaser.yaml

Lines changed: 76 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,76 @@
1+
# This is an example .goreleaser.yml file with some sensible defaults.
2+
# Make sure to check the documentation at https://goreleaser.com
3+
4+
# The lines below are called `modelines`. See `:help modeline`
5+
# Feel free to remove those if you don't want/need to use them.
6+
# yaml-language-server: $schema=https://goreleaser.com/static/schema.json
7+
# vim: set ts=2 sw=2 tw=0 fo=cnqoj
8+
9+
version: 2
10+
11+
before:
12+
hooks:
13+
# You may remove this if you don't use go modules.
14+
- go mod tidy
15+
16+
builds:
17+
- main: ./cmd/oastools
18+
binary: oastools
19+
env:
20+
- CGO_ENABLED=0
21+
goos:
22+
- linux
23+
- windows
24+
- darwin
25+
goarch:
26+
- amd64
27+
- arm64
28+
- "386"
29+
ignore:
30+
- goos: darwin
31+
goarch: "386"
32+
- goos: windows
33+
goarch: arm64
34+
35+
archives:
36+
- formats: [tar.gz]
37+
# this name template makes the OS and Arch compatible with the results of `uname`.
38+
name_template: >-
39+
{{ .ProjectName }}_
40+
{{- title .Os }}_
41+
{{- if eq .Arch "amd64" }}x86_64
42+
{{- else if eq .Arch "386" }}i386
43+
{{- else }}{{ .Arch }}{{ end }}
44+
{{- if .Arm }}v{{ .Arm }}{{ end }}
45+
# use zip for windows archives
46+
format_overrides:
47+
- goos: windows
48+
formats: [zip]
49+
50+
brews:
51+
- name: oastools
52+
repository:
53+
owner: erraggy
54+
name: homebrew-oastools
55+
commit_author:
56+
name: Robbie Coleman
57+
email: robbie@robnrob.com
58+
homepage: "https://github.com/erraggy/oastools"
59+
description: "OpenAPI Specification (OAS) tools for validation, parsing, converting, and joining specs."
60+
license: "MIT"
61+
62+
changelog:
63+
sort: asc
64+
filters:
65+
exclude:
66+
- "^docs:"
67+
- "^test:"
68+
69+
release:
70+
# Use existing release created by gh release create
71+
# This allows you to write detailed release notes first
72+
mode: keep-existing
73+
# Disable draft mode - use the release as-is
74+
draft: false
75+
# Don't replace the release notes you created
76+
replace_existing_draft: false

CLAUDE.md

Lines changed: 147 additions & 47 deletions
Original file line numberDiff line numberDiff line change
@@ -483,7 +483,18 @@ func TestConverterConvert(t *testing.T) { ... }
483483

484484
### Creating a new release
485485

486+
**Release Infrastructure:**
487+
488+
This repository uses **GoReleaser** with **GitHub Actions** to automate binary builds and Homebrew publishing.
489+
The recommended workflow is:
490+
491+
1. Create a GitHub Release with detailed notes using `gh release create`
492+
2. GoReleaser automatically triggers, adds binaries to your release, and publishes to Homebrew
493+
494+
This approach maintains full control over release notes while automating the build and distribution process.
495+
486496
**Prerequisites:**
497+
487498
1. Ensure you are on the `main` branch
488499
2. Ensure your local `main` is up-to-date with `origin/main`
489500
3. Verify all tests pass: `make check`
@@ -506,14 +517,88 @@ The version tag follows the format `vMAJOR.MINOR.PATCH` (e.g., `v1.7.0`):
506517
- Use when: Removing/changing public APIs, incompatible changes
507518
- Note: Major version bumps require careful planning and are extremely rare
508519

509-
**Release Process:**
520+
**GitHub Personal Access Token (PAT) Setup:**
521+
522+
**REQUIRED:** The automated release workflow needs a Personal Access Token to push to the Homebrew tap repository.
523+
524+
The default `GITHUB_TOKEN` provided by GitHub Actions only has permissions for the current repository and
525+
**cannot** push to the separate `homebrew-oastools` repository. You must create a PAT with `repo` scope.
526+
527+
**For Automated Releases (GitHub Actions) - REQUIRED SETUP:**
528+
529+
1. Go to GitHub Settings → Developer settings → Personal access tokens → Tokens (classic)
530+
2. Click "Generate new token (classic)"
531+
3. Set a descriptive name (e.g., "GoReleaser - oastools Homebrew Publishing")
532+
4. Select scopes: **`repo`** (full control of private repositories)
533+
5. Click "Generate token" and **copy the token immediately** (you won't be able to see it again)
534+
6. Go to the oastools repository → Settings → Secrets and variables → Actions
535+
7. Click "New repository secret"
536+
8. Name: `HOMEBREW_TAP_TOKEN`
537+
9. Value: Paste the token you copied
538+
10. Click "Add secret"
539+
540+
The `.github/workflows/release.yml` file is already configured to use `${{ secrets.HOMEBREW_TAP_TOKEN }}`.
541+
542+
**Verify PAT Setup:**
543+
544+
Before creating your first release, verify the PAT secret is configured:
545+
```bash
546+
gh secret list --repo erraggy/oastools
547+
```
548+
549+
You should see `HOMEBREW_TAP_TOKEN` in the list.
550+
551+
**For Local Releases:**
552+
553+
If you prefer to run releases locally (not recommended), you'll need to use the same PAT:
554+
555+
1. Create a PAT as described above (or use the same one)
556+
2. Export it in your shell:
557+
```bash
558+
export GITHUB_TOKEN="ghp_your_token_here"
559+
```
560+
3. Use the `make release-test` target to test locally before running a real release
561+
562+
**IMPORTANT:** Never commit the PAT to git. Keep it secure in GitHub Secrets or your local environment only.
563+
564+
**Release Make Targets:**
565+
566+
The following make targets are available to help with testing:
567+
568+
```bash
569+
# Test the GoReleaser configuration locally (no publishing)
570+
make release-test
571+
572+
# Clean up local release artifacts
573+
make release-clean
574+
```
575+
576+
**Pre-Release Checklist:**
577+
578+
Before creating your first release, ensure:
579+
- [ ] `HOMEBREW_TAP_TOKEN` secret is created and added to repository secrets
580+
- [ ] Secret verified with: `gh secret list --repo erraggy/oastools`
581+
- [ ] Local test successful: `make release-test`
582+
- [ ] Commit author email in `.goreleaser.yaml` matches a verified email in your GitHub account
583+
- [ ] All changes committed and pushed to `main`
584+
- [ ] All tests pass: `make check`
585+
586+
**Release Process (Recommended Workflow):**
510587

511-
1. **Determine the version number** based on the changes included (see rules above)
588+
This workflow creates the GitHub Release first with detailed notes, then GoReleaser adds binaries and publishes to Homebrew.
512589

513-
2. **Create the release using `gh release create`:**
590+
1. **Determine the version number** based on the changes included (see SemVer rules above)
591+
592+
2. **Test the release configuration locally** (optional but recommended):
514593
```bash
515-
gh release create v1.7.0 \
516-
--title "v1.7.0 - Brief summary within 72 chars" \
594+
make release-test
595+
```
596+
This runs GoReleaser in snapshot mode to verify everything builds correctly without publishing.
597+
598+
3. **Create the GitHub Release with detailed notes**:
599+
```bash
600+
gh release create v1.7.1 \
601+
--title "v1.7.1 - Brief summary within 72 chars" \
517602
--notes "$(cat <<'EOF'
518603
## Summary
519604
@@ -538,55 +623,70 @@ The version tag follows the format `vMAJOR.MINOR.PATCH` (e.g., `v1.7.0`):
538623
539624
- #17 - PR title
540625
- #18 - PR title
541-
EOF
542-
)"
543-
```
544-
545-
3. **Title format**: `vX.Y.Z - Brief summary` (keep within 72 characters total)
546-
- Example: `v1.7.0 - Performance improvements and benchmark suite`
547-
548-
4. **Body format**: Well-formatted markdown with:
549-
- **Summary**: High-level overview of the release
550-
- **What's New**: Bullet points of new features/improvements
551-
- **Changes**: Notable changes or fixes
552-
- **Technical Details**: Benchmarks, metrics, migration notes (if applicable)
553-
- **Related PRs**: Links to merged pull requests
554626
555-
**Example Release Command:**
627+
## Installation
556628
557-
```bash
558-
gh release create v1.7.0 \
559-
--title "v1.7.0 - Performance improvements and benchmark suite" \
560-
--notes "$(cat <<'EOF'
561-
## Summary
562-
563-
This release introduces comprehensive performance benchmarking infrastructure
564-
and delivers significant JSON marshaling performance improvements.
565-
566-
## What's New
567-
568-
- Comprehensive benchmark suite (60+ benchmarks across all packages)
569-
- JSON marshaler optimization (25-32% faster, 29-37% fewer allocations)
570-
- Performance improvement planning documentation
571-
572-
## Performance Improvements
573-
574-
- Info marshaling: 26% faster (2,323ns → 1,707ns)
575-
- Contact marshaling: 32% faster (2,336ns → 1,599ns)
576-
- Server marshaling: 25% faster (2,837ns → 2,160ns)
629+
### Homebrew
630+
```bash
631+
brew tap erraggy/oastools
632+
brew install oastools
633+
```
577634
578-
## Related PRs
635+
### Binary Download
636+
Download the appropriate binary for your platform from the assets below.
637+
EOF
638+
)"
639+
```
579640

580-
- #17 - Phase 1: Benchmark infrastructure and baseline
581-
- #18 - Phase 2: JSON marshaler optimization
582-
EOF
583-
)"
584-
```
641+
**Important:** The `gh release create` command automatically:
642+
- Creates the version tag (e.g., `v1.7.1`)
643+
- Creates the GitHub Release with your detailed notes
644+
- Triggers the GitHub Actions workflow
645+
646+
4. **Monitor the automated build**:
647+
The GitHub Actions workflow will automatically:
648+
- Build binaries for all platforms (Linux, macOS, Windows)
649+
- Add binary archives to your GitHub Release
650+
- Publish the Homebrew formula to `homebrew-oastools`
651+
652+
Monitor progress at:
653+
- Workflow: https://github.com/erraggy/oastools/actions
654+
- Release: https://github.com/erraggy/oastools/releases
655+
- Homebrew tap: https://github.com/erraggy/homebrew-oastools
656+
657+
5. **Verify the release**:
658+
- Check that binaries were added to the GitHub Release
659+
- Verify Homebrew formula was updated in `homebrew-oastools`
660+
- Test Homebrew installation (optional):
661+
```bash
662+
brew tap erraggy/oastools
663+
brew install oastools
664+
oastools --version
665+
```
585666

586667
**After Release:**
668+
587669
- Verify the release appears correctly on GitHub
588-
- The tag is automatically created by `gh release create`
589-
- GitHub Actions will run (if configured) to build/publish artifacts
670+
- Test Homebrew installation: `brew tap erraggy/oastools && brew install oastools`
671+
- Announce the release (if applicable)
672+
- Update project documentation if needed
673+
674+
**Troubleshooting:**
675+
676+
**Issue: GoReleaser can't push to homebrew-oastools**
677+
- Verify the `homebrew-oastools` repository exists and is public
678+
- Check that the GitHub token has `repo` scope
679+
- Ensure the commit author email in `.goreleaser.yaml` matches a verified email in your GitHub account
680+
681+
**Issue: Build fails for certain platforms**
682+
- Review the GitHub Actions logs for specific error messages
683+
- Check if any dependencies require CGO (we've set `CGO_ENABLED=0`)
684+
- Test locally with `make release-test` to reproduce the issue
685+
686+
**Issue: Homebrew formula doesn't work**
687+
- Verify the formula in `homebrew-oastools` repository
688+
- Test installation in a clean environment
689+
- Check binary architecture matches the user's system
590690

591691
## Go Module
592692

Makefile

Lines changed: 24 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
.PHONY: build test lint clean install tidy check help bench bench-parser bench-validator bench-converter bench-joiner bench-save bench-compare bench-baseline bench-clean
1+
.PHONY: build test lint clean install tidy check help bench bench-parser bench-validator bench-converter bench-joiner bench-save bench-compare bench-baseline bench-clean release-test release-clean
22

33
# Build variables
44
BINARY_NAME=oastools
@@ -55,6 +55,7 @@ vet:
5555
clean:
5656
@echo "Cleaning..."
5757
@rm -rf $(BUILD_DIR)
58+
@rm -rf dist/
5859
@rm -f coverage.txt coverage.html
5960
@rm -f benchmark-*.txt
6061

@@ -179,6 +180,28 @@ bench-clean:
179180
@rm -f mem-profile-*.prof
180181
@echo "Benchmark outputs cleaned (baseline preserved)"
181182

183+
## release-test: Test GoReleaser configuration locally (creates dist/ without publishing)
184+
release-test:
185+
@echo "Testing GoReleaser configuration (snapshot mode)..."
186+
@if ! command -v goreleaser >/dev/null 2>&1; then \
187+
echo "Error: goreleaser not installed. Install it with:"; \
188+
echo " brew install goreleaser"; \
189+
exit 1; \
190+
fi
191+
@goreleaser release --snapshot --clean
192+
@echo ""
193+
@echo "Test successful! Check dist/ directory for generated artifacts."
194+
@echo "To clean up: make release-clean"
195+
@echo ""
196+
@echo "To create a real release, use:"
197+
@echo " gh release create vX.Y.Z --title \"vX.Y.Z - Description\" --notes \"...\""
198+
199+
## release-clean: Clean GoReleaser artifacts from local testing
200+
release-clean:
201+
@echo "Cleaning release artifacts..."
202+
@rm -rf dist/
203+
@echo "Release artifacts cleaned"
204+
182205
## help: Show this help message
183206
help:
184207
@echo "Usage: make [target]"

0 commit comments

Comments
 (0)