feat: add CLI release workflow and Homebrew support#122
Conversation
- Add cli-release.yml workflow triggered by v* tags - Build binaries for macOS (amd64, arm64) and Linux (amd64) - Create release.sh script for creating releases - Make version variable injectable via ldflags - Use strings.CutPrefix for cleaner code (modernize linting) The homebrew-tap repository has been created separately at livetemplate/homebrew-tap with the formula template. Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
Pull Request Review: CLI Release Workflow and Homebrew SupportThank you for this contribution! This is a well-structured PR that adds important release automation. StrengthsCode Quality
Best Practices
Critical Issues1. Go Version Mismatch (BLOCKING)Location: .github/workflows/cli-release.yml:18 The workflow uses GO_VERSION: 1.23, but go.mod specifies 1.25.3, and desktop-release.yml also uses 1.25.3. Fix: Change to GO_VERSION: 1.25.3 or use go-version-file: go.mod (like ci.yml does) 2. Missing GOWORK Environment VariableLocation: .github/workflows/cli-release.yml:59 Test plan mentions GOWORK=off but workflow does not set it. If go.work is added later, builds could fail. Fix: Add GOWORK: off to build environment variables Recommendations3. Missing Build VerificationAdd smoke test after building to verify binary works before release 4. Windows SupportDesktop workflow includes Windows but CLI does not. Is this intentional? 5. Release Script Race Conditionrelease.sh:75-81 allows proceeding even when out of sync with remote. Consider making this a hard failure with --force flag override. 6. Minor Improvements
SecurityGood:
Consider:
SummarySolid PR with valuable automation. Main blocking issue is Go version mismatch. Required Before Merge:
Scores:
|
There was a problem hiding this comment.
Pull request overview
This PR adds automated CLI release infrastructure to enable publishing tinkerdown binaries via GitHub releases and Homebrew installation. The changes implement a complete release pipeline including build automation, binary distribution, and version management.
Changes:
- Added GitHub Actions workflow (cli-release.yml) triggered by version tags to build multi-platform CLI binaries
- Created release.sh script with validation and safety checks for creating releases
- Refactored version handling from const to var for build-time injection via ldflags
- Improved argument parsing using strings.CutPrefix for cleaner code
Reviewed changes
Copilot reviewed 3 out of 3 changed files in this pull request and generated 9 comments.
| File | Description |
|---|---|
| .github/workflows/cli-release.yml | Implements automated build and release pipeline for macOS (amd64/arm64) and Linux (amd64) binaries with checksums |
| release.sh | Provides safe release creation script with version validation, git state checks, and user confirmations |
| cmd/tinkerdown/main.go | Updates version handling to support ldflags injection and refactors argument parsing to use strings.CutPrefix |
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
| inputs: | ||
| version: | ||
| description: 'Version (e.g., 1.0.0)' | ||
| required: true | ||
| type: string |
There was a problem hiding this comment.
The workflow_dispatch version input lacks validation. A user could potentially input a malformed or malicious version string that would be injected into the ldflags. Consider adding input validation to ensure the version follows semantic versioning format, similar to the validation in release.sh script.
.github/workflows/cli-release.yml
Outdated
| - name: Create archive | ||
| run: | | ||
| mkdir -p dist | ||
| tar -czvf dist/tinkerdown-${{ matrix.os }}-${{ matrix.arch }}.tar.gz tinkerdown |
There was a problem hiding this comment.
The tarball creation includes the -v flag for verbose output, which will clutter the workflow logs. Since the next step lists the files anyway with ls -la, consider removing the -v flag from tar to keep the logs cleaner.
| tar -czvf dist/tinkerdown-${{ matrix.os }}-${{ matrix.arch }}.tar.gz tinkerdown | |
| tar -czf dist/tinkerdown-${{ matrix.os }}-${{ matrix.arch }}.tar.gz tinkerdown |
.github/workflows/cli-release.yml
Outdated
| runner: macos-latest | ||
| - os: darwin | ||
| arch: arm64 | ||
| runner: macos-latest |
There was a problem hiding this comment.
When building for darwin/arm64 on macos-latest runners, the runner architecture might be either arm64 or amd64 depending on GitHub's runner allocation. Cross-compilation from amd64 to arm64 (or vice versa) should work fine with CGO_ENABLED=0, but consider using the more explicit macos-14 (arm64) and macos-13 (amd64) runner images if you want to ensure native builds for each architecture, which could improve build performance.
| runner: macos-latest | |
| - os: darwin | |
| arch: arm64 | |
| runner: macos-latest | |
| runner: macos-13 | |
| - os: darwin | |
| arch: arm64 | |
| runner: macos-14 |
| if [ "${{ github.event_name }}" = "workflow_dispatch" ]; then | ||
| echo "version=${{ inputs.version }}" >> $GITHUB_OUTPUT | ||
| else | ||
| echo "version=${GITHUB_REF_NAME#v}" >> $GITHUB_OUTPUT | ||
| fi |
There was a problem hiding this comment.
The workflow manually handles version extraction in two different places (build job line 46-53 and release job line 101-108) with identical logic. Consider extracting this to a separate job that outputs the version, or use a reusable workflow step, to follow the DRY principle and reduce the risk of inconsistency.
| if [ "${{ github.event_name }}" = "workflow_dispatch" ]; then | |
| echo "version=${{ inputs.version }}" >> $GITHUB_OUTPUT | |
| else | |
| echo "version=${GITHUB_REF_NAME#v}" >> $GITHUB_OUTPUT | |
| fi | |
| VERSION="${{ github.event_name == 'workflow_dispatch' && inputs.version || '' }}" | |
| if [ -z "$VERSION" ]; then | |
| VERSION="${GITHUB_REF_NAME#v}" | |
| fi | |
| echo "version=$VERSION" >> "$GITHUB_OUTPUT" |
.github/workflows/cli-release.yml
Outdated
| - name: Create checksums file | ||
| run: | | ||
| cd release | ||
| cat *.sha256 > checksums.txt |
There was a problem hiding this comment.
The checksums.txt file is created by concatenating all *.sha256 files with cat, which may result in checksums appearing in non-deterministic order depending on shell glob expansion. While this doesn't affect functionality, consider sorting the checksums for consistency across builds, which can be helpful for debugging and verification.
| cat *.sha256 > checksums.txt | |
| cat *.sha256 | sort > checksums.txt |
.github/workflows/cli-release.yml
Outdated
| curl -L https://github.com/livetemplate/tinkerdown/releases/download/v${{ steps.version.outputs.version }}/tinkerdown-darwin-arm64.tar.gz | tar xz | ||
| sudo mv tinkerdown /usr/local/bin/ | ||
|
|
||
| # macOS Intel | ||
| curl -L https://github.com/livetemplate/tinkerdown/releases/download/v${{ steps.version.outputs.version }}/tinkerdown-darwin-amd64.tar.gz | tar xz | ||
| sudo mv tinkerdown /usr/local/bin/ | ||
|
|
||
| # Linux | ||
| curl -L https://github.com/livetemplate/tinkerdown/releases/download/v${{ steps.version.outputs.version }}/tinkerdown-linux-amd64.tar.gz | tar xz | ||
| sudo mv tinkerdown /usr/local/bin/ |
There was a problem hiding this comment.
The manual installation instructions use sudo to move the binary to /usr/local/bin/, but piping curl output directly to tar and then moving could leave a binary in the current directory if the sudo command fails or is cancelled. Consider adding error handling or suggesting users verify the extraction succeeded before running sudo, or use a two-step process that extracts to a temporary directory first.
| curl -L https://github.com/livetemplate/tinkerdown/releases/download/v${{ steps.version.outputs.version }}/tinkerdown-darwin-arm64.tar.gz | tar xz | |
| sudo mv tinkerdown /usr/local/bin/ | |
| # macOS Intel | |
| curl -L https://github.com/livetemplate/tinkerdown/releases/download/v${{ steps.version.outputs.version }}/tinkerdown-darwin-amd64.tar.gz | tar xz | |
| sudo mv tinkerdown /usr/local/bin/ | |
| # Linux | |
| curl -L https://github.com/livetemplate/tinkerdown/releases/download/v${{ steps.version.outputs.version }}/tinkerdown-linux-amd64.tar.gz | tar xz | |
| sudo mv tinkerdown /usr/local/bin/ | |
| TMPDIR="$(mktemp -d)" | |
| curl -L "https://github.com/livetemplate/tinkerdown/releases/download/v${{ steps.version.outputs.version }}/tinkerdown-darwin-arm64.tar.gz" -o "$TMPDIR/tinkerdown.tar.gz" | |
| tar -xzf "$TMPDIR/tinkerdown.tar.gz" -C "$TMPDIR" | |
| sudo install -m 0755 "$TMPDIR/tinkerdown" /usr/local/bin/tinkerdown | |
| rm -rf "$TMPDIR" | |
| # macOS Intel | |
| TMPDIR="$(mktemp -d)" | |
| curl -L "https://github.com/livetemplate/tinkerdown/releases/download/v${{ steps.version.outputs.version }}/tinkerdown-darwin-amd64.tar.gz" -o "$TMPDIR/tinkerdown.tar.gz" | |
| tar -xzf "$TMPDIR/tinkerdown.tar.gz" -C "$TMPDIR" | |
| sudo install -m 0755 "$TMPDIR/tinkerdown" /usr/local/bin/tinkerdown | |
| rm -rf "$TMPDIR" | |
| # Linux | |
| TMPDIR="$(mktemp -d)" | |
| curl -L "https://github.com/livetemplate/tinkerdown/releases/download/v${{ steps.version.outputs.version }}/tinkerdown-linux-amd64.tar.gz" -o "$TMPDIR/tinkerdown.tar.gz" | |
| tar -xzf "$TMPDIR/tinkerdown.tar.gz" -C "$TMPDIR" | |
| sudo install -m 0755 "$TMPDIR/tinkerdown" /usr/local/bin/tinkerdown | |
| rm -rf "$TMPDIR" |
.github/workflows/cli-release.yml
Outdated
| contents: write | ||
|
|
||
| env: | ||
| GO_VERSION: '1.23' |
There was a problem hiding this comment.
The GO_VERSION is set to '1.23', but the go.mod file specifies '1.25.3' and the desktop-release.yml workflow uses '1.25.3'. This inconsistency could lead to build issues or unexpected behavior. Consider updating this to match the go.mod version for consistency across the project.
| GOARCH: ${{ matrix.arch }} | ||
| CGO_ENABLED: 0 | ||
| run: | | ||
| go build -ldflags="-s -w -X main.version=${{ steps.version.outputs.version }}" \ |
There was a problem hiding this comment.
The Makefile defines LDFLAGS with both main.version and main.buildTime variables, but the version command in main.go only uses the version variable. The buildTime variable is not defined or used anywhere in main.go. Consider either adding buildTime support to the version command output, or removing it from the Makefile LDFLAGS to avoid confusion and maintain consistency between the Makefile and the workflow.
| @@ -0,0 +1,116 @@ | |||
| #!/bin/bash | |||
There was a problem hiding this comment.
The release.sh script is missing a file extension check to ensure it's executable. While the script has a shebang and proper permissions might be set via git, the PR description mentions checking that the script is executable. Consider documenting in the README or adding a check in CI to verify the script has executable permissions, or ensure it's set correctly when the file is created.
- Fix Go version to 1.25.3 to match go.mod (Claude, Copilot) - Add GOWORK=off to build env to handle workspace files (Claude) - Use explicit macOS runners: macos-13 (amd64), macos-14 (arm64) (Copilot) - Add version validation for workflow_dispatch input (Copilot) - Remove verbose flag from tar command (Copilot) - Sort checksums for deterministic output (Copilot) Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
PR Review: CLI Release Workflow and Homebrew SupportThis PR adds a comprehensive CLI release infrastructure with GitHub Actions workflow, release script, and Homebrew support. Overall, the implementation is solid and well-structured. Here's my detailed feedback: ✅ Strengths
🔍 Code Quality Observations.github/workflows/cli-release.yml
release.sh
cmd/tinkerdown/main.go
🔒 Security Considerations
⚡ Performance Considerations
🧪 Test Coverage
📝 Suggested Improvements (Optional)
🎯 Overall AssessmentThis is a high-quality PR that adds professional release infrastructure. The implementation follows Go and GitHub Actions best practices, includes appropriate safety checks, and provides a good user experience. Recommendation: ✅ Approve The suggested improvements are nice-to-haves rather than blockers. The current implementation is production-ready and well-tested according to the test plan. 📋 Pre-merge ChecklistBefore merging and creating the first release:
Great work on this release infrastructure! The attention to detail in both the workflow and release script shows careful consideration of the release process. 🚀 |
- Improve manual install instructions with tmpdir for safer extraction - Remove unused buildTime variable from Makefile LDFLAGS Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
PR Review: CLI Release Workflow and Homebrew SupportI've reviewed PR #122 and have the following feedback: ✅ Strengths
🔍 Issues & RecommendationsCritical1. Go Version Issue (cli-release.yml:18) GO_VERSION: '1.25.3'Problem: Go 1.25.3 doesn't exist yet. The latest Go version as of early 2026 is 1.23.x. This will cause the workflow to fail. Fix: Update to a valid Go version: GO_VERSION: '1.23' # or '1.23.x' for latest patchThis same issue exists in Medium Priority2. Missing Windows support - os: windows
arch: amd64
runner: windows-latest3. No tests run before release (cli-release.yml) jobs:
test:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: actions/setup-go@v5
with:
go-version: ${{ env.GO_VERSION }}
- run: GOWORK=off go test -short ./internal/...
build:
needs: [test] # Add this dependency
# ... rest of build job4. Checksum aggregation could fail (cli-release.yml:119) cat *.sha256 | sort > checksums.txtThis assumes all Low Priority / Suggestions5. Hardcoded binary name (cli-release.yml:69) 6. Release script doesn't validate Go installation 7. Manual Homebrew formula updates
8. No rollback instructions 9. GOWORK=off usage 🔒 SecurityNo significant security concerns. The workflow:
📊 Test CoverageThe PR includes a comprehensive test plan, but the workflow itself doesn't validate the code before releasing. Recommend adding automated tests to the CI workflow. 📝 DocumentationThe release notes template is excellent and user-friendly. The installation instructions are clear and cover multiple methods. SummaryThis is a solid foundation for CLI releases. The main blocker is the invalid Go version (1.25.3) which must be fixed before merge. I recommend:
After fixing the Go version, this PR will provide a professional release workflow for the tinkerdown CLI. Recommendation: Request changes for the Go version issue, then approve after fix. |
Summary
cli-release.ymlworkflow triggered byv*tags to build and release CLI binariesrelease.shscript for creating releases safelylivetemplate/homebrew-taprepository with formula templateCloses #62
Test plan
GOWORK=off go build ./...go test -short ./internal/...go build -ldflags="-X main.version=test" -o tinkerdown ./cmd/tinkerdown && ./tinkerdown versionoutputstestrelease.shscript is executable and validates input./release.sh 0.1.0to create first release🤖 Generated with Claude Code