Security change on December 1, 2026: TLS 1.2 will become the minimum standard for 365 business Print Agent. Details and system requirements
365 business development
Concepts

Build versioning

Every CI run on a release line stamps a version into app.json and claims a build/<version> branch for it. This page explains how the four version parts are decided, why the build number no longer restarts after a version bump, and what a release pipeline reproduces from it.

The versioning work is done by the Stamp Build Version task (Invoke-BcBuildVersionStamp), driven by the shared CI and CD templates in businessdev.BC.Pipeline (yaml/v2/ci.yml, yaml/v2/cd.yml).


How the version is built

The version is expressed as a token schema. The CI uses major.minor.increment.0:

PartWhere it comes from
Majorapp.json (unchanged)
Minorapp.json (unchanged)
Buildthe build number - seeded from the highest build/* branch, then incremented
Revision0 in CI; the pipeline build id in a CD release

A version is only stamped - and only then is a build/<version> branch claimed and a package published - on the release lines dev and hotfix/*. Every other run (feature/*, PR validation, schedule) builds and tests without a version, without a build branch and without a package.

The build/<version> branch is the carrier: it is what a later run reads the already-claimed build number back from, and it is what makes two parallel builds compute different versions.


The build number runs across version bumps

Seeding the counter from the highest build/<Major>.<Minor>.* branch of the current Major.Minor line has a consequence that only shows up when the version is raised: the new line has no build branch yet, so the counter starts at 1 again.

Code
app.json 18.3 -> 18.4 last build branch 18.3.539.* first build on the new line 18.4.1.0

While Major.Minor moves with every release that is merely untidy. It becomes a real problem when Major.Minor stays put: the freshly built version then lands below the version customers already have installed, and the app can no longer be upgraded.

Code
shipped to customers 18.12.239 CI keeps building 18.12.8.0 -> lower, so no upgrade path

continuousBuildNumber fixes this. With it enabled the build number is derived from the highest build branch of the whole repository, across all lines, while Major and Minor still come from app.json:

Code
app.json 18.3 -> 18.4 highest build branch in the repo 18.3.539.* -> 18.4.540.0
SettingValueEffect
continuousBuildNumber (CI template parameter)true (default)Build number runs on across all build/* branches of the repository
continuousBuildNumberfalseOld behaviour: the counter restarts per <Major>.<Minor> line
-ContinuousBuildNumber (Invoke-BcBuildVersionStamp)switchSame option at cmdlet level, for a local or hand-written pipeline

A product that wants its build number to restart with every release line sets it in its own root ci.yml:

YAMLCode
extends: template: yaml/v2/ci.yml@pipeline parameters: projectFolder: '' continuousBuildNumber: false

Requires StampBuildVersion 0.2 / module 2.59.0

The CI template passes the continuousBuildNumber input to StampBuildVersion@0, which needs task version 0.2 of the ALbuild Azure DevOps extension and module 2.59.0 or later.

One-time seeding after the migration from the old pipeline

The counter can only run on as high as a build branch already says it is. A repository whose earlier versions were produced without build branches therefore needs a single build/<version> branch at the height it had already reached, otherwise the first v2 build starts under the shipped version.


What the CD does not change

The release pipeline is deliberately untouched by this. It takes the version the CI assigned from the build branch of its own release line - schema latest.latest.latest.build-id, which reads the highest build/<Major>.<Minor>.* branch and sets the fourth part to the build id:

Code
CI claimed 18.4.540.* release run -> 18.4.540.<build-id>

That scoping is intentional: a release must reproduce the exact version its CI validated, not the tallest number somewhere else in the repository. The latest token enables seedFromBuildBranch automatically, and the read is read-only on the remote - a release never claims a build branch.

PipelineBuild number scopeReason
CI (StampBuildVersion with continuousBuildNumber)highest build branch of the repositorythe number only has to keep rising
CD (seedFromBuildBranch)highest build branch of its own <Major>.<Minor> linethe release must match what CI tested

Last modified on