Branching and Versioning Strategy¶
This document covers branch naming, PR rules, version format, and the release workflow for contributors.
Branch Model¶
| Branch | Purpose | PR required? | Approval required? |
|---|---|---|---|
main |
Stable trunk. Never commit directly. | Yes | Yes (at least 1) |
feature/<name> |
All feature work. Created from main. Merged back to main via PR. |
Yes | No |
bugfixes/<name> |
Bug fixes. Branch from main for pre-release fixes; branch from a release tag for hotfixes. |
Yes (to main) |
No |
Rules:
feature/branches must be branched offmain.bugfixes/branches branch frommainfor pre-release fixes. For hotfixes to an already-released version, branch from the release tag:git checkout -b bugfixes/fix-foo v5.2.0.- Hotfix commits must be cherry-picked back to
mainvia a separate PR so the fix is included in future releases. - All merges to
maingo through a Pull Request — no direct pushes. - Branch names: lowercase, hyphens. Examples:
feature/risk-score-export,bugfixes/fix-login-redirect. - One issue per branch. Each branch fixes exactly one issue or implements exactly one feature. Never combine unrelated fixes into a single branch or PR.
Starting New Work¶
# Feature or pre-release bugfix — branch from main
git checkout main && git pull
git checkout -b feature/<name>
# or
git checkout -b bugfixes/<name>
# Hotfix to a released version — branch from the release tag, NOT from main
git checkout -b bugfixes/<name> v5.2.0
Version Number Format¶
| Context | Format | Example |
|---|---|---|
main dev builds (:edge) |
Major.Minor.yyyyMMdd.HHmm |
5.3.20260419.1430 |
Pre-release tags (:beta) |
Major.Minor.Patch-beta.N |
5.3.0-beta.1 |
Release tags (:latest) |
Major.Minor.Patch.0 |
5.2.1.0 |
Who updates what:
| Action | Who | When |
|---|---|---|
Minor bump + timestamp |
bump-version.yml GitHub Action |
Automatically on every PR merge to main |
| Pre-release version | cut-beta.yml GitHub Action |
When you run Actions → Cut Beta |
| Release version | cut-release.yml GitHub Action |
When you run Actions → Cut Release |
| Hotfix version | cut-hotfix.yml GitHub Action |
When you run Actions → Cut Hotfix |
Major bump |
Developer, via PR to main |
Only for breaking changes |
| Branch work | Nobody | Never touch setup/IdentityAtlas.psd1 on a branch |
Changelog Fragments¶
Every feature/ or bugfixes/ branch must include a changelog fragment. Never edit CHANGES.md directly — the bump-version.yml CI action merges all fragments on PR merge.
File: changes/<descriptive-name>.md (e.g. changes/fix-login-redirect.md)
Format:
- Fixed the login redirect when auth is enabled and no session exists
- Improved error message when tenant ID is missing
Write in user-facing language. One bullet per functional change. Add the file alongside the code change — don't batch at the end.
Merging to Main (via PR)¶
- Open a PR from
feature/<name>orbugfixes/<name>intomain. - Use the changelog fragment content as the PR description body.
- Requires 1 approval and passing CI.
- After merge,
bump-version.ymlautomatically incrementsMinor, updates the timestamp, and merges allchanges/*.mdfragments intoCHANGES.md. Thedocker-publish.ymlaction then builds and pushes Docker images tagged:edge.
Cutting a Pre-Release (Beta / RC)¶
To publish a build for testers without touching :latest:
- Go to Actions → Cut Beta → Run workflow
- Enter the version:
Major.Minor.Patch-beta.N(e.g.5.3.0-beta.1,5.3.0-rc.1,5.3.0-alpha.1) - The workflow creates the tag on the current
mainHEAD (or on the optional ref input — any branch, tag or commit) docker-publish.ymlbuilds:beta+:5.3.0-beta.1
Users on docker-compose.prod.yml tracking :latest are not affected.
Cutting a Release¶
The body of the GitHub release — changelog, baseline comparison and the dependency-update list — is assembled by the workflow. See How a release's notes are assembled.
When main is stable and ready to ship to customers:
- Go to Actions → Cut Release → Run workflow
- Enter the version:
Major.Minor.Patch(e.g.5.2.0) - The workflow creates tag
v5.2.0on the currentmainHEAD (or on the optional ref input — any branch, tag or commit) docker-publish.ymltriggers automatically on the tag push and builds:latest+:5.2.0.0
Customers who track :latest will receive the new version on their next docker compose pull.
Hotfix Releases¶
To ship a bugfix without including features already on main:
# 1. Branch from the release tag — NOT from main
git checkout -b bugfixes/fix-login-crash v5.2.0
# 2. Fix the bug, commit, push
git push origin bugfixes/fix-login-crash
- Go to Actions → Cut Hotfix → Run workflow
- Enter the branch name (
bugfixes/fix-login-crash) and new version (5.2.1) - The workflow creates tag
v5.2.1on the HEAD of your branch docker-publish.ymlbuilds:latest+:5.2.1.0
To cut a patch from a long-lived release branch such as release/5.9, dispatch the workflow from that branch so the branch's own copy runs. main's copy calls scripts an older branch does not have, and stops before tagging when it finds them missing:
After the hotfix ships, open a PR to cherry-pick the fix into main:
git checkout main && git pull
git cherry-pick <fix-commit-sha>
gh pr create --base main --title "fix: cherry-pick hotfix from v5.2.1"
Stacked PRs¶
For larger features, break the work into a stack of small focused PRs. Each PR targets the previous branch in the stack:
# Step 1 — targets main
git checkout -b feature/foo-step-1
gh pr create --base main --title "step 1: ..."
# Step 2 — stacked on step 1
git checkout -b feature/foo-step-2
gh pr create --base feature/foo-step-1 --title "step 2: ..."
When a bottom PR merges, retarget the next one: gh pr edit <number> --base main.
Image Channels¶
| Tag | Published when | Who uses it |
|---|---|---|
:latest |
A release tag (v5.2.0) is created via Actions → Cut Release or Cut Hotfix |
Customers (default) |
:beta |
A pre-release tag is created via Actions → Cut Beta | Beta testers |
:edge |
Every PR merges to main |
Developers and testers |
:5.2.0.0 |
Same time as :latest — exact pinned version |
Production deployments needing controlled upgrades |
:5.3.0-beta.1 |
Same time as :beta — exact pre-release version |
Testers who want a reproducible pre-release build |
See Docker Setup for how to select a channel via IMAGE_TAG.
Repository Rulesets¶
Branch protection is configured via two GitHub rulesets. If you ever need to recreate them (e.g. after transferring the repo), do so manually in Settings → Rules → Rulesets.
Protect main¶
| Setting | Value |
|---|---|
| Target | main |
| Merge methods | Squash only |
| Required approvals | 1 + code owner review |
| Dismiss stale reviews | Yes |
| Required status checks | PR Summary (strict) |
| Code scanning | CodeQL — errors threshold, high/higher security alerts |
| Deletion | Blocked |
| Force push | Blocked |
Bypass actors:
| Actor | Type | Mode | Why |
|---|---|---|---|
| Fortigi CI bot | Integration | Always | Pushes automated version bump commits on PR merge |
| IdentityAtlas-Owners | Team | Pull request only | Allows team members to merge without a second approval |
Protect gh-pages¶
| Setting | Value |
|---|---|
| Target | gh-pages |
| Deletion | Blocked |
| Force push | Blocked |
No bypass actors — mike (the docs versioning tool) only does regular fast-forward pushes and never needs to delete or force-push the branch.