This document describes the automated release process for Philote-Cpp, including how to create both stable releases and prereleases.
Overview
Philote-Cpp uses an automated release workflow that is triggered when a pull request with specific labels is merged to the main branch. The workflow:
- Validates PR labels
- Calculates the new version number
- Updates copyright years (stable releases only)
- Updates version in
CMakeLists.txt
- Updates
CHANGELOG.md
- Creates a signed git commit and tag
- Pushes directly to
main (using PAT with write permissions)
- Creates a GitHub Release
Prerequisites
The automated release workflow requires the following repository settings:
1. Personal Access Token (PAT)
The workflow requires a fine-grained Personal Access Token with permissions to push to the protected main branch.
Creating the PAT:
- Go to your GitHub profile → Settings → Developer settings → Personal access tokens → Fine-grained tokens
- Click Generate new token
- Configure:
- Token name: "Philote-Cpp Release Automation" (or similar)
- Expiration: Your choice (recommend 1 year and set a reminder to renew)
- Resource owner: Select MDO-Standards (the organization)
- Repository access: "Only select repositories" → Select Philote-Cpp
- Repository permissions:
- Contents: Read and write
- Metadata: Read-only (auto-selected)
- Pull requests: Read-only
- Click Generate token and copy it immediately
Adding to repository:
- Go to Philote-Cpp repository → Settings → Secrets and variables → Actions
- Click New repository secret
- Name:
RELEASE_TOKEN
- Value: Paste the PAT
- Click Add secret
2. Branch Protection on <tt>main</tt>
The following protections should be enabled:
- "Require pull requests before merging" - Enabled
- "Require signed commits" - Enabled (workflow creates GPG-signed commits)
3. Repository Labels
The following labels must exist in the repository:
- Release type:
release, prerelease
- Version bump:
major, minor, patch
- Prerelease type:
alpha, beta, rc
To create labels, see Step-by-Step Release Instructions below.
Version Numbering
Philote-Cpp follows Semantic Versioning (SemVer):
- MAJOR version for incompatible API changes
- MINOR version for backward-compatible functionality additions
- PATCH version for backward-compatible bug fixes
Prerelease versions follow the format: MAJOR.MINOR.PATCH-TYPE.NUMBER where:
- TYPE is one of:
alpha, beta, or rc (release candidate)
- NUMBER is an auto-incrementing counter starting at 1
Examples:
- Stable:
0.4.0, 1.0.0, 2.1.3
- Prerelease:
0.4.0-alpha.1, 1.0.0-beta.2, 2.0.0-rc.1
Release Types
Stable Release
A stable release is intended for production use and marks a version as complete and tested.
Required labels:
release - Marks this as a stable release
- Exactly one of:
major, minor, or patch - Specifies the version bump type
Process:
- Increments the version according to the bump type
- Updates copyright years in all source files
- Replaces
[Unreleased] section in CHANGELOG.md with the new version
- Creates a new
[Unreleased] section for future changes
- Creates a git tag and GitHub release
Example labels: release, minor → Version 0.3.0 becomes 0.4.0
Prerelease
A prerelease is a preview version for testing purposes before a stable release.
Required labels:
prerelease - Marks this as a prerelease
- Exactly one of:
major, minor, or patch - Specifies the base version bump type
- Exactly one of:
alpha, beta, or rc - Specifies the prerelease stage
Process:
- Increments the base version according to the bump type
- Finds existing prerelease tags for this version and increments the prerelease number
- Keeps the
[Unreleased] section in CHANGELOG.md
- Adds a new prerelease section above
[Unreleased]
- Creates a git tag and GitHub release marked as prerelease
Example labels: prerelease, minor, alpha → Version 0.3.0 becomes 0.4.0-alpha.1
Step-by-Step Release Instructions
1. Prepare Your Changes
Before creating a release, ensure:
- All changes are committed and pushed to a feature branch
- Tests pass in CI
- Documentation is updated
- CHANGELOG.md
[Unreleased] section lists all changes
2. Update CHANGELOG.md
Edit CHANGELOG.md to document changes under the [Unreleased] section:
## [Unreleased]
### Added
- New feature X that does Y
### Changed
- Modified behavior of Z
### Fixed
- Fixed bug in component A
Follow the Keep a Changelog format with these categories:
- Added - New features
- Changed - Changes to existing functionality
- Deprecated - Soon-to-be removed features
- Removed - Removed features
- Fixed - Bug fixes
- Security - Security fixes
3. Create Pull Request
Create a pull request to merge your changes to main:
git checkout -b feature/my-feature
git add -A
git commit -m "Add feature X"
git push origin feature/my-feature
4. Add Release Labels
Add the appropriate labels to the pull request:
For a patch release (0.3.0 → 0.3.1):
- Add labels:
release, patch
For a minor release (0.3.0 → 0.4.0):
- Add labels:
release, minor
For a major release (0.3.0 → 1.0.0):
- Add labels:
release, major
For an alpha prerelease (0.3.0 → 0.4.0-alpha.1):
- Add labels:
prerelease, minor, alpha
For a beta prerelease (0.4.0-alpha.2 → 0.4.0-beta.1):
- Add labels:
prerelease, minor, beta
For a release candidate (0.4.0-beta.2 → 0.4.0-rc.1):
- Add labels:
prerelease, minor, rc
5. Merge Pull Request
Once the PR is reviewed and approved:
- Merge the pull request to
main
- The release workflow will automatically trigger
- Monitor the workflow in the Actions tab
6. Verify Release
After the workflow completes:
- Check that the version was updated in
CMakeLists.txt
- Verify the git tag was created:
git tag -l
- View the GitHub Release at:
https://github.com/MDO-Standards/Philote-Cpp/releases
- Confirm the CHANGELOG.md was updated correctly
Label Validation Rules
The release workflow enforces these rules:
- Release type: Must have exactly one of
release or prerelease
- Version bump: Must have exactly one of
major, minor, or patch
- Prerelease type: If using
prerelease, must have exactly one of alpha, beta, or rc
- Stable releases: Cannot have
alpha, beta, or rc labels
- Prereleases: Must have a prerelease type label
If labels are invalid, the workflow will fail with a descriptive error message.
Prerelease Progression Example
A typical prerelease progression for version 1.0.0 might look like:
0.3.0 (current stable)
↓
1.0.0-alpha.1 (early testing)
↓
1.0.0-alpha.2 (more changes)
↓
1.0.0-beta.1 (feature complete)
↓
1.0.0-beta.2 (bug fixes)
↓
1.0.0-rc.1 (release candidate)
↓
1.0.0-rc.2 (final fixes)
↓
1.0.0 (stable release)
To create each version:
1.0.0-alpha.1: labels prerelease, major, alpha
1.0.0-alpha.2: labels prerelease, major, alpha (auto-increments to .2)
1.0.0-beta.1: labels prerelease, major, beta
1.0.0-rc.1: labels prerelease, major, rc
1.0.0: labels release, major
Automated Actions
Copyright Year Updates
For stable releases only, the workflow runs scripts/update_copyright.py to update copyright years in:
*.cpp files
*.h and *.hpp files
CMakeLists.txt files
The script updates copyright statements from:
Copyright 2022-2024 Christopher A. Lupp
to:
Copyright 2022-2025 Christopher A. Lupp
CHANGELOG.md Updates
The workflow automatically:
For stable releases:
- Replaces
## [Unreleased] with ## [VERSION] - DATE
- Creates a new
## [Unreleased] section at the top
- Updates version comparison links at the bottom
For prereleases:
- Keeps the
## [Unreleased] section
- Adds a new
## [VERSION] - DATE section below it
- Adds prerelease version comparison links
CMakeLists.txt Version Update
The workflow updates the VERSION in the root CMakeLists.txt:
project(Philote-Cpp
VERSION 0.4.0
# ...
)
Troubleshooting
Workflow fails with "Must have one of 'major', 'minor', or 'patch' label"
Solution: Add exactly one version bump label to the PR.
Workflow fails with "Cannot have both 'release' and 'prerelease' labels"
Solution: Remove one of the conflicting labels. Use only release OR prerelease.
Workflow fails with "Prerelease must have one of 'alpha', 'beta', or 'rc' label"
Solution: Add a prerelease type label when using the prerelease label.
Release was created with wrong version
If the release was created but has the wrong version:
- Delete the incorrect tag locally and remotely:
git tag -d vX.Y.Z
git push origin :refs/tags/vX.Y.Z
- Delete the GitHub Release from the releases page
- Manually update
CMakeLists.txt and CHANGELOG.md to correct versions
- Create a new PR with correct labels
Copyright years not updated
This is expected for prereleases. Copyright years are only updated for stable releases.
Manual Release (Emergency)
In case the automated workflow fails, you can create a release manually:
# 1. Update version in CMakeLists.txt
vim CMakeLists.txt
# 2. Update copyright years (stable releases only)
python scripts/update_copyright.py 2025
# 3. Update CHANGELOG.md
vim CHANGELOG.md
# 4. Commit changes
git add -A
git commit -m "chore: release version X.Y.Z"
git push origin main
# 5. Create and push tag
git tag -a vX.Y.Z -m "Release vX.Y.Z"
git push origin vX.Y.Z
# 6. Create GitHub Release manually via web interface
Best Practices
- Test prereleases: Always create alpha/beta/rc versions before stable releases
- Document changes: Keep CHANGELOG.md up to date as you develop
- Follow SemVer: Use major bumps for breaking changes, minor for features, patch for fixes
- Review labels: Double-check PR labels before merging
- Monitor workflow: Watch the Actions tab to ensure the release completes successfully
- Update docs: Ensure documentation reflects changes before releasing
Resources