|
| 1 | +# Conventional Commit Workflow |
| 2 | + |
| 3 | +This document describes the development and release workflow for the Universal Numbers Library using conventional commits, git-cliff, and tag-based releases. |
| 4 | + |
| 5 | +## Overview |
| 6 | + |
| 7 | +The workflow separates **development** (branches + conventional commits) from **releasing** (git tags). No files on main are modified during a release, so in-flight branches never need to rebase due to version bumps. |
| 8 | + |
| 9 | +## Conventional Commit Format |
| 10 | + |
| 11 | +Every commit message (and PR title) follows this format: |
| 12 | + |
| 13 | +``` |
| 14 | +<type>(<optional scope>): <description> |
| 15 | +``` |
| 16 | + |
| 17 | +### Types |
| 18 | + |
| 19 | +| Type | Meaning | Example | |
| 20 | +|------------|----------------------------------|----------------------------------------------------| |
| 21 | +| `feat` | New feature | `feat(dfloat): add decimal256 support` | |
| 22 | +| `fix` | Bug fix | `fix(posit): correct subnormal rounding` | |
| 23 | +| `docs` | Documentation only | `docs: update build instructions` | |
| 24 | +| `style` | Formatting, no logic change | `style(cfloat): fix indentation` | |
| 25 | +| `refactor` | Code change, no new feature/fix | `refactor(lns): simplify encoding logic` | |
| 26 | +| `perf` | Performance improvement | `perf(blas): vectorize dot product` | |
| 27 | +| `test` | Adding or fixing tests | `test(fixpnt): add overflow edge cases` | |
| 28 | +| `build` | Build system changes | `build(cmake): add AVX-512 detection` | |
| 29 | +| `ci` | CI/CD changes | `ci: add clang-18 to test matrix` | |
| 30 | +| `chore` | Maintenance, deps, etc. | `chore: update docker base image` | |
| 31 | + |
| 32 | +### Scopes |
| 33 | + |
| 34 | +Scopes are optional but recommended. Use the number system name or infrastructure area: |
| 35 | + |
| 36 | +`posit`, `cfloat`, `fixpnt`, `lns`, `integer`, `dfloat`, `hfloat`, `dd`, `qd`, |
| 37 | +`bfloat16`, `areal`, `valid`, `einteger`, `edecimal`, `erational`, `efloat`, `ereal`, |
| 38 | +`blockbinary`, `microfloat`, `e8m0`, `mxblock`, `nvblock`, |
| 39 | +`blas`, `math`, `numeric`, `cmake`, `ci`, `docker`, `deps` |
| 40 | + |
| 41 | +### Breaking Changes |
| 42 | + |
| 43 | +Append `!` after the type to signal a breaking change: |
| 44 | + |
| 45 | +``` |
| 46 | +feat!: redesign number system API |
| 47 | +feat(posit)!: change template parameter order |
| 48 | +``` |
| 49 | + |
| 50 | +This indicates a major version bump when releasing. |
| 51 | + |
| 52 | +## Day-to-Day Development |
| 53 | + |
| 54 | +### 1. Create a feature branch |
| 55 | + |
| 56 | +```bash |
| 57 | +git checkout main && git pull |
| 58 | +git checkout -b feat/decimal256 |
| 59 | +``` |
| 60 | + |
| 61 | +Branch naming convention: `feat/`, `fix/`, `refactor/`, `docs/`, etc. |
| 62 | + |
| 63 | +### 2. Work and commit |
| 64 | + |
| 65 | +Use conventional commit messages for your commits: |
| 66 | + |
| 67 | +```bash |
| 68 | +git commit -m 'feat(dfloat): add decimal256 significand handling' |
| 69 | +git commit -m 'test(dfloat): add decimal256 regression tests' |
| 70 | +git commit -m 'fix(dfloat): correct overflow in wide multiply' |
| 71 | +``` |
| 72 | + |
| 73 | +### 3. Push and create a PR |
| 74 | + |
| 75 | +```bash |
| 76 | +git push -u origin feat/decimal256 |
| 77 | +gh pr create --title 'feat(dfloat): add decimal256 support' |
| 78 | +``` |
| 79 | + |
| 80 | +The PR title must follow conventional commit format. The `conventional-commits.yml` workflow validates this automatically. |
| 81 | + |
| 82 | +### 4. Merge |
| 83 | + |
| 84 | +Use **squash merge** on GitHub. The PR title becomes the commit message on main. This keeps main's history clean with one conventional commit per PR. |
| 85 | + |
| 86 | +### 5. Clean up |
| 87 | + |
| 88 | +```bash |
| 89 | +git checkout main && git pull |
| 90 | +git branch -d feat/decimal256 |
| 91 | +git push origin --delete feat/decimal256 |
| 92 | +``` |
| 93 | + |
| 94 | +## Releasing |
| 95 | + |
| 96 | +Releases are **decoupled** from development. You decide when to release. Commits accumulate on main and are grouped into a release when you push a tag. |
| 97 | + |
| 98 | +### 1. Ensure main is ready |
| 99 | + |
| 100 | +```bash |
| 101 | +git checkout main && git pull |
| 102 | +``` |
| 103 | + |
| 104 | +Verify CI is green on the latest main commit. |
| 105 | + |
| 106 | +### 2. Preview the changelog (optional) |
| 107 | + |
| 108 | +```bash |
| 109 | +# See what will be in the release notes |
| 110 | +git cliff --unreleased |
| 111 | +``` |
| 112 | + |
| 113 | +### 3. Tag the release |
| 114 | + |
| 115 | +Use **single quotes** (bash interprets `!` in double quotes as history expansion): |
| 116 | + |
| 117 | +```bash |
| 118 | +# Patch release (bug fixes only) |
| 119 | +git tag -a v4.0.1 -m 'fix: patch release description' |
| 120 | + |
| 121 | +# Minor release (new features, backward compatible) |
| 122 | +git tag -a v4.1.0 -m 'feat: minor release description' |
| 123 | + |
| 124 | +# Major release (breaking changes) |
| 125 | +git tag -a v5.0.0 -m 'feat!: major release description' |
| 126 | +``` |
| 127 | + |
| 128 | +### 4. Push the tag |
| 129 | + |
| 130 | +```bash |
| 131 | +git push origin v4.1.0 |
| 132 | +``` |
| 133 | + |
| 134 | +### 5. CI does the rest |
| 135 | + |
| 136 | +The `release.yml` workflow triggers on the tag push and: |
| 137 | + |
| 138 | +1. Generates release notes with git-cliff from all conventional commits since the previous tag |
| 139 | +2. Creates a GitHub Release with the structured changelog |
| 140 | +3. Builds and pushes the Docker image to Docker Hub |
| 141 | + |
| 142 | +No files on main are modified. No rebase cascade for in-flight branches. |
| 143 | + |
| 144 | +## Version Resolution |
| 145 | + |
| 146 | +CMakeLists.txt derives the version from the most recent git tag using `git describe --tags`. This means: |
| 147 | + |
| 148 | +- **In a git checkout**: version comes from the tag automatically |
| 149 | +- **From a source tarball** (no `.git`): falls back to the hardcoded `UNIVERSAL_FALLBACK_VERSION_*` variables in CMakeLists.txt |
| 150 | +- **Override**: pass `-DUNIVERSAL_VERSION_MAJOR=X -DUNIVERSAL_VERSION_MINOR=Y -DUNIVERSAL_VERSION_PATCH=Z` to cmake |
| 151 | + |
| 152 | +The fallback version should be updated periodically in a normal commit (e.g., after a major release). |
| 153 | + |
| 154 | +## Versioning Policy |
| 155 | + |
| 156 | +The project follows [Semantic Versioning](https://semver.org/): |
| 157 | + |
| 158 | +- **MAJOR** (`feat!:` or `BREAKING CHANGE:` footer): incompatible API changes |
| 159 | +- **MINOR** (`feat:`): new functionality, backward compatible |
| 160 | +- **PATCH** (`fix:`): bug fixes, backward compatible |
| 161 | + |
| 162 | +You decide the version number when you create the tag. The conventional commit types are a guide, not an automated enforcement. |
| 163 | + |
| 164 | +## Tools |
| 165 | + |
| 166 | +| Tool | Purpose | Config File | |
| 167 | +|------|---------|-------------| |
| 168 | +| [git-cliff](https://git-cliff.org/) | Changelog generation from conventional commits | `cliff.toml` | |
| 169 | +| [amannn/action-semantic-pull-request](https://github.com/amannn/action-semantic-pull-request) | PR title linting | `.github/workflows/conventional-commits.yml` | |
| 170 | + |
| 171 | +### Installing git-cliff locally |
| 172 | + |
| 173 | +```bash |
| 174 | +# Via cargo |
| 175 | +cargo install git-cliff |
| 176 | + |
| 177 | +# Via brew (macOS/Linux) |
| 178 | +brew install git-cliff |
| 179 | +``` |
| 180 | + |
| 181 | +### Useful git-cliff commands |
| 182 | + |
| 183 | +```bash |
| 184 | +# Preview unreleased changes (since last tag) |
| 185 | +git cliff --unreleased |
| 186 | + |
| 187 | +# Generate full changelog |
| 188 | +git cliff -o CHANGELOG.md |
| 189 | + |
| 190 | +# Show changes between two tags |
| 191 | +git cliff v3.105.2..v4.0.0 |
| 192 | + |
| 193 | +# Show latest release only |
| 194 | +git cliff --latest |
| 195 | +``` |
| 196 | + |
| 197 | +## Quick Reference |
| 198 | + |
| 199 | +``` |
| 200 | +Development: |
| 201 | + git checkout -b feat/thing # branch |
| 202 | + git commit -m 'feat(x): ...' # conventional commits |
| 203 | + gh pr create --title 'feat(x): ..' # PR with conventional title |
| 204 | + squash merge # clean commit on main |
| 205 | +
|
| 206 | +Releasing: |
| 207 | + git cliff --unreleased # preview changelog |
| 208 | + git tag -a v4.1.0 -m 'description' # tag (use single quotes!) |
| 209 | + git push origin v4.1.0 # push tag -> CI creates release |
| 210 | +
|
| 211 | +Branch cleanup: |
| 212 | + git branch -d feat/thing # delete local |
| 213 | + git push origin --delete feat/thing # delete remote |
| 214 | +``` |
0 commit comments