Skip to content
Documentation
Releases

Versions, channels and releases

One version for every artifact, stable releases and developer previews, how to read the build a deployment runs, and how a release is cut.

Releases 5 min read

One version number names everything OpenSmartRoute ships - the Python package, the osr command line, the npm client, the platform API and web images, the Helm chart, the Compose bundle, the Azure Marketplace package, the browser extension and the desktop app - and every deployment tells you which build it runs. This page says what the numbers mean, how to read them on each surface, and how a release is made.

One version

The release version is X.Y.Z (Semantic Versioning). Its single source is pyproject.toml; scripts/release.py keeps every mirror in step (the __version__ literals, the platform and web packages, the TypeScript SDK, the browser extension, the desktop app, the Helm chart, the image tags in the deploy documents, the Marketplace template) and release.py check fails CI when any of them drifts. There is no separate extension version, desktop version, SDK version or platform version: pip install opensmartroute==1.3.0, npm install @opensmartroute/sdk@1.3.0, osr-platform-api:1.3.0, chart 1.3.0, the extension 1.3.0 and OpenSmartRoute-1.3.0-win-x64.exe were all built from the tag v1.3.0.

  • A minor release (1.3.0 -> 1.4.0) adds; the public Python API only grows (Contributing, Public API).
  • A patch release (1.3.0 -> 1.3.1) fixes; nothing new is announced.
  • A major release may remove what was deprecated two minors earlier.
  • SECURITY.md lists the supported minors: the current one gets fixes, the one before it security fixes only.

Two channels

Every build is either a stable release or a developer preview. The pipelines decide with scripts/release.py build-version, from the git tags alone - no one types a build number:

ChannelBuilt fromVersionWhere it goes
stablethe tag vX.Y.Z (the Release pipeline)X.Y.ZPyPI, npm, the image tags X.Y.Z / X.Y / latest, the release downloads, extension/latest.json, desktop/latest.json + latest*.yml, the browser stores
developer previewany other commit on main<next>-preview.<N>the hosted platform (opensmartroute.ai), extension/preview.json, desktop/preview.json + preview*.yml

<next> is the release the preview leads up to: the pyproject version while its tag does not exist yet (a merged release pull request, about to be tagged), otherwise the next patch after the last release. N counts the commits since the last release tag, so previews sort and read in order: 1.3.0 -> 1.3.1-preview.1 ... 1.3.1-preview.89 -> 1.4.0-preview.3 (release PR merged) -> 1.4.0.

The hosted platform deploys every merge to main, so what you use at opensmartroute.ai is always the developer preview of the next release - the [Unreleased] section of the changelog is what it carries beyond the last stable release. Self-hosted installations pull stable images and stay on releases.

Browser manifests take up to four integers, so a preview extension carries X.Y.Z.N (1.3.1.89) in manifest.json; Chrome and Edge show the readable label (1.3.1-preview.89) as version_name. A release carries X.Y.Z. Previews are never submitted to the browser stores. The desktop installers carry the label in their file names (OpenSmartRoute-1.3.1-preview.89-win-x64.exe) and a preview build's updater follows the preview*.yml feed, so an installed release only ever updates to the next release.

Reading the version

SurfaceWhat it shows
osr --version, opensmartroute.__version__the package release, 1.3.0
GET /api/v1/info -> build, GET /healthz -> build{"version": "1.3.1-preview.89", "release": "1.3.0", "channel": "preview", "commit": "4ced2b2..."} on a pipeline-built image; channel is null for a checkout or a hand-built image, which report the release alone
the website footerv<build version>; a developer preview badge when the deployment runs main ahead of the next release
GET /api/v1/releasesevery release's notes, the running build, and unreleased - what has landed since the last release (GET /api/v1/releases/{version} with unreleased as the version returns that section alone)
GET /extension/release.jsonthe extension build offered for download with its channel; ?channel=preview for the preview, ?channel=stable for the release only
GET /desktop/release.jsonthe same for the desktop app's installers
the dashboard's What's new card and the release mailthe stable release notes, sent once per release version - previews never announce
image labelsorg.opencontainers.image.version and .revision on osr-platform-api and osr-platform-web (docker inspect)

The build stamp reaches the images as build arguments (OSR_BUILD_VERSION, OSR_BUILD_CHANNEL, OSR_BUILD_COMMIT in azure.yaml and the Release pipeline) and the API as the settings OSR_PLATFORM_BUILD_VERSION, OSR_PLATFORM_BUILD_CHANNEL and OSR_PLATFORM_BUILD_COMMIT. Anyone building the image by hand can pass the same arguments (docker build --build-arg OSR_BUILD_VERSION=... ); without them the image is honest about not knowing its channel.

Downloads

opensmartroute.ai/downloads always resolves to the current stable release: /download/<artifact> redirects to the versioned file and /download/latest.json lists them with SHA-256 checksums. Versioned files never change once published (cached for a year); the latest.json pointers move with each release.

The browser extension follows the same rule with two pointers: /extension/download/<browser> offers the stable release and /extension/download/<browser>?channel=preview the developer preview. While no stable release of the extension has been published yet, the preview stands in and the page says so (Browser extension, Where the packages come from). The desktop app is served the same way from /desktop (/desktop/release.json?channel=preview, installers and update feeds under /desktop/download/).

Unreleased changes

Every user-visible change lands as a bullet under ## [Unreleased] in CHANGELOG.md the moment it merges - that section is the developer preview's release notes and the draft of the next release's. The website renders it on the changelog page, GET /api/v1/releases returns it as unreleased, and release.py prepare turns it into the dated ## [X.Y.Z] section when the release is cut. Nothing is announced to users until then: the release mail and the What's new card key off the stable release version.

How a release is made

Releases are cut by two Azure Pipelines from main; nobody edits a version or a tag by hand (Contributing, Releasing, has the maintainer detail).

  1. Prepare release (azure-pipelines/release-prepare.yml, run by hand with bump = major / minor / patch or an explicit version): release.py prepare bumps the version everywhere, rolls [Unreleased] into [X.Y.Z] - date, refreshes the compare links and the supported-versions table, regenerates the OpenAPI snapshot, and opens the pull request release/vX.Y.Z. From the moment that pull request merges, main builds are X.Y.Z-preview.N - release candidates of X.Y.Z.
  2. Release (azure-pipelines/release.yml, run by hand on main after the merge): verifies release.py check, runs the test suite and the routing gates, builds the sdist and wheel, creates the annotated tag vX.Y.Z (its message is the changelog section), publishes to PyPI and npm, builds the opensmartroute, osr-platform-api and osr-platform-web images stamped stable, packages the Compose bundle, Helm chart and Marketplace zip into the release downloads, queues the Extension and Desktop pipelines on the tag (stable packages behind latest.json, store submissions), then fetches everything back the way a user would - the PyPI files must match the built checksums, the image must report the version and answer /readyz.
  3. Announce. The next deploy of the hosted platform runs the new version; its release-news job mails the digest of the changelog section to opted-in users once and the dashboard shows the What's new card (Platform guide, Campaigns).

A red verification stage means the release is not usable as published: fix forward with a patch release, never by moving a tag.

Maintainer commands

python scripts/release.py version                 # 1.3.0
python scripts/release.py check                   # every mirror agrees, the changelog has the section
python scripts/release.py build-version --json    # what a build of this checkout is called
python scripts/release.py notes 1.3.0             # the changelog section (the tag message)
python scripts/release.py prepare --bump minor    # what the Prepare release pipeline runs

build-version needs the tags (git fetch --tags; pipelines check out with fetchDepth: 0 and fetchTags: true). --channel stable or --channel preview forces the answer, --build N the preview number - the pipelines use auto.