Skip to content

feat: add CLI release workflow and Homebrew support#122

Merged
adnaan merged 3 commits intomainfrom
homebrew-formula
Feb 2, 2026
Merged

feat: add CLI release workflow and Homebrew support#122
adnaan merged 3 commits intomainfrom
homebrew-formula

Conversation

@adnaan
Copy link
Contributor

@adnaan adnaan commented Jan 25, 2026

Summary

  • Add cli-release.yml workflow triggered by v* tags to build and release CLI binaries
  • Build binaries for macOS (amd64, arm64) and Linux (amd64)
  • Add release.sh script for creating releases safely
  • Make version variable injectable via ldflags for build-time versioning
  • Created separate livetemplate/homebrew-tap repository with formula template

Closes #62

Test plan

  • Build succeeds with GOWORK=off go build ./...
  • Unit tests pass with go test -short ./internal/...
  • Version injection works: go build -ldflags="-X main.version=test" -o tinkerdown ./cmd/tinkerdown && ./tinkerdown version outputs test
  • release.sh script is executable and validates input
  • Homebrew tap repository created at https://github.com/livetemplate/homebrew-tap
  • After merge: run ./release.sh 0.1.0 to create first release
  • After release: update homebrew-tap formula with SHA256 checksums

🤖 Generated with Claude Code

- 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>
Copilot AI review requested due to automatic review settings January 25, 2026 10:13
@claude
Copy link

claude bot commented Jan 25, 2026

Pull Request Review: CLI Release Workflow and Homebrew Support

Thank you for this contribution! This is a well-structured PR that adds important release automation.

Strengths

Code Quality

  • Clean workflow structure with clear job separation
  • Good use of modern Go features (strings.CutPrefix refactoring in cmd/tinkerdown/main.go:42-45)
  • Comprehensive release script with excellent validation and safety checks
  • Good documentation with clear inline comments

Best Practices

  • Proper minimal permissions (contents: write)
  • Proper artifact management between jobs
  • Clean version handling from both tag and workflow_dispatch
  • SHA256 checksums for all binaries

Critical Issues

1. 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 Variable

Location: .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

Recommendations

3. Missing Build Verification

Add smoke test after building to verify binary works before release

4. Windows Support

Desktop workflow includes Windows but CLI does not. Is this intentional?

5. Release Script Race Condition

release.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

  • Run shellcheck on release.sh
  • Extract release body to template file
  • Automate Homebrew formula updates
  • Add automated tests for version injection

Security

Good:

  • SHA256 checksums
  • Minimal permissions
  • Input validation
  • No hardcoded secrets

Consider:

  • Binary signing for macOS/Windows
  • SLSA provenance
  • GitHub attestation

Summary

Solid PR with valuable automation. Main blocking issue is Go version mismatch.

Required Before Merge:

  • Fix Go version to 1.25.3
  • Add GOWORK=off

Scores:

  • Code Quality: 8/10
  • Security: 8/10
  • Test Coverage: 6/10
  • Documentation: 9/10
  • Overall: 8/10

Copy link

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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.

Comment on lines +8 to +12
inputs:
version:
description: 'Version (e.g., 1.0.0)'
required: true
type: string
Copy link

Copilot AI Jan 25, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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.

Copilot uses AI. Check for mistakes.
- name: Create archive
run: |
mkdir -p dist
tar -czvf dist/tinkerdown-${{ matrix.os }}-${{ matrix.arch }}.tar.gz tinkerdown
Copy link

Copilot AI Jan 25, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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.

Suggested change
tar -czvf dist/tinkerdown-${{ matrix.os }}-${{ matrix.arch }}.tar.gz tinkerdown
tar -czf dist/tinkerdown-${{ matrix.os }}-${{ matrix.arch }}.tar.gz tinkerdown

Copilot uses AI. Check for mistakes.
Comment on lines 29 to 32
runner: macos-latest
- os: darwin
arch: arm64
runner: macos-latest
Copy link

Copilot AI Jan 25, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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.

Suggested change
runner: macos-latest
- os: darwin
arch: arm64
runner: macos-latest
runner: macos-13
- os: darwin
arch: arm64
runner: macos-14

Copilot uses AI. Check for mistakes.
Comment on lines +104 to +108
if [ "${{ github.event_name }}" = "workflow_dispatch" ]; then
echo "version=${{ inputs.version }}" >> $GITHUB_OUTPUT
else
echo "version=${GITHUB_REF_NAME#v}" >> $GITHUB_OUTPUT
fi
Copy link

Copilot AI Jan 25, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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.

Suggested change
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"

Copilot uses AI. Check for mistakes.
- name: Create checksums file
run: |
cd release
cat *.sha256 > checksums.txt
Copy link

Copilot AI Jan 25, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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.

Suggested change
cat *.sha256 > checksums.txt
cat *.sha256 | sort > checksums.txt

Copilot uses AI. Check for mistakes.
Comment on lines 150 to 159
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/
Copy link

Copilot AI Jan 25, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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.

Suggested change
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"

Copilot uses AI. Check for mistakes.
contents: write

env:
GO_VERSION: '1.23'
Copy link

Copilot AI Jan 25, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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.

Copilot uses AI. Check for mistakes.
GOARCH: ${{ matrix.arch }}
CGO_ENABLED: 0
run: |
go build -ldflags="-s -w -X main.version=${{ steps.version.outputs.version }}" \
Copy link

Copilot AI Jan 25, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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.

Copilot uses AI. Check for mistakes.
@@ -0,0 +1,116 @@
#!/bin/bash
Copy link

Copilot AI Jan 25, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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.

Copilot uses AI. Check for mistakes.
- 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>
@claude
Copy link

claude bot commented Jan 25, 2026

PR Review: CLI Release Workflow and Homebrew Support

This 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

  1. Well-structured release workflow: The separation of build and release jobs with proper artifact handling is clean and follows best practices.

  2. Cross-platform support: Good coverage with macOS (Intel + ARM), and Linux AMD64 builds.

  3. Version injection: The ldflags-based version injection is the correct approach for Go binaries.

  4. Safe release script: The release.sh script includes excellent safety checks:

    • Uncommitted changes detection
    • Branch validation
    • Tag existence checking
    • Remote sync verification
    • User confirmation prompts
  5. Code modernization: The use of strings.CutPrefix in main.go:42-45 is a nice improvement over strings.TrimPrefix (cleaner and more idiomatic in Go 1.20+).

  6. Consistent versioning: Go version matches go.mod (1.25.3) across all workflows.

🔍 Code Quality Observations

.github/workflows/cli-release.yml

  1. Potential issue with workflow_dispatch tag creation (.github/workflows/cli-release.yml:127):

    • When using workflow_dispatch, the tag is created by the workflow but may not exist when the build job runs
    • The build jobs checkout with @v4 which defaults to the commit SHA, not the tag
    • Consider if the tag needs to be created before the build starts, or if this is intentional
  2. Checksum aggregation (.github/workflows/cli-release.yml:120):

    • Using cat *.sha256 | sort works, but the individual .sha256 files already contain the filename
    • This is fine, but ensure downstream consumers (Homebrew formula) know to parse these correctly
  3. Missing Windows support:

    • The workflow only builds for macOS and Linux
    • Windows users might want CLI binaries too
    • Consider adding Windows to the matrix (can be added in follow-up):
      - os: windows
        arch: amd64
        runner: windows-latest

release.sh

  1. Excellent error handling and user experience: The colored output, validation, and safety checks are well done.

  2. Minor: Hardcoded branch name (release.sh:53-60):

    • The script assumes main as the primary branch
    • Consider making this configurable or detecting from git config
    • However, this is a minor point and the current approach is acceptable for most workflows
  3. Tag message (release.sh:103):

    • The tag message is simple: "Release X.Y.Z"
    • Consider including a brief summary or reference to the changelog in future iterations
    • Current approach is perfectly acceptable

cmd/tinkerdown/main.go

  1. strings.CutPrefix usage (main.go:42-45):

    • This is a good modernization from the previous strings.TrimPrefix
    • Requires Go 1.20+ but your go.mod shows 1.25.3, so this is fine ✅
  2. Version variable (main.go:13):

    • Changed from const version = "0.1.0-dev" to var version = "dev"
    • The "dev" default is good for identifying non-release builds
    • The comment clearly explains the ldflags usage ✅

🔒 Security Considerations

  1. Workflow permissions (.github/workflows/cli-release.yml:14-15):

    • Using contents: write is necessary for creating releases
    • This is appropriately scoped and minimal
    • ✅ Good practice
  2. Checksum generation (.github/workflows/cli-release.yml:76-79):

    • SHA256 checksums are generated for all artifacts
    • ✅ Excellent for supply chain security
    • Users can verify downloads haven't been tampered with
  3. Build flags (.github/workflows/cli-release.yml:68):

    • CGO_ENABLED=0: Good for static binaries
    • -ldflags="-s -w": Strips debug info and symbol table (smaller binaries)
    • ✅ Standard practice for production releases
  4. release.sh git operations:

    • The script validates uncommitted changes and tag existence
    • Consider adding remote URL validation in a future iteration (nice-to-have, not critical)

⚡ Performance Considerations

  1. Build matrix parallelization (.github/workflows/cli-release.yml:21-35):

    • Three builds run in parallel (macOS Intel, macOS ARM, Linux)
    • ✅ Efficient use of GitHub Actions runners
  2. Go build cache (.github/workflows/cli-release.yml:43-44):

    • cache: true is enabled for the Go setup
    • ✅ Will speed up subsequent builds
  3. Archive creation (.github/workflows/cli-release.yml:72-74):

    • Using gzip compression (tar -czf)
    • Good balance between compression ratio and compatibility ✅

🧪 Test Coverage

  1. Test plan in PR description:

    • ✅ Excellent manual test plan included
    • Build verification, unit tests, version injection testing all covered
    • Manual validation of release.sh script
  2. No automated workflow testing:

    • The workflow itself isn't tested in CI before merge
    • This is common for release workflows and acceptable given thorough manual testing
  3. Potential enhancement - smoke tests:

    • Could add a simple smoke test step after build to verify binaries execute:
      - name: Smoke test
        run: |
          ./tinkerdown version
          ./tinkerdown help
    • Not critical, but would provide additional confidence

📝 Suggested Improvements (Optional)

  1. Add Windows support to the build matrix for completeness (can be added later)

  2. Add smoke tests after building binaries to verify they execute correctly (nice-to-have)

  3. Consider signing/notarization for macOS binaries in the future (requires Apple Developer account)

  4. Document the Homebrew update process:

    • Currently mentioned in release.sh output and PR description
    • Consider adding a RELEASING.md document with the full release process
  5. Version validation consistency: The semver regex is consistent between workflow and release.sh ✅

🎯 Overall Assessment

This 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 Checklist

Before merging and creating the first release:

  • Verify the homebrew-tap repository formula template is ready at https://github.com/livetemplate/homebrew-tap
  • Decide on initial version number (suggested: 0.1.0 based on test plan)
  • Ensure GOWORK environment is properly handled in local builds (already addressed with GOWORK=off)

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>
@claude
Copy link

claude bot commented Feb 2, 2026

PR Review: CLI Release Workflow and Homebrew Support

I've reviewed PR #122 and have the following feedback:

✅ Strengths

  1. Well-structured workflow: The GitHub Actions workflow is clean and follows best practices with proper job separation (build + release).

  2. Good platform coverage: macOS (Intel + Apple Silicon) and Linux amd64 covers the primary target platforms.

  3. Safe release script: release.sh includes excellent safety checks:

    • Validates semver format
    • Checks for uncommitted changes
    • Verifies branch and tag status
    • Requires explicit confirmation before tagging
  4. Code improvements: The change from strings.HasPrefix to strings.CutPrefix in cmd/tinkerdown/main.go:42-45 is more idiomatic Go (available since Go 1.20).

  5. Proper versioning: Using ldflags for version injection is the standard approach.

🔍 Issues & Recommendations

Critical

1. 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 patch

This same issue exists in desktop-release.yml:18 and should be fixed there too.

Medium Priority

2. Missing Windows support
The workflow only builds for macOS and Linux. While not blocking, Windows is a significant platform for CLI tools. Consider adding:

- os: windows
  arch: amd64
  runner: windows-latest

3. No tests run before release (cli-release.yml)
The workflow builds and releases without running tests. Consider adding a test job that runs before the build job:

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 job

4. Checksum aggregation could fail (cli-release.yml:119)

cat *.sha256 | sort > checksums.txt

This assumes all .sha256 files are in the expected format. If any file is malformed, it will silently include bad data. Consider validation or using a more explicit approach.

Low Priority / Suggestions

5. Hardcoded binary name (cli-release.yml:69)
The binary name "tinkerdown" is hardcoded. Consider using a variable for consistency.

6. Release script doesn't validate Go installation
release.sh doesn't check if the local build passes before tagging. Users could tag a version that won't build in CI.

7. Manual Homebrew formula updates
The process requires manual SHA256 updates to the Homebrew formula. Consider:

  • Automating this with a second workflow that updates the tap repository
  • Using GoReleaser which can handle Homebrew taps automatically

8. No rollback instructions
If a release fails, there's no documented way to clean up and retry. Consider adding rollback steps to the release script or documentation.

9. GOWORK=off usage
The workflow uses GOWORK=off but the test plan also mentions it. Ensure this is consistently needed across the codebase (workspaces usage).

🔒 Security

No significant security concerns. The workflow:

  • Uses pinned action versions (good)
  • Has appropriate permissions (contents: write only)
  • Doesn't expose secrets in logs

📊 Test Coverage

The 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.

📝 Documentation

The release notes template is excellent and user-friendly. The installation instructions are clear and cover multiple methods.

Summary

This 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:

  1. Must fix: Update Go version to 1.23 or latest stable
  2. Should add: Test job before build
  3. Consider: Windows support, automated Homebrew updates

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.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

5.4 Homebrew Formula

1 participant