FontGet Release Guide

May 27, 2026 ยท View on GitHub

๐Ÿ“‹ Overview

This guide provides a step-by-step process for creating releases in FontGet. It covers when to release, how to determine version numbers, and the exact commands needed to create and publish a release.

Related Documentation:


๐ŸŽฏ When to Release

Release When You Have:

  1. New Features - Added functionality that users will benefit from
  2. Bug Fixes - Fixed issues that affect users
  3. Security Fixes - Critical security patches (release immediately)
  4. Breaking Changes - Incompatible changes that require user action
  5. Documentation Updates - Significant documentation improvements (usually bundled with features)

Don't Release For:

  • โŒ Work-in-progress features (wait until complete)
  • โŒ Internal refactoring only (unless it's a breaking change)
  • โŒ Every single commit (batch related changes)
  • โŒ Unmerged pull requests (must be on main branch)

Release Frequency

  • No fixed schedule - Release when you have meaningful changes
  • Regular releases - Better to release frequently with small changes than infrequently with large changes
  • Security fixes - Release immediately, even if it's just a PATCH increment

๐Ÿ“ Determining Version Numbers

FontGet uses Semantic Versioning (SemVer) in the format MAJOR.MINOR.PATCH.

Quick Decision Tree

Did you make a breaking change?
โ”œโ”€ YES โ†’ Increment MAJOR (1.2.3 โ†’ 2.0.0)
โ””โ”€ NO โ†’ Did you add new features?
    โ”œโ”€ YES โ†’ Increment MINOR (1.2.3 โ†’ 1.3.0)
    โ””โ”€ NO โ†’ Increment PATCH (1.2.3 โ†’ 1.2.4)

Version Increment Rules

Change TypeExampleIncrementExample
Breaking changeRemoved command, changed config formatMAJOR1.2.3 โ†’ 2.0.0
New featureAdded new command, new flagsMINOR1.2.3 โ†’ 1.3.0
Bug fixFixed crash, corrected outputPATCH1.2.3 โ†’ 1.2.4
Security fixFixed vulnerabilityPATCH1.2.3 โ†’ 1.2.4
PerformanceFaster search, reduced memoryPATCH1.2.3 โ†’ 1.2.4

What Counts as Breaking?

โœ… Breaking Changes:

  • Removing a command or flag
  • Changing config file format (old configs won't work)
  • Changing default behavior in a way that breaks existing scripts
  • Removing or renaming exported functions (for libraries)

โŒ NOT Breaking Changes:

  • Adding new commands/flags
  • Changing internal implementation
  • Performance improvements
  • Bug fixes that change incorrect behavior

Multiple Changes in One Release

If your release includes multiple types of changes:

  • Breaking + Features + Fixes โ†’ MAJOR increment (breaking takes priority)
  • Features + Fixes โ†’ MINOR increment (features take priority)
  • Fixes only โ†’ PATCH increment

Example:

  • Current: v1.2.3
  • Changes: Added backup command (feature) + Fixed crash bug (fix)
  • Decision: MINOR increment โ†’ v1.3.0 (bug fix included in minor release)

For detailed version decision logic, see the Versioning Guide.


โœ… Pre-Release Checklist

Before creating a release, ensure:

  • All features complete - No work-in-progress code
  • All tests pass - Run tests locally and verify CI passes
  • Code merged to main - All changes are on the main branch
  • Documentation updated - README, guides, and help text are current
  • Version number decided - Use decision tree above
  • Changelog reviewed - Know what's changed since last release
  • No breaking changes (or documented if there are)

Verifying Changes Since Last Release

# Check latest tag
git describe --tags --abbrev=0
# Output: v1.2.3

# Review commits since last release
git log v1.2.3..HEAD --oneline

# Review detailed changes
git log v1.2.3..HEAD --pretty=format:"%h - %s (%an, %ar)"

๐Ÿš€ Release Process

Step 1: Ensure You're on Main Branch

# Check current branch
git branch --show-current
# Should output: main

# If not on main, switch to it
git checkout main

# Pull latest changes
git pull origin main

Step 2: Check Current Version

# Get the latest tag
git describe --tags --abbrev=0
# Output: v1.2.3

# List all tags (optional, for reference)
git tag -l

Step 3: Determine Next Version

  1. Review changes since last release (see commands above)
  2. Use decision tree to determine MAJOR/MINOR/PATCH
  3. Calculate next version:
    • Last: v1.2.3
    • Added feature โ†’ v1.3.0 (MINOR)
    • Fixed bug โ†’ v1.2.4 (PATCH)
    • Breaking change โ†’ v2.0.0 (MAJOR)

Step 4: Create Release Tag

Use an annotated tag with a descriptive message:

# Format: git tag -a v<MAJOR>.<MINOR>.<PATCH> -m "Release v<MAJOR>.<MINOR>.<PATCH>: <Description>"

# Example for MINOR release
git tag -a v1.3.0 -m "Release v1.3.0: Added interactive TUI onboarding system and theme improvements"

# Example for PATCH release
git tag -a v1.2.4 -m "Release v1.2.4: Fixed Windows terminal detection and update cleanup"

# Example for MAJOR release
git tag -a v2.0.0 -m "Release v2.0.0: Major refactor with breaking config format changes"

Tag Message Best Practices:

  • โœ… Include version number in message
  • โœ… List main features/changes
  • โœ… Be descriptive but concise
  • โŒ Don't use generic messages like "Release" or "Update"

Step 5: Verify Tag Created

# Verify tag exists
git tag -l v1.3.0

# View tag details
git show v1.3.0

Step 6: Push Tag to GitHub

# Push the tag (this triggers CI/CD)
git push origin v1.3.0

# Or push all tags (if you have multiple)
git push origin --tags

What happens next:

  • GitHub Actions detects the new tag
  • GoReleaser builds binaries for all platforms
  • GitHub Release is created automatically
  • Binaries and checksums are uploaded
  • Self-update system can now detect the new version

Step 7: Monitor CI/CD Pipeline

  1. Go to GitHub repository โ†’ Actions tab
  2. Find the workflow run triggered by the tag push
  3. Wait for build to complete (usually 5-10 minutes)
  4. Verify GitHub Release was created with binaries

Step 8: Verify Release

# Check GitHub Release was created (via web interface)
# Or verify via GitHub CLI if installed
gh release view v1.3.0

# Test self-update (from a previous version)
fontget update

Step 9: Run Chocolatey Workflow

Chocolatey publication is handled by a dedicated workflow (Chocolatey Release) after the main release completes.

  1. Open GitHub Actions and run Chocolatey Release (workflow_dispatch).
  2. Enter the release version (for example: 1.3.0 or v1.3.0).
  3. Confirm preflight checks pass:
    • Release tag exists and is public (not draft)
    • Required assets exist (fontget_<version>_windows_amd64.zip, checksums.txt)
    • Checksum placeholder in chocolateyInstall.ps1 is replaced before packing
    • Generated nupkg contains tools/chocolateyInstall.ps1 and tools/chocolateyUninstall.ps1
  4. Confirm the push step succeeds (dotnet nuget push ... --skip-duplicate).

Release Orchestration (Current)

  1. Release workflow (release.yml)
    • Runs tests and goreleaser check
    • Publishes GitHub release assets
    • Publishes Winget, AUR, Homebrew, and Scoop
    • Skips Chocolatey
  2. Chocolatey Release workflow (chocolatey-release.yml)
    • Runs strict preflight validations
    • Packs and pushes Chocolatey package independently

๐Ÿ“ Complete Release Example

Here's a complete example of releasing v1.3.0:

# Step 1: Ensure on main branch
git checkout main
git pull origin main

# Step 2: Check current version
git describe --tags --abbrev=0
# Output: v1.2.3

# Step 3: Review changes
git log v1.2.3..HEAD --oneline
# Review: Added TUI onboarding (feature), fixed bugs (fixes)
# Decision: MINOR increment (new feature)

# Step 4: Create tag
git tag -a v1.3.0 -m "Release v1.3.0: Added interactive TUI onboarding system, theme improvements, and bug fixes"

# Step 5: Verify tag
git show v1.3.0

# Step 6: Push tag
git push origin v1.3.0

# Step 7: Monitor CI/CD (via GitHub web interface)
# Wait for build to complete

# Step 8: Verify release
# Check GitHub Releases page for v1.3.0

๐Ÿท๏ธ Tag Format Requirements

Correct Format

  • โœ… v1.2.3 - Recommended (with v prefix)
  • โœ… 1.2.3 - Works, but less common

Incorrect Format

  • โŒ v1.2 - Missing PATCH number
  • โŒ v1.2.3.4 - Too many numbers
  • โŒ 1-2-3 - Wrong separator
  • โŒ release-1.2.3 - Wrong prefix

Tag Type

Always use annotated tags (-a flag):

  • Store metadata (author, date, message)
  • Preferred by GoReleaser
  • Better for releases

Don't use lightweight tags (no -a flag) for releases.


โš ๏ธ Common Mistakes and How to Avoid Them

Mistake 1: Skipping Versions

โŒ Wrong: v1.2.3 โ†’ v1.5.0 (skipped 1.3.0, 1.4.0)
โœ… Correct: v1.2.3 โ†’ v1.3.0 โ†’ v1.4.0 โ†’ v1.5.0

Why: SemVer requires sequential versions. Skipping versions confuses users and tools.

Mistake 2: Wrong Increment Type

โŒ Wrong: Added new feature but incremented PATCH (v1.2.3 โ†’ v1.2.4)
โœ… Correct: Added new feature, increment MINOR (v1.2.3 โ†’ v1.3.0)

Why: Version numbers communicate what changed. Wrong increment misleads users.

Mistake 3: Forgetting to Reset

โŒ Wrong: v1.2.3 โ†’ v2.1.4 (should reset MINOR and PATCH)
โœ… Correct: v1.2.3 โ†’ v2.0.0 (MAJOR increments reset others)

Why: MAJOR increments indicate breaking changes. MINOR and PATCH should reset to 0.

Mistake 4: Reusing Tags

โŒ Wrong: Delete and recreate tag v1.2.3
โœ… Correct: Use new version v1.2.4 (tags are immutable)

Why: Tags are permanent markers. Deleting and recreating breaks references.

Mistake 5: Tagging Wrong Commit

โŒ Wrong: Tag a commit that's not on main
โœ… Correct: Ensure all changes are merged to main before tagging

Why: Releases should only come from the main branch.

Mistake 6: Generic Tag Messages

โŒ Wrong: git tag -a v1.3.0 -m "Release"
โœ… Correct: git tag -a v1.3.0 -m "Release v1.3.0: Added backup command and fixed font matching"

Why: Descriptive messages help users understand what changed.


๐Ÿ” Post-Release Verification

After the CI/CD pipeline completes:

1. Check GitHub Release

  • Release exists on GitHub Releases page
  • Release title matches tag: v1.3.0
  • Release description is present
  • Archives are uploaded for all platforms (GoReleaser archives):
    • fontget_<version>_linux_amd64.tar.gz
    • fontget_<version>_linux_arm64.tar.gz
    • fontget_<version>_darwin_amd64.tar.gz
    • fontget_<version>_darwin_arm64.tar.gz
    • fontget_<version>_windows_amd64.zip
    • fontget_<version>_windows_arm64.zip
  • checksums.txt file is present
  • Source tarball is present (AUR)

2. Test Self-Update

# From a previous version, test update
fontget update

# Verify version after update
fontget version
# Should show: FontGet v1.3.0

3. Verify Version in Binary

# Download and test a binary
./fontget version
# Should output: FontGet v1.3.0

๐Ÿ”„ Rollback Procedure

If a release has critical issues:

  1. Create a new PATCH release with fixes
  2. Example: v1.3.0 (broken) โ†’ v1.3.1 (fixed)
  3. Follow normal release process
# Delete local tag
git tag -d v1.3.0

# Delete remote tag
git push origin --delete v1.3.0

โš ๏ธ Warning: Only do this if the release hasn't been downloaded yet. Deleting tags that users have already downloaded can cause confusion.

Better approach: Create a new PATCH release with fixes instead of deleting tags.


๐Ÿ“Š Release History Tracking

View Release History

# List all tags
git tag -l

# List tags with messages
git tag -l -n9

# View specific tag
git show v1.3.0

Compare Releases

# See changes between releases
git log v1.2.3..v1.3.0 --oneline

# See detailed diff
git diff v1.2.3..v1.3.0

๐ŸŽ“ Best Practices

1. Start with 0.1.0

  • First release should be v0.1.0 (not v1.0.0)
  • v1.0.0 should be reserved for "first stable release"

2. Be Conservative

  • When in doubt, use a smaller increment
  • Better to release v1.2.4 than v1.3.0 if unsure
  • You can always release another PATCH if needed

3. Document Breaking Changes

  • Always document what broke in MAJOR releases
  • Consider migration guides for breaking changes
  • Update changelog/README with migration steps

4. Regular Releases

  • Don't wait too long between releases
  • Small, frequent releases are better than large, infrequent ones
  • Users appreciate regular updates

5. Meaningful Tag Messages

# โœ… Good
git tag -a v1.3.0 -m "Release v1.3.0: Added backup command and improved font matching"

# โŒ Bad
git tag -a v1.3.0 -m "Release"

6. Test Before Releasing

  • Always test locally before creating a tag
  • Run all tests: go test ./...
  • Build and test binaries: go build -o fontget.exe .

๐Ÿšจ Emergency Releases

For critical security fixes or severe bugs:

  1. Fix the issue immediately
  2. Test thoroughly (but quickly)
  3. Create PATCH release (e.g., v1.2.3 โ†’ v1.2.4)
  4. Push tag immediately to trigger CI/CD
  5. Notify users if necessary (via GitHub Release notes)

Example:

# Emergency security fix
git tag -a v1.2.4 -m "Release v1.2.4: Critical security fix - CVE-2024-XXXX"
git push origin v1.2.4

โ“ FAQ

Q: Can I change a version after releasing?

A: No, tags are immutable. If you need to fix a release, create a new PATCH version (e.g., v1.3.0 โ†’ v1.3.1).

Q: What if I make a mistake in the version number?

A: Create a new tag with the correct version. Don't delete the old tag (it may be referenced). Consider documenting the mistake in release notes.

Q: How often should I release?

A: There's no fixed schedule. Release when you have meaningful changes (bug fixes, features, security fixes).

Q: Should I version every commit?

A: No, only version releases. Daily commits use "dev" version automatically.

Q: What about pre-releases (alpha, beta, rc)?

A: FontGet typically releases directly to stable. Pre-releases are supported by SemVer but not commonly used. See Versioning Guide for details.

Q: How do I know if CI/CD succeeded?

A: Check the GitHub Actions tab. A successful build will create a GitHub Release with binaries.

Q: What if CI/CD fails?

A:

  1. Check the Actions log for errors
  2. Fix the issue
  3. Delete the tag (if needed): git push origin --delete v1.3.0
  4. Create a new tag with fixes: git tag -a v1.3.1 -m "Release v1.3.1: Fixed build issue"
  5. Push the new tag

๐Ÿ“š Additional Resources


๐Ÿ“‹ Quick Reference

Release Command Template

# 1. Ensure on main
git checkout main && git pull origin main

# 2. Check current version
git describe --tags --abbrev=0

# 3. Review changes
git log $(git describe --tags --abbrev=0)..HEAD --oneline

# 4. Create and push tag
git tag -a v<MAJOR>.<MINOR>.<PATCH> -m "Release v<MAJOR>.<MINOR>.<PATCH>: <Description>"
git push origin v<MAJOR>.<MINOR>.<PATCH>

Version Decision Matrix

Change TypeIncrementExample
Breaking changeMAJOR1.2.3 โ†’ 2.0.0
New featureMINOR1.2.3 โ†’ 1.3.0
Bug fixPATCH1.2.3 โ†’ 1.2.4
Security fixPATCH1.2.3 โ†’ 1.2.4

Last Updated: 2025-01-XX
Status: Active Guide