This session involved:
- Minimizing Go CLI dependencies (removing goldmark library)
- Setting up GitHub Actions CI/CD for multi-platform builds
- Creating first release with proper binary distribution
Mistake: Built binary (<x or any name>-cli) was committed to the repository.
Why it's bad:
- Bloates repository size
- Binaries are platform-specific and shouldn't be in source control
- Conflicts with CI/CD build process
- Makes cloning slower
Fix:
git rm --cached mdquery-cli
echo "mdquery-cli" >> .gitignoreBest Practice: Always add .gitignore before building, exclude:
- Compiled binaries
- Build artifacts
- Platform-specific files
Mistake: Used release event trigger instead of tag push.
Original:
on:
release:
types: [created]Why it's bad:
- Requires manual release creation in GitHub UI first
- Doesn't work with automated tag-based releases
- Extra manual step defeats automation purpose
Fix:
on:
push:
tags:
- "v*"Best Practice: Use tag-based releases for automation:
git tag v0.0.1
git push origin v0.0.1This automatically triggers the workflow and creates the release.
Mistake: All platform builds used same binary name (mdquery-cli), causing overwrites.
Original:
- name: Build binary
run: |
BINARY_NAME=mdquery-cli
if [ "$GOOS" = "windows" ]; then
BINARY_NAME=mdquery-cli.exe
fi
go build -ldflags="-s -w" -o $BINARY_NAMEWhy it's bad:
- Each matrix iteration overwrites the same file
- Only the last built binary remains
- Release ends up with single binary instead of multi-platform set
Fix:
- name: Build binary
run: |
BINARY_NAME=mdquery-cli-${{ matrix.goos }}-${{ matrix.goarch }}
if [ "$GOOS" = "windows" ]; then
BINARY_NAME=mdquery-cli-${{ matrix.goos }}-${{ matrix.goarch }}.exe
fi
go build -ldflags="-s -w" -o $BINARY_NAMEBest Practice: Always include platform/architecture in binary names:
mdquery-cli-linux-amd64mdquery-cli-darwin-arm64mdquery-cli-windows-amd64.exe
# Initialize .gitignore first
cat > .gitignore << 'EOF'
# Binaries
*cli
*.exe
# Build artifacts
*.o
*.a
*.so
# Test coverage
*.out
coverage.html
EOF
# Build and test locally
go build -o myapp
./myapp --help
go test -vname: Build and Release
on:
push:
branches: [main, master]
tags:
- "v*"
pull_request:
branches: [main, master]
jobs:
build:
runs-on: ubuntu-latest
strategy:
matrix:
goos: [linux, darwin, windows]
goarch: [amd64, arm64]
exclude:
- goos: windows
goarch: arm64
steps:
- uses: actions/checkout@v4
- uses: actions/setup-go@v5
with:
go-version: "1.21"
- name: Download dependencies
run: go mod download
- name: Run tests
run: go test -v
- name: Build binary
env:
GOOS: ${{ matrix.goos }}
GOARCH: ${{ matrix.goarch }}
CGO_ENABLED: 0
run: |
BINARY_NAME=myapp-${{ matrix.goos }}-${{ matrix.goarch }}
if [ "$GOOS" = "windows" ]; then
BINARY_NAME=myapp-${{ matrix.goos }}-${{ matrix.goarch }}.exe
fi
go build -ldflags="-s -w" -o $BINARY_NAME
- name: Upload artifact
uses: actions/upload-artifact@v4
with:
name: myapp-${{ matrix.goos }}-${{ matrix.goarch }}
path: myapp-${{ matrix.goos }}-${{ matrix.goarch }}*
release:
if: startsWith(github.ref, 'refs/tags/v')
needs: build
runs-on: ubuntu-latest
permissions:
contents: write
steps:
- uses: actions/checkout@v4
- name: Download all artifacts
uses: actions/download-artifact@v4
with:
path: ./artifacts
- name: Create GitHub Release
uses: softprops/action-gh-release@v2
with:
files: ./artifacts/**/*
generate_release_notes: true
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}# Make changes
git add .
git commit -m "Your commit message"
# Tag and push
git tag v0.0.1
git push origin main
git push origin v0.0.1This single command sequence:
- Builds all platform binaries
- Runs tests
- Creates GitHub release
- Attaches all binaries to release
- Generates release notes automatically
- Never commit binaries - Set up
.gitignorebefore first build - Use tag-based releases - Automate the entire process with
git push v* - Unique binary names - Always include platform/architecture in filename
- Test locally first - Ensure builds work before pushing
- Use CGO_ENABLED=0 - Creates static binaries for portability
- Strip debug symbols - Use
-ldflags="-s -w"for smaller binaries - Generate release notes - Let GitHub create changelog from commits
git rm --cached binary-name
echo "binary-name" >> .gitignore
git add .gitignore
git commit -m "Remove binary from git"# Wrong: release event
on:
release:
types: [created]
# Right: tag push
on:
push:
tags:
- "v*"# Wrong: all builds produce same name
go build -o myapp
# Right: platform-specific names
go build -o myapp-${GOOS}-${GOARCH}git tag v1.0.0
git push origin v1.0.0This single command triggers the entire automated release process.