NowAround.DevOps.Versioning.Cli 1.2.0-ci.216
NowAround.DevOps.Versioning.Cli
NowAround.DevOps.Versioning.Cli is a .NET tool for Azure DevOps semantic version calculation. It reads git release tags and Azure DevOps pull request metadata, then emits a package version for PR, main, and release pipelines.
Design
The tool separates version decision from release execution:
- PR validation decides a bump and persists it as a PR label.
- Main and release pipelines read only persisted
semver:*labels. - Stable release state is represented by git tags such as
v1.2.3. - Package versions are emitted as Azure Pipeline variables.
This makes main and release builds deterministic: they do not infer from titles or branches after the PR has merged.
Modes
PR mode:
nowaround-versioning calculate --mode pr --output azure-pipelines
Output shape:
X.Y.Z-pr.<pullRequestId>.<buildId>
If the current PR is missing a semver:* label, PR mode infers a bump from description markers, branch prefixes, title prefixes, or --unknown-bump-policy, then adds the matching PR label.
Main mode:
nowaround-versioning calculate --mode main --output azure-pipelines
Output shape:
X.Y.Z-ci.<buildId>
Main mode scans completed PRs since the latest stable tag and fails if an included PR has no semver:* label.
Release mode:
nowaround-versioning calculate --mode release --output azure-pipelines
Output shape:
X.Y.Z
Release mode uses the same strict label-only behavior as main mode.
Local mode:
nowaround-versioning calculate --mode local --output text
Output shape:
X.Y.Z-dev.<headSha>
Local mode uses the same completed-PR scan as main mode, then appends a prerelease label and the current HEAD commit. It is intended for developer builds and can cache the calculated value in a gitignored file.
Inputs
The tool reads Azure Pipelines variables by default:
SYSTEM_COLLECTIONURI
SYSTEM_TEAMPROJECT
BUILD_REPOSITORY_ID
SYSTEM_ACCESSTOKEN
SYSTEM_PULLREQUEST_PULLREQUESTID
BUILD_BUILDID
Equivalent CLI options are available:
nowaround-versioning calculate \
--mode pr \
--collection-uri "https://dev.azure.com/ORG/" \
--project "PROJECT" \
--repository-id "GUID" \
--access-token "$SYSTEM_ACCESSTOKEN" \
--pull-request-id "123" \
--build-id "4567"
Azure Pipelines System.AccessToken uses bearer authentication. For local testing with a PAT:
nowaround-versioning calculate \
--mode pr \
--access-token "$AZURE_DEVOPS_PAT" \
--access-token-type pat
Output
With --output azure-pipelines, the tool writes:
##vso[task.setvariable variable=BaseVersion]...
##vso[task.setvariable variable=NextVersion]...
##vso[task.setvariable variable=PackageVersion]...
##vso[task.setvariable variable=VersionBump]...
##vso[task.setvariable variable=LastStableTag]...
With --output json, it emits the full calculation result, including included PR decisions.
With --output value, it emits only the calculated package version.
Local Build Cache
Developer builds can avoid repeated Azure DevOps API calls by using local mode with --cache-file:
git fetch --tags
nowaround-versioning calculate \
--mode local \
--output text \
--cache-file obj/nowaround/version.json \
--write-msbuild-props obj/nowaround/NowAround.Version.props \
--collection-uri "https://dev.azure.com/ORG/" \
--project "PROJECT" \
--repository-id "GUID" \
--access-token "$AZURE_DEVOPS_PAT" \
--access-token-type pat
When the cache exists and still matches HEAD, branch, target branch, and latest stable tag, the tool uses it without contacting Azure DevOps. If the cache is missing or stale, the tool recalculates the version and writes a fresh cache. Use --refresh-cache to force recalculation.
The generated props file can be imported from Directory.Build.props by projects that should embed the version:
<Project>
<Import Project="$(MSBuildThisFileDirectory)obj/nowaround/NowAround.Version.props"
Condition="Exists('$(MSBuildThisFileDirectory)obj/nowaround/NowAround.Version.props')" />
</Project>
If you import from an individual .csproj, write the cache under that project's directory or adjust the import path accordingly.
To let normal local builds calculate the version automatically when the cache is missing or stale, add a local-only target in Directory.Build.targets:
<Project>
<Target Name="ResolveNowAroundLocalVersion"
BeforeTargets="GetAssemblyVersion"
Condition="'$(TF_BUILD)' != 'True'">
<Exec Command="nowaround-versioning calculate --mode local --output value --cache-file "$(MSBuildThisFileDirectory)obj/nowaround/version.json" --collection-uri "$(NowAroundCollectionUri)" --project "$(NowAroundProject)" --repository-id "$(NowAroundRepositoryId)" --access-token "$(AZURE_DEVOPS_PAT)" --access-token-type pat"
ConsoleToMsBuild="true">
<Output TaskParameter="ConsoleOutput" PropertyName="NowAroundPackageVersion" />
</Exec>
<PropertyGroup>
<Version>$(NowAroundPackageVersion)</Version>
<PackageVersion>$(NowAroundPackageVersion)</PackageVersion>
<InformationalVersion>$(NowAroundPackageVersion)</InformationalVersion>
</PropertyGroup>
</Target>
</Project>
Set NowAroundCollectionUri, NowAroundProject, and NowAroundRepositoryId as MSBuild properties or environment variables. Set AZURE_DEVOPS_PAT in your local environment. The target still starts the CLI on each local build, but valid cache hits do not call Azure DevOps.
The props file sets Version, PackageVersion, InformationalVersion, AssemblyVersion, and FileVersion. Runtime code can read AssemblyInformationalVersionAttribute and publish that value as service.version.
Keep local outputs out of git:
obj/nowaround/
Files under obj are removed by dotnet clean, so clean rebuilds recalculate the local version while normal builds can reuse the cache.
The PAT used for local cache refresh must be able to read the repository and pull request labels. Label write permission is only needed for pr mode when the tool creates missing semver:* labels.
Artifact Publish Decisions
Artifact publish decisions are separate from SemVer calculation. semver:* labels decide the version bump. artifact:* labels decide whether NuGet packages and/or Docker images should be published with the calculated PackageVersion.
Enable artifact decision variables with:
nowaround-versioning calculate \
--mode main \
--output azure-pipelines \
--unknown-bump-policy fail \
--include-artifact-decisions \
--artifact-default none
--artifact-default accepts none, nuget, docker, or all. If omitted, the default is none for PR/main mode and all for release mode. In main mode, --artifact-triggering-pr-required fails the pipeline when the triggering PR cannot be found; otherwise publishing is disabled with ArtifactDecisionSource=no-triggering-pr.
Supported artifact labels:
artifact:publish
artifact:nuget
artifact:docker
artifact:none
Use at most one artifact:* decision label on a PR. Multiple artifact decision labels fail the run.
Main mode artifact decision looks only at the completed PR that produced the current main run's BUILD_SOURCEVERSION merge commit. It does not use the full unreleased PR set scanned for SemVer calculation.
When enabled, Azure Pipelines output includes:
PublishArtifacts
PublishNuGet
PublishDockerImage
ArtifactDecision
ArtifactDecisionSource
ArtifactDecisionLabels
TriggeringPullRequestId
Publish conditions:
condition: and(succeeded(), eq(variables['PublishNuGet'], 'true'))
condition: and(succeeded(), eq(variables['PublishDockerImage'], 'true'))
Supported Labels
semver:major
semver:minor
semver:patch
semver:none
Only one semver:* label is allowed per PR. Multiple labels fail the run.
Git Requirements
Use full history and tags:
- checkout: self
fetchDepth: 0
persistCredentials: true
Existing Repositories
Before enabling the tool on an existing repository, create one bootstrap stable tag on the current main commit or on the last released commit:
git checkout main
git pull
git tag -a v1.2.3 -m "Bootstrap versioning from v1.2.3"
git push origin v1.2.3
Use the version that represents the current released package. The tag must match the configured stable tag pattern, which defaults to v[0-9]*.[0-9]*.[0-9]*.
Without this tag, pr, main, and release modes treat all reachable completed PRs as unreleased history. Existing completed PRs that do not already have exactly one semver:* label can then fail the pipeline.
The latest stable tag defaults to:
v[0-9]*.[0-9]*.[0-9]*
The default tag prefix is:
v
Both can be customized with --stable-tag-pattern and --tag-prefix.
Implementation Notes
The tool uses:
git tag,git rev-list,git rev-parse, andgit merge-basefor local repository history.- Azure DevOps Git REST API for pull requests and PR labels.
System.Text.JsonandHttpClientinstead of the large Azure DevOps SDK dependency, keeping the package small.
The package does not create release tags or push NuGet packages. Pipelines should use $(PackageVersion) for those steps.
| Version | Downloads | Last updated |
|---|---|---|
| 2.0.0 | 24 | 06/15/2026 |
| 1.2.1 | 4 | 06/15/2026 |
| 1.2.0 | 42 | 06/01/2026 |
| 1.2.0-ci.251 | 1 | 06/01/2026 |
| 1.2.0-ci.216 | 1 | 05/23/2026 |
| 1.1.0 | 42 | 05/23/2026 |
| 1.1.0-ci.211 | 1 | 05/23/2026 |
| 1.1.0-ci.209 | 1 | 05/23/2026 |
| 1.0.3 | 5 | 05/23/2026 |
| 1.0.2 | 3 | 05/23/2026 |
| 1.0.1 | 33 | 05/15/2026 |
| 1.0.0 | 6 | 05/14/2026 |
| 0.1.1 | 5 | 05/14/2026 |
| 0.1.0 | 5 | 05/14/2026 |
| 0.0.3 | 8 | 05/14/2026 |
| 0.0.2 | 3 | 05/14/2026 |
| 0.0.1 | 4 | 05/14/2026 |