Skip to content
SENS Platform Docs

Platform Release Version

This contract defines one deliberate SemVer cycle from local development through test and Production. It prevents a released base version from producing new alpha images and prevents image tags, runtime configuration, and /version from describing different releases.

Environment or purposeRequired formatExample
Root base versionMAJOR.MINOR.PATCH0.1.2
LocalMAJOR.MINOR.PATCH-local.00.1.2-local.0
TestMAJOR.MINOR.PATCH-alpha.RUN.ATTEMPT0.1.2-alpha.2.1
StagingMAJOR.MINOR.PATCH-rc.NUMBER0.1.2-rc.1
ProductionMAJOR.MINOR.PATCH0.1.2

Build metadata introduced with + is forbidden because it cannot be preserved unchanged in a Docker tag. Numeric pre-release identifiers have no leading zeroes. RUN, ATTEMPT, and NUMBER are positive integers.

RUN is the one-based position of the ci workflow run on main inside the stable root-version line. A new MAJOR.MINOR.PATCH base therefore starts at RUN=1. The final CI job derives that position from the bounded GitHub Actions history and the stable package.json version at each Main commit; it does not depend on a mutable counter. ATTEMPT uses GITHUB_RUN_ATTEMPT. Failed or cancelled runs may leave gaps, and retrying the same CI run creates a new immutable alpha tag with the same RUN and the next ATTEMPT.

Three values describe the release state:

  1. root package.json is the stable base of the prepared test and Production release line;
  2. sens-platform-infra/environments/production/release.yaml is the desired Production version in GitOps;
  3. https://api.iot-sens.schlossers.at/version is the version currently served by the live Production platform-api.

Use pnpm version:status to read and validate all three without changing any file. Failure to read GitHub or the live API, malformed YAML or JSON, a pre-release value in Production, or invalid SemVer fails closed.

A released state has the same stable version in all three sources. A human then chooses major, minor, or patch. The resulting version PR changes only the root package.json and opens the next alpha line:

Released 0.1.1
-> choose patch
Prepared base 0.1.2
-> 0.1.2-alpha.1.1, 0.1.2-alpha.2.1, ...
-> Ops portal prod-release
Production 0.1.2
-> alpha publication blocked until the next version is chosen

Before Production promotion, a human may reclassify an open line upward. The new target is always calculated from the aligned GitOps/live Production baseline, not by incrementing the already prepared base:

Production 0.1.2 + prepared patch 0.1.3
-> choose minor: 0.2.0
-> choose major: 1.0.0

The same or a lower SemVer class is rejected. Reclassification is also blocked while GitOps and the live API differ, because that state can represent an in-progress promotion or rollback.

Automatic test publication requires the root base version to be greater than both GitOps Production and live Production. GitOps and the live API may differ briefly during a Production sync; the prepared base must still be newer than both. The controller’s stable-image workflow dispatch is not subject to this alpha guard. It uses the supplied stable tag and does not read the CI workflow history or increment the alpha run.

Alpha tags published before the release-local counter was introduced remain immutable and valid. New release lines use the main CI history and start visibly at alpha.1.1; no historical tag is renamed or overwritten.

The next version is deterministic:

  • patch increments Patch exactly once;
  • minor increments Minor exactly once and resets Patch to zero;
  • major increments Major exactly once and resets Minor and Patch to zero.

For example, major from 0.1.1 produces 1.0.0. Downgrades and skipped versions such as 0.1.1 to 0.1.5 are invalid. An unchanged version remains valid for ordinary feature pull requests.

Run a local bump only after selecting its type explicitly:

Terminal window
pnpm version:status
pnpm version:next patch

The GitHub Actions workflow Prepare next version performs the same check and opens a review PR. It never merges the PR. At most one open branch with prefix prepare-version- may have an active version PR.

For platform-api, the Helm value services.platformApi.image.tag supplies both the container image tag and SENS_PLATFORM_VERSION. The configured value must match the runtime environment’s release channel. GET /version returns that exact value without transformation.

Deployment and smoke checks must therefore preserve this equality:

platform-api image tag = SENS_PLATFORM_VERSION = GET /version

Published release tags are immutable. Before a build, the image workflow checks an existing tag’s OCI revision and version labels. A retry for the same commit converges without another push. A tag owned by another commit fails the workflow instead of overwriting the artifact.

Local Compose and direct TypeScript starts derive <package.json>-local.0 unless the operator provides a valid explicit local-channel override.

If GitOps and the live API differ, do not prepare another bump. Complete or repair the current Production promotion first, then rerun pnpm version:status. If both Production sources agree and package.json contains exactly the next patch, minor, or major target, the line is open and may only be reclassified upward. During a normal release, wait for the validated Production desired-state commit, sync sens-platform-production in Argo CD, wait for Synced and Healthy, and verify that GitOps and /version report the same stable version.

An incorrect image or runtime channel prevents platform-api from starting. Restore a previously known-good immutable tag or forward-fix the GitOps release selection; do not bypass runtime validation. A rollback may make GitOps and live temporarily diverge. New bumps and alpha publication remain blocked until the desired and live states are intentionally reconciled.

Version changes never require a StatefulSet or PVC mutation. If Argo CD reports a forbidden StatefulSet specification update, compare rendered and live immutable fields and restore the compatible desired state. Do not delete the StatefulSet or its PVC as a version-release workaround.