Skip to main content
Version: 4.0.0

Release Process

This is the canonical guide for releasing a new version of the Pyx Identity Resolver. It covers versioning, the step-by-step release workflow, documentation versioning, hotfixes, and troubleshooting.

The repository follows trunk-based development on master; releases are cut by tagging from master. There is no next branch, no release/* branch, and no hotfix/* branch convention.

For the maintainer-facing summary, see also RELEASE_MANAGEMENT_GUIDE.md in the repository root.

Versioning scheme

The project follows Semantic Versioning using the format MAJOR.MINOR.PATCH:

IncrementWhen to useExample
MAJORIncompatible or breaking changes3.0.1 to 4.0.0
MINORNew features, backward-compatible4.0.0 to 4.1.0
PATCHBackward-compatible bug fixes4.1.0 to 4.1.1

Pre-release identifiers may be appended for staged rollouts:

  • vX.Y.Z-rc.N for release candidates.
  • vX.Y.Z-alpha.N and -beta.N for earlier-stage previews.
  • vX.Y.Z-pre.N for ad-hoc pre-release builds.

Pre-release tags push the semver-tagged Docker image but do not move the :latest pointer, so a pre-release does not become the default pull target.

API version alignment

version.json records three version fields that move on different cadences:

FieldFormatBumps when
versionsemver MAJOR.MINOR.PATCHEvery release; matches the git tag and app/package.json.
apiVersionMAJOR.MINORThe API contract changes. MAJOR drives the URL path (/api/v<MAJOR>); MINOR documents backwards-compatible additions.
docVersionsemver MAJOR.MINOR.PATCHUser-visible documentation changes ship.

The MAJOR of apiVersion is in lockstep with the URL path. A backwards-compatible API addition bumps apiVersion MINOR but does not change the URL. A breaking API change bumps both apiVersion MAJOR and the repository version MAJOR together. See ADR 002 for the rationale.

Cutting a regular release

  1. Open a chore/release-vX.Y.Z branch from master.

  2. Bump the relevant fields in version.json (using the table above) and the version field in app/package.json so it matches version.json.

  3. Add the human-facing entry to RELEASE_NOTES.md (operator's summary of what changed and why) and the technical entry to CHANGELOG.md (engineering changelog).

  4. Open a PR titled chore(release): vX.Y.Z. Get it reviewed and merge to master.

  5. From a fresh checkout of master at the merge commit, tag and push:

    git tag vX.Y.Z
    git push origin vX.Y.Z
  6. The docker.yml pipeline picks up the tag push and builds the release image at ghcr.io/pyx-industries/pyx-identity-resolver:X.Y.Z. The :latest pointer moves to the new image (non-pre-release tags only).

Cutting a pre-release

Same as a regular release, with the pre-release suffix on the tag:

git tag vX.Y.Z-rc.1
git push origin vX.Y.Z-rc.1

The pipeline pushes ghcr.io/pyx-industries/pyx-identity-resolver:X.Y.Z-rc.1 and does not move :latest. Operators opt in to the pre-release by pulling the specific tag.

Cutting a hotfix

  1. Branch from the affected release tag:

    git checkout -b fix/<short-description> vX.Y.Z
  2. Apply the fix, open a PR targeting master, get it reviewed, merge.

  3. From the merge commit, tag and push the new patch:

    git tag vX.Y.Z+1
    git push origin vX.Y.Z+1

If the hotfix is also relevant to older release lines, cherry-pick the commit onto branches based at those tags and tag patch releases there too.

Documentation versioning

docVersion in version.json tracks the Docusaurus snapshot version independently of the service version. Generate a new snapshot when shipping user-visible documentation changes by running the docs site's own versioning command and bumping docVersion.

Troubleshooting

SymptomLikely causeResolution
Tag push didn't trigger the docker pipelineTag pattern mismatchConfirm the tag matches v*.
:latest didn't move after a tag pushTag is a pre-release (contains -rc, -alpha, -beta, or -pre)Expected. Pre-releases never move :latest.
Two versions in version.json and app/package.json disagreeBump step missed a fileRe-run the release-prep PR with both files in sync.
URL path didn't change after a MINOR API bumpExpected behaviourThe URL path is derived from apiVersion MAJOR only. MINOR bumps document additions without changing routes.