From 7bdd1965ae5854630bc267f75ab512f86d5533d0 Mon Sep 17 00:00:00 2001 From: Claude Date: Thu, 21 May 2026 23:22:41 +0000 Subject: [PATCH] feat: add Homebrew distribution via GoReleaser - Add .goreleaser.yaml: builds cross-platform archives, generates checksums, commits Formula/tasklin.rb to main on each release - Replace manual release workflow with goreleaser/goreleaser-action@v6 - Add Formula/tasklin.rb Homebrew formula (auto-updated by GoReleaser) - Wire version/commit/buildDate into cmd.Execute so `tasklin --version` works (required by the Homebrew formula test block) - Update README with `brew tap` / `brew install` install instructions - Update CLAUDE.md with release and distribution conventions https://claude.ai/code/session_01J4tGzE5t3iE1CQmAJXgq2r --- .github/workflows/release.yml | 33 +++++------------ .goreleaser.yaml | 70 +++++++++++++++++++++++++++++++++++ CLAUDE.md | 21 +++++++++++ Formula/tasklin.rb | 43 +++++++++++++++++++++ README.md | 9 ++++- cmd/root.go | 7 +++- main.go | 8 +++- 7 files changed, 165 insertions(+), 26 deletions(-) create mode 100644 .goreleaser.yaml create mode 100644 Formula/tasklin.rb diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index e7e3239..e19c13f 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -6,17 +6,17 @@ on: - 'v*.*.*' permissions: - contents: write # needed to create releases and upload assets + contents: write # needed to create releases, upload assets, and commit formula jobs: release: - name: Build & Release + name: GoReleaser runs-on: ubuntu-latest steps: - uses: actions/checkout@v4 with: - fetch-depth: 0 # full history so `git describe` resolves the tag + fetch-depth: 0 # full history so git describe resolves the tag - uses: actions/setup-go@v5 with: @@ -26,24 +26,11 @@ jobs: - name: Run tests run: make test-ci - - name: Build all binaries - run: make build-all - - - name: Generate checksums - run: | - cd bin - sha256sum tasklin-* > checksums.txt - echo "--- checksums ---" - cat checksums.txt - - - name: Create GitHub release and upload assets - uses: softprops/action-gh-release@v2 + - name: Run GoReleaser + uses: goreleaser/goreleaser-action@v6 with: - files: | - bin/tasklin-linux-amd64 - bin/tasklin-linux-arm64 - bin/tasklin-darwin-amd64 - bin/tasklin-darwin-arm64 - bin/tasklin-windows-amd64.exe - bin/checksums.txt - generate_release_notes: true + distribution: goreleaser + version: latest + args: release --clean + env: + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} diff --git a/.goreleaser.yaml b/.goreleaser.yaml new file mode 100644 index 0000000..6f47396 --- /dev/null +++ b/.goreleaser.yaml @@ -0,0 +1,70 @@ +version: 2 + +project_name: tasklin + +before: + hooks: + - go mod tidy + +builds: + - env: + - CGO_ENABLED=0 + goos: + - linux + - darwin + - windows + goarch: + - amd64 + - arm64 + ignore: + - goos: windows + goarch: arm64 + flags: + - -trimpath + ldflags: + - -s -w + - -X main.version={{.Version}} + - -X main.commit={{.Commit}} + - -X main.buildDate={{.Date}} + +archives: + - id: default + format: tar.gz + name_template: "{{ .ProjectName }}_{{ .Os }}_{{ .Arch }}" + format_overrides: + - goos: windows + format: zip + files: + - LICENSE + - README.md + +checksum: + name_template: checksums.txt + algorithm: sha256 + +brews: + - name: tasklin + homepage: "https://github.com/yamidaisuke/tasklin" + description: "Keyboard-driven CLI/TUI kanban board for personal project backlogs" + license: "MIT" + repository: + owner: yamidaisuke + name: tasklin + branch: main + token: "{{ .Env.GITHUB_TOKEN }}" + commit_author: + name: goreleaserbot + email: bot@goreleaser.com + commit_msg_template: "chore: update Homebrew formula to {{ .Tag }}" + directory: Formula + install: | + bin.install "tasklin" + test: | + system "#{bin}/tasklin", "--version" + +release: + github: + owner: yamidaisuke + name: tasklin + draft: false + generate_release_notes: true diff --git a/CLAUDE.md b/CLAUDE.md index b028404..2555f2f 100644 --- a/CLAUDE.md +++ b/CLAUDE.md @@ -22,6 +22,27 @@ make build-all # cross-compile for all platforms Never use `go build .` directly — always use `make build` so ldflags (version, commit, buildDate) are injected correctly. +## Release & distribution + +Releases are driven by GoReleaser (`.goreleaser.yaml`) via the `.github/workflows/release.yml` workflow. Push a `v*.*.*` tag to trigger it. + +GoReleaser: +- Builds cross-platform binaries (darwin/amd64, darwin/arm64, linux/amd64, linux/arm64, windows/amd64) +- Creates `.tar.gz` / `.zip` archives +- Generates `checksums.txt` (SHA-256) +- Creates the GitHub release +- Commits an updated `Formula/tasklin.rb` to `main` so the Homebrew tap is always current + +Users install via Homebrew: +```sh +brew tap yamidaisuke/tasklin https://github.com/yamidaisuke/tasklin +brew install tasklin +``` + +`Formula/tasklin.rb` is auto-managed by GoReleaser — do not edit it by hand. + +Version info is injected at link time via ldflags into `main.version`, `main.commit`, and `main.buildDate`. `cmd.Execute` accepts these as parameters and sets `rootCmd.Version` so `tasklin --version` works. + ## Architecture ``` diff --git a/Formula/tasklin.rb b/Formula/tasklin.rb new file mode 100644 index 0000000..9e948d2 --- /dev/null +++ b/Formula/tasklin.rb @@ -0,0 +1,43 @@ +# This file is managed by GoReleaser and updated automatically on each release. +# Do not edit by hand. +# +# Installation: +# brew tap yamidaisuke/tasklin https://github.com/yamidaisuke/tasklin +# brew install tasklin +class Tasklin < Formula + desc "Keyboard-driven CLI/TUI kanban board for personal project backlogs" + homepage "https://github.com/yamidaisuke/tasklin" + license "MIT" + + on_macos do + on_arm do + url "https://github.com/yamidaisuke/tasklin/releases/download/v0.0.0/tasklin_darwin_arm64.tar.gz" + sha256 "0000000000000000000000000000000000000000000000000000000000000000" + end + + on_intel do + url "https://github.com/yamidaisuke/tasklin/releases/download/v0.0.0/tasklin_darwin_amd64.tar.gz" + sha256 "0000000000000000000000000000000000000000000000000000000000000000" + end + end + + on_linux do + on_arm do + url "https://github.com/yamidaisuke/tasklin/releases/download/v0.0.0/tasklin_linux_arm64.tar.gz" + sha256 "0000000000000000000000000000000000000000000000000000000000000000" + end + + on_intel do + url "https://github.com/yamidaisuke/tasklin/releases/download/v0.0.0/tasklin_linux_amd64.tar.gz" + sha256 "0000000000000000000000000000000000000000000000000000000000000000" + end + end + + def install + bin.install "tasklin" + end + + test do + system "#{bin}/tasklin", "--version" + end +end diff --git a/README.md b/README.md index fb6e8d7..7d6ff0b 100644 --- a/README.md +++ b/README.md @@ -20,12 +20,19 @@ A lightweight, portable CLI tool for managing personal project backlogs. Data is ## Requirements -- Go 1.24+ +- Go 1.24+ (source installs only) --- ## Installation +### Homebrew (macOS and Linux) + +```sh +brew tap yamidaisuke/tasklin https://github.com/yamidaisuke/tasklin +brew install tasklin +``` + ### From source ```sh diff --git a/cmd/root.go b/cmd/root.go index 13a769c..f3a067e 100644 --- a/cmd/root.go +++ b/cmd/root.go @@ -18,6 +18,10 @@ var rootCmd = &cobra.Command{ RunE: runRoot, } +func initVersion(version, commit, buildDate string) { + rootCmd.Version = fmt.Sprintf("%s (commit: %s, built: %s)", version, commit, buildDate) +} + func runRoot(cmd *cobra.Command, args []string) error { cwd, err := os.Getwd() if err != nil { @@ -48,7 +52,8 @@ func runRoot(cmd *cobra.Command, args []string) error { } // Execute runs the root command. -func Execute() { +func Execute(version, commit, buildDate string) { + initVersion(version, commit, buildDate) if err := rootCmd.Execute(); err != nil { fmt.Fprintln(os.Stderr, err) os.Exit(1) diff --git a/main.go b/main.go index de3e342..aa826b8 100644 --- a/main.go +++ b/main.go @@ -2,6 +2,12 @@ package main import "github.com/frankcruz/tasklin/cmd" +var ( + version = "dev" + commit = "unknown" + buildDate = "unknown" +) + func main() { - cmd.Execute() + cmd.Execute(version, commit, buildDate) }