Skip to content

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 off main.
  • bugfixes/ branches branch from main for 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 main via a separate PR so the fix is included in future releases.
  • All merges to main go 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)

  1. Open a PR from feature/<name> or bugfixes/<name> into main.
  2. Use the changelog fragment content as the PR description body.
  3. Requires 1 approval and passing CI.
  4. After merge, bump-version.yml automatically increments Minor, updates the timestamp, and merges all changes/*.md fragments into CHANGES.md. The docker-publish.yml action then builds and pushes Docker images tagged :edge.

Cutting a Pre-Release (Beta / RC)

To publish a build for testers without touching :latest:

  1. Go to Actions → Cut Beta → Run workflow
  2. 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)
  3. The workflow creates the tag on the current main HEAD
  4. docker-publish.yml builds :beta + :5.3.0-beta.1

Users on docker-compose.prod.yml tracking :latest are not affected.

Cutting a Release

When main is stable and ready to ship to customers:

  1. Go to Actions → Cut Release → Run workflow
  2. Enter the version: Major.Minor.Patch (e.g. 5.2.0)
  3. The workflow creates tag v5.2.0 on the current main HEAD
  4. docker-publish.yml triggers 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
  1. Go to Actions → Cut Hotfix → Run workflow
  2. Enter the branch name (bugfixes/fix-login-crash) and new version (5.2.1)
  3. The workflow creates tag v5.2.1 on the HEAD of your branch
  4. docker-publish.yml builds :latest + :5.2.1.0

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.