Universal Library Release Process
This document describes the release process for the Universal Numbers Library (v4+). The process uses conventional commits, git tags, and git-cliff for changelog generation.
Overview
Section titled “Overview”Development and releasing are fully decoupled. No files on main are modified during a release — you simply push an annotated tag and CI handles the rest. This eliminates rebase cascades for in-flight branches.
feature branch → PR (squash merge) → main → git tag → GitHub Release + DockerConventional Commits
Section titled “Conventional Commits”All PR titles must follow the Conventional Commits format. This is enforced by CI on every pull request.
Format
Section titled “Format”<type>(<optional scope>): <description>| Type | Purpose | Changelog Section |
|---|---|---|
feat | New feature | Added |
fix | Bug fix | Fixed |
perf | Performance improvement | Performance |
refactor | Code change (no new feature/fix) | Changed |
docs | Documentation only | Documentation |
test | Adding or fixing tests | Testing |
build | Build system changes | Build System |
ci | CI/CD changes | CI/CD |
style | Formatting, no logic change | Styling |
chore | Maintenance, deps, etc. | Miscellaneous |
Scopes (optional)
Section titled “Scopes (optional)”Number systems: posit, cfloat, fixpnt, lns, integer, areal, valid, bfloat16, dfloat, hfloat, dd, qd, dd_cascade, td_cascade, qd_cascade, einteger, edecimal, erational, efloat, ereal
Building blocks: blockbinary, blockdecimal, blocksignificant, blocktriple, microfloat, e8m0, mxblock, nvblock
Infrastructure: blas, math, numeric, cmake, ci, docker, deps
Breaking Changes
Section titled “Breaking Changes”Append ! after the type to signal a breaking change:
feat!: redesign number system APIfeat(posit)!: change template parameter orderExamples
Section titled “Examples”feat(dfloat): add decimal256 significand handlingfix(posit): correct rounding for subnormal conversiondocs: update installation instructionstest(cfloat): add exhaustive 8-bit verificationci: add RISC-V cross-compilation targetrefactor(blockbinary): simplify division algorithmperf(blas): vectorize dot product inner loopchore: update fallback version to 4.1.0Day-to-Day Development
Section titled “Day-to-Day Development”1. Create a Feature Branch
Section titled “1. Create a Feature Branch”git checkout main && git pullgit checkout -b feat/decimal256Branch naming is flexible — use descriptive names like feat/decimal256, fix/posit-rounding, docs/api-guide.
2. Develop and Commit
Section titled “2. Develop and Commit”Use conventional commit messages:
git commit -m 'feat(dfloat): add decimal256 significand handling'git commit -m 'test(dfloat): add decimal256 regression tests'git commit -m 'fix(dfloat): correct overflow in wide multiply'3. Create a Pull Request
Section titled “3. Create a Pull Request”git push -u origin feat/decimal256gh pr create --title 'feat(dfloat): add decimal256 support' --body '...'The PR title must follow conventional commit format — CI will reject it otherwise.
4. Merge
Section titled “4. Merge”Use squash merge on GitHub. The PR title becomes the single commit message on main, which feeds directly into the changelog.
5. Clean Up
Section titled “5. Clean Up”git checkout main && git pullgit branch -d feat/decimal256Creating a Release
Section titled “Creating a Release”1. Ensure main is ready
Section titled “1. Ensure main is ready”git checkout main && git pullVerify CI is green on the latest commit.
2. Preview the changelog (optional)
Section titled “2. Preview the changelog (optional)”git cliff --unreleased3. Choose a version
Section titled “3. Choose a version”Follow Semantic Versioning:
| Change Type | Version Bump | Example |
|---|---|---|
| Bug fixes only | Patch | v4.0.0 → v4.0.1 |
| New features (backward compatible) | Minor | v4.0.1 → v4.1.0 |
| Breaking changes | Major | v4.1.0 → v5.0.0 |
You decide the version when creating the tag. Conventional commit types are a guide, not automated enforcement.
4. Tag and push
Section titled “4. Tag and push”git tag -a v4.1.0 -m 'feat: mixed-precision SDK and block format benchmarks'git push origin v4.1.05. CI handles the rest
Section titled “5. CI handles the rest”The release.yml workflow triggers on the tag push and automatically:
- Generates release notes from commit history using git-cliff
- Creates a GitHub Release with the changelog
- Builds and pushes a Docker image to Docker Hub (
stillwater/universal)
Version Resolution
Section titled “Version Resolution”The library version is determined at CMake configure time:
| Context | Source | Example |
|---|---|---|
| Git checkout | git describe --tags --abbrev=0 | v4.1.0 → 4.1.0 |
Source tarball (no .git) | Fallback in CMakeLists.txt | 4.0.0 |
| Manual override | -DUNIVERSAL_VERSION_MAJOR=X ... | Any version |
The fallback version in CMakeLists.txt should be updated after major releases.
CI Workflows
Section titled “CI Workflows”| Workflow | Trigger | Purpose |
|---|---|---|
cmake.yml | Push to main, PRs | Build matrix (GCC, Clang, MSVC, macOS, ARM64, RISC-V, MinGW) |
conventional-commits.yml | PR open/edit | Validates PR title format |
release.yml | Tag push (v*) | Creates GitHub Release + Docker image |
full-regression.yml | Manual | Complete test suite before release |
sanitizers.yml | Weekly, PRs | ASan/UBSan testing |
Changelog Generation
Section titled “Changelog Generation”Changelogs are generated by git-cliff using the configuration in cliff.toml. The output groups commits by type and includes scope, PR number, and author:
## [4.1.0] - 2026-02-28
### Added- **dfloat**: portable blockbinary storage and string I/O (#524) by @Ravenwater
### Fixed- **posit**: correct subnormal rounding edge case (#525) by @contributor
### Documentation- update installation guide (#526) by @contributorUseful local commands:
git cliff --unreleased # Preview unreleased changesgit cliff --latest # Show latest release notesgit cliff v4.0.0..v4.1.0 # Between two tagsgit cliff -o CHANGELOG.md # Generate full changelog fileInstall git-cliff via cargo install git-cliff or brew install git-cliff.
Troubleshooting
Section titled “Troubleshooting”Release not created after tag push
Section titled “Release not created after tag push”- Check the Actions tab for the
release.ymlworkflow run - Verify the tag matches the pattern
v[0-9]* - Ensure the tag is annotated (
git tag -a), not lightweight
Tag already exists
Section titled “Tag already exists”# Delete and recreategit tag -d v4.1.0git push origin :refs/tags/v4.1.0git tag -a v4.1.0 -m 'feat: release description'git push origin v4.1.0Manual release (fallback)
Section titled “Manual release (fallback)”gh release create v4.1.0 --title "Universal v4.1.0" --generate-notesPR title rejected by CI
Section titled “PR title rejected by CI”Check that the title follows <type>(<scope>): <description> format. The type must be one of the 10 allowed types. The scope (if used) must be one of the allowed scopes listed above.