Overview
check-image uses fully automated releases with:- release-please: Manages versioning, changelog, and release PRs
- GoReleaser: Builds binaries for multiple platforms
- Docker: Publishes multi-arch images to GHCR
- Homebrew: Updates the tap formula automatically
Conventional Commits
Releases are driven by commit messages following the Conventional Commits format:Commit Types and Version Bumps
| Type | Version Bump | Changelog | Example |
|---|---|---|---|
feat: | Minor (0.1.0 → 0.2.0) | Yes | feat: Add platform validation |
fix: | Patch (0.1.0 → 0.1.1) | Yes | fix: Handle missing config |
perf: | Patch | Yes | perf: Optimize image loading |
refactor: | Patch | Yes | refactor: Simplify auth logic |
docs: | None | No | docs: Update installation guide |
chore: | None | No | chore: Update dependencies |
ci: | None | No | ci: Add security scanning |
test: | None | No | test: Add coverage for secrets |
build: | None | No | build: Update GoReleaser config |
revert: | None | No | revert: Undo feature X |
Only
feat:, fix:, perf:, and refactor: trigger version bumps and releases.Breaking Changes
For major version bumps (0.1.0 → 1.0.0), includeBREAKING CHANGE: in the commit body:
Commit Message Rules
Required:- Start with a lowercase type prefix
- Start description with uppercase letter
- Wrap code/commands/files in backticks
- Use present tense (“Add feature” not “Added feature”)
Release Workflow
1. Development Phase
Developers make changes and commit with conventional commits:2. Merge to Main
After PR approval and CI passes, merge tomain:
3. Release PR Creation
release-please analyzes commits since the last release and automatically:- Calculates the next version based on commit types
- Generates changelog entries from commit messages
- Updates version references in files
- Creates or updates a Release PR
CHANGELOG.mdwith new entriesREADME.mdwith updated version referencesaction.ymlwith updated default version.github/release-please-manifest.jsonwith new version
4. Release Execution
When the Release PR is merged, three jobs run in sequence:Job 1: release-please
- Creates git tag (e.g.,
v0.20.0) - Creates GitHub Release with changelog
- Updates PR label to
autorelease: tagged - Exports outputs:
releases_created=true,tag_name=v0.20.0
Job 2: goreleaser
Depends onrelease-please job. Only runs if releases_created == 'true'.
- Checks out the tagged commit
- Runs
go mod tidyandgo test ./... - Builds binaries for all platforms:
- Linux: amd64, arm64
- macOS: amd64, arm64
- Windows: amd64
- Injects version via ldflags:
- Creates archives (
.tar.gzfor Unix,.zipfor Windows) - Generates checksums
- Uploads assets to GitHub Release via
mode: append - Updates Homebrew tap formula at
jarfernandez/homebrew-tap
Job 3: docker
Depends on bothrelease-please and goreleaser jobs.
- Checks out the tagged commit
- Lints Dockerfile with hadolint
- Builds single-arch image (linux/amd64) for scanning
- Runs Trivy security scan (fails on CRITICAL/HIGH)
- Validates image with check-image (dogfooding):
- Builds and pushes multi-arch image (linux/amd64, linux/arm64)
- Tags with semver versions:
ghcr.io/jarfernandez/check-image:0.20.0ghcr.io/jarfernandez/check-image:0.20ghcr.io/jarfernandez/check-image:0ghcr.io/jarfernandez/check-image:latest
Configuration Files
.github/release-please-config.json
Configures release-please behavior:release-type: "go": Enables Go-specific versioningbump-minor-pre-major: true:featbumps minor before 1.0.0changelog-sections: Controls what appears in changeloghidden: true: Excludes from changelog (but still tracked)extra-files: Auto-updates version markers in these files
.github/release-please-manifest.json
Tracks the current version:.goreleaser.yml
Configures binary builds:- Static binaries (CGO_ENABLED=0)
- Stripped binaries (-s -w reduces size)
- Version injection via ldflags
- Multi-platform builds (5 platforms)
- Archives include sample configs
- Homebrew formula auto-published
GoReleaser Templates
GoReleaser uses template variables:| Variable | Example | Description |
|---|---|---|
{{.Version}} | 0.20.0 | Release version (without v) |
{{.Tag}} | v0.20.0 | Git tag (with v) |
{{.ShortCommit}} | a1b2c3d | 7-character commit hash |
{{.Commit}} | a1b2c3d4e5f6... | Full commit hash |
{{.Date}} | 2026-03-04T12:34:56Z | RFC3339 UTC timestamp |
{{.Os}} | linux | Target operating system |
{{.Arch}} | amd64 | Target architecture |
Homebrew Distribution
Tap Setup
Repository:jarfernandez/homebrew-tapFormula path:
Formula/check-image.rbInstallation:
Authentication
GoReleaser needs write access to the tap repository:- Create a Fine-grained Personal Access Token
- Grant
Contents: read and writepermission onhomebrew-tap - Add as repository secret:
HOMEBREW_TAP_GITHUB_TOKEN - GoReleaser uses this token in the
brewssection
Token Expiration
GitHub sends email warnings before PAT expiration. Renew the token and update the repository secret to avoid release failures.Docker Image Publishing
Image Tags
Every release publishes images toghcr.io/jarfernandez/check-image with multiple tags:
Multi-Arch Support
Images are built for:linux/amd64linux/arm64
Security Validation
Before publishing, the image is:- Scanned with Trivy for CRITICAL/HIGH vulnerabilities
- Validated with check-image (dogfooding):
- Size < 20MB
- Runs as non-root
- No unauthorized ports
- No secrets detected
Version Injection
The Dockerfile accepts build args:Troubleshooting
Release PR Not Created
Possible causes:- No commits since last release that trigger version bumps
- Only
docs:,chore:,ci:, ortest:commits - Commits don’t follow Conventional Commits format
feat:, fix:, perf:, or refactor: commit.
Release PR Stuck
Symptom: New release PR not created after merging previous one. Cause: Previous release PR has labelautorelease: pending but was never released.
Solution:
- Find the stuck PR
- Change label from
autorelease: pendingtoautorelease: tagged - release-please will create a new Release PR
GoReleaser Fails
Check:- Tests pass:
go test ./... - Modules are tidy:
go mod tidy - Homebrew token is valid (check expiration)
Docker Build Fails
Check:- Dockerfile lints:
hadolint Dockerfile - Image builds locally:
- Trivy scan passes:
Manual Release (Emergency)
If automation fails, you can release manually:Next Steps
CI/CD
Learn about continuous integration pipelines
Contributing
Back to contributing overview