Skip to main content

Versioning Strategy

This document describes how Hermodr is versioned, how version numbers are calculated automatically, and how releases are produced.


Semantic Versioning

Hermodr follows Semantic Versioning 2.0.0:

MAJOR.MINOR.PATCH[-pre-release]
SegmentChanges when…
MAJORA maintainer explicitly tags a new major baseline (e.g. v2.0.0)
MINORA maintainer explicitly tags a new minor baseline (e.g. v1.3.0)
PATCHEvery merge into main (automatic)

Automated Version Calculation — GitVersion

Version numbers are computed automatically by GitVersion from the git history. No manual version editing is required. The two inputs that drive the calculation are:

  1. The last git tag on main — establishes the baseline version (major and minor).
  2. The commit count since that tag — determines the patch number for pre-release builds.

Note: Commit message prefixes (feat:, fix:, etc.) do not influence the version number. They are used for readability and changelog generation only.

Conventional Commits — PR title format

PR titles should follow the Conventional Commits specification for changelog clarity, but they have no effect on the computed version.

PR title prefixPurpose
feat: / feat(<scope>):Documents a new feature in the changelog
fix: / fix(<scope>):Documents a bug fix
feat!: / BREAKING CHANGE:Documents a breaking change
docs:, chore:, ci:, test:, build:Maintenance; no changelog entry needed

Tip: The <scope> is optional and refers to the package or subsystem affected, e.g. publisher, schema, outbox.


Branch Version Labels

While work is in progress on a branch, GitVersion produces a pre-release version to identify CI builds. These are not published as public stable releases.

Branch typeNaming conventionExample branchVersion produced
mainmain1.2.3-pre.5
Feature / enhancement<issue-number>-<slug>123-add-dead-letter-channel1.2.3-alpha.2
Bug fixbugfix/<issue-number>-<slug>bugfix/99-fix-null-reference1.2.3-bugfix.1
Pull requestautoPR #421.2.3-pr.1

The number at the end of the pre-release tag is the commit count since the last stable tag on that branch.


Release Lifecycle

git tag v1.2.0


┌─────────────┐ merge (any) ┌──────────────┐ merge (any) ┌──────────────┐
│ main 1.2.0 │ ───────────────► │ 1.2.1-pre.1 │ ───────────────► │ 1.2.1-pre.2 │
└─────────────┘ └──────┬───────┘ └──────┬───────┘
│ CI → GitHub Packages │
│ git tag v1.2.1
│ ▼
│ ┌──────────────┐
│ │ 1.2.1 ✅ │ ← NuGet.org stable
│ └──────────────┘

when milestone features are ready:
git tag v1.3.0


┌──────────────┐
│ 1.3.0 ✅ │ ← NuGet.org stable
└──────────────┘

next merges → 1.3.1-pre.1, …

Pre-release packages (GitHub Packages)

Every merge into main triggers a CI workflow that:

  1. Runs GitVersion to compute the new pre-release version (e.g. 1.2.3-pre.5).
  2. Builds and packs all NuGet packages with that version.
  3. Publishes the packages to the GitHub Packages feed for this repository.

Pre-release packages are intended for integration testing and early feedback. They are not considered stable.

Preview packages (NuGet.org pre-release)

When a milestone is approaching readiness, a maintainer may publish a public preview to NuGet.org by pushing a preview tag:

git tag v1.3.0-preview.1
git push origin v1.3.0-preview.1

This allows early adopters to test against a stable-ish snapshot without consuming the full GitHub Packages feed.

Stable releases (NuGet.org)

A stable release is produced by pushing a git tag to main:

git tag v1.3.0
git push origin v1.3.0

The tag triggers a separate CI workflow that:

  1. Verifies GitVersion computes the clean version 1.3.0 (no pre-release suffix).
  2. Builds, packs, signs, and publishes all packages to NuGet.org.
  3. Creates a GitHub Release with auto-generated release notes.

Tags must follow the v{MAJOR}.{MINOR}.{PATCH} format (e.g. v1.3.0). Do not tag commits on feature or bugfix branches.


Version Control Rules by Segment

SegmentWho controls itHow
PATCHAutomatic (GitVersion)Increments on every merge to main since the last tag
MINORMaintainerPush a vX.Y.0 tag when all planned milestone features are merged and stable
MAJORMaintainerPush a vX.0.0 tag when breaking changes are ready to release

Milestone Planning vs. Published Version

Milestone labels (e.g. "1.3.0") are planning intent, not published version truth.

  • Features planned for milestone 1.3.0 are merged to main as regular patches.
  • Before the tag v1.3.0 is pushed, those features ship as 1.2.x-pre.* on GitHub Packages.
  • The stable version 1.3.0 exists for consumers only after the maintainer pushes git tag v1.3.0.

Examples

Bug fix: 1.2.01.2.1

main is at v1.2.0

1. Open issue #99: "Null reference when payload is empty"
2. Create branch: bugfix/99-fix-null-reference
3. Fix + regression test
4. PR title: fix(publisher): resolve null reference on empty payload
5. PR merged → GitVersion computes 1.2.1-pre.1 → published to GitHub Packages
6. Maintainer pushes git tag v1.2.1 → 1.2.1 published to NuGet.org

Feature milestone: 1.2.x1.3.0

main is at v1.2.0, milestone "1.3.0" has multiple features planned

1. Features are implemented across multiple PRs and merged to main
2. During development, CI publishes: 1.2.1-pre.1, 1.2.1-pre.2, ...
3. When all milestone features are merged and validated:
4. Maintainer pushes git tag v1.3.0 → 1.3.0 published to NuGet.org
5. Next merge to main → 1.3.1-pre.1

Breaking change: 1.x2.0.0

main is at v1.x, breaking changes are implemented across one or more PRs

1. Breaking change PRs are merged to main (title: feat!: ... or any prefix)
2. CI publishes 1.2.x-pre.* builds to GitHub Packages during development
3. When all v2 breaking changes are merged and validated:
4. Maintainer pushes git tag v2.0.0 → 2.0.0 published to NuGet.org

Consuming Pre-release Packages

To consume a pre-release build from GitHub Packages, add the feed to your NuGet.config:

<configuration>
<packageSources>
<add key="deveel-events-preview"
value="https://nuget.pkg.github.com/deveel/index.json" />
</packageSources>
</configuration>

You will need a GitHub Personal Access Token (PAT) with read:packages scope configured as a credential for that source.