XDev.EasyVersion 1.0.0.1

dotnet tool install --global XDev.EasyVersion --version 1.0.0.1                
This package contains a .NET tool you can call from the shell/command line.
dotnet new tool-manifest # if you are setting up this repo
dotnet tool install --local XDev.EasyVersion --version 1.0.0.1                
This package contains a .NET tool you can call from the shell/command line.
#tool dotnet:?package=XDev.EasyVersion&version=1.0.0.1                
nuke :add-package XDev.EasyVersion --version 1.0.0.1                

This is Still a Work in Progress

Table of Contents

Introduction

Automatically versioning builds while maintaining a semantic versioning scheme can get somewhat complicated quickly when you try to make an approach flexible enough to accommodate different versioning and naming schemes. This repo contains a collection of tools to help you build reliable CI-CD pipelines with minimal environment-specific scripts needed. The only real requirement is the .Net 6 or newer runtime for the CLI tool, or you can use a Cake build to import the XDev.EasyVersion.Core or other packages directly.

The versioning concept encapsulated in this project is that all releasable products should have a unique version number that is never duplicated by separate builds. Also, build numbers are generated as part of a CI/CD pipeline process and not manually assigned. These 2 rules allows us to generate human readable unique build numbers applied in a Symantic Versioning scheme without having to worry about accidental duplication while building a variety of code bases, such as Nuget packages and multi platform application packages such as Xamarin or Maui apps, as well as desktop apps.

Support Files

Build Information.txt

This file is responsible for specifying the information needed to generate version numbers for a given repo, and must be included in the repo, by default the tool looks in the root source directory for this file.

/// ***********************************************************************************
/// **  The Release Pipeline will use this file to auto-version and track releases.  **
/// ***********************************************************************************

/// <summary>
/// Specifies the name of the package to release.
/// This tag and value is required.
/// </summary>
/// <example>
/// [Package Name]
/// My.Package
/// </example>
[Package Name]
My.Package

/// <summary>
/// Specifies the 3 octet version of the release.  This is the versioning schema used by Nuget.
/// This tag and value is required.
/// </summary>
/// <example>
/// [Release Version]
/// 1.2.3
/// </example>
[Release Version]
1.0.1

The Build Information.txt file provides 2 key pieces of information, the Package Name and Release Version in semantic form (Major.minor.Patch) and is manually incremented as desired by the repo owner.

Package Name

This is the public name of the package. Whether we are building a Nuget repo, iOS or Android App, or desktop application, this is the product name that's included with the Release Metadata.

Release Version

This is the Major.Minor.Patch Semantic version of the package. This notation comes from the Nuget guidelines, it also allows a CI-CD Pipeline to auto-increment the 4th octet used for file versioning. You can use any 3 octet versioning scheme you choose such as Major.Minor.Patch, Major.Minor.Revision, Year.Quarter.Patch, etc. Just keep in mind that modifying Build Information.txt is required to update any of the first 3 octets and fits well into a Git-Flow branch and merge strategy with a CI-CD DevOps process running auto builds on one or more of the long-lived branches, like dev and main or whatever you may call them.

Release History.json

This file is resident separate from the source code and keeps track of all builds and releases for a particular Repo. The presence of this file is what gives a build process the ability to generate unique version numbers that are also easy to read without resorting to some date-code or long hash code as part of a version number. A very popular option for CM of build history and build code itself is to keep it in the same repository as the source code. This is a very simple and easy option, if one does not mind "mixing" build code and source code, and if your dev team is also responsible for DevOps. However, this does introduce certain difficulties, such as the fact that any build that updates the history, now must make a commit back to the source repo, potentially causing a recursive build scenario unless one excludes the history file/directory from auto-build triggers, as well as introducing un-reviewed changes to otherwise protected branches.

In the opinion of the author, below are some better CM options because they allow separation of concerns between CI-CD and the source code:

  • On a local build machine, simply keep this file in a location accessible by the build agents (runners)
  • Keep this file in a git sub-repository and add steps to your build(s) to pull the correct sub-repo at the start of the build, and only commit changes to the sub repo responsible for the build history
  • If you are using Azure Dev ops, Universal Packages are a very simple way to keep a versioned artifact between builds, including code coverage results or our Release History.json file.
  • Lastly, if none of the other options are available or suit you, XDev.EasyVersion.Nuget.History can provide a tool that is almost as easy as ADO's Universal Packages, but using NuGet as the packaging tool.

The below mentioned XDev.EasyVersion.exe is capable of retrieving specific release information as well as rolling build number, generating context-specific version numbers from the semantic version including the build, and adding the new build to the history file: Release History.json.

##todo: add buildinfo.txt details...

dotnet Tool Install

To install as a local tool, see instructions here to setup a local tool store, then install the package from nuget.org:

 dotnet new tool-manifest
 .
 .
 .
 dotnet tool install XDev.EasyVersion

To install as a global tool (admin or root permissions required, so this is probably not a good option for pipelines): dotnet tool install --global XDev.EasyVersion

To install a prerelease version, include --prerelease and possibly --allow-downgrade

dotnet Tool Usage

XDev.EasyVersion

To set up EasyVersion via a command-line tool during a CI or CD pipeline, pass in a Package Name (i.e. "My.Custom.Package"), 3 octet Release Version (i.e. 1.2.3), and Drop Type (i.e. alpha, beta, etc for a pre-release semantic version with tag format) (optional).

For a pre-Release build the options might look like:
dotnet easyVersion roll --PackageName=Foo.Bar --ReleaseVersion=1.2.3 --DropType=beta --ReleaseHistoryJsonPath="./Build Tracking/Release History.json"

With the output:

Package Name         : Foo.Bar  
Semantic Version     : 1.2.3
Build                : beta043
Package Version      : 11.2.3-beta043  
File Version         : 11.3.3.43  
Android Version Code : 110203043  
Build Date           : 2020-06-01T16:30:03.9423513-05:00

For a Release build the options might look like:
Bloomerang.Build.Versioning.exe roll --PackageName=Foo.Bar --ReleaseVersion=1.2.3 --ReleaseHistoryJsonPath="./Build Tracking/Release History.json"

With the output:

Package Name         : Foo.Bar 
Semantic Version     : 1.2.3
Build                : 43
Package Version      : 11.2.3  
File Version         : 11.3.3.44  
Android Version Code : 110203044  
Build Date           : 2020-06-01T16:30:03.9423513-05:00
  • Note: the build number is continuous and monotonically increases regardless of whether the build is pre-release (tag) or release (no tag) supplied, so it is possible to have the following version result from a series of builds:
  • 1.2.3-rc001
  • 1.2.3.2
  • 1.2.3-rc003

It is presumed that one would manually update the Major, Minor, or Patch version in BuildInfo.txt if you wish to avoid the scenario of publishing a pre-release version newer than the last release version. You are free to manage how/when to roll the Major, Minor, Patch versions as best fits your project or preferred conventions.

Depending on where in your build you save the Release History.json in a pipeline, you may have "skipped" build numbers, if you save the Release History.json before ensuring that all build steps have succeeded. Again, you are in control of when you store the Releas History.json file in your build so you can "discard" failed build numbers, or re-use a build number until the build succeeds.

Android VersionCode limit

Per google's dev docs, the max VersionCode is 2100000000. This tool may generate a value greater than this. Version your app | Android Studio | Android Developers

What this means is that this tool can produce an invalid AndroidVersion code, if Minor > 99, and/or if Patch > 99 in a semantic version like 12.100.222.3 would produce a versionCode of 12100222003, which is > 2100000000, the max-supported value by Google. There is an issue to help mitigate this scenario by checking and appending "-invalid" to the output, but this should be a rare and easily avoided situation.

To avoid this, limit input semantic versions to the pattern MMM.mm.pp.bbb when consuming Android-VersionCode generation by this tool, per the previous example.

EasyVersionRoll.ps1

This powershell script provides a simple way to read the Build-Info.txt file for Major.Minor.Patch version info, supply input to the cli tool, and then store the tool's output to ADO pipeline variables for use in later steps, such as setting version info in .csproj files or ``AssemblyInfo.cs` files before building.

  • Currently only ADO is supported with this script, but you could easily modify it for different build hosting environments.

Calling the versioning script might look like this for a pre-Release build:
EasyVersionRoll.ps1 -buildInfoPath "SomeDir\BuildInfo.txt" -easyVersionToolPath "SomeDir\XDev.EasyVersion.exe" -dropType "beta"

The script will read the Package Name and Release Version from BuildInfo.txt, call XDev.EasyVersion.exe with the parameters set. It will then process the return text from XDev.EasyVersion.exe at set the Pipeline AndroidVersionCode, FileVersion, and PackageVersion variables.

The ADO pipeline variables that are set and available for later tasks in the pipeline:

  • AndroidVersionCode
  • FileVersion
  • PackageVersion
Product Compatible and additional computed target framework versions.
.NET net8.0 is compatible.  net8.0-android was computed.  net8.0-browser was computed.  net8.0-ios was computed.  net8.0-maccatalyst was computed.  net8.0-macos was computed.  net8.0-tvos was computed.  net8.0-windows was computed. 
Compatible target framework(s)
Included target framework(s) (in package)
Learn more about Target Frameworks and .NET Standard.

This package has no dependencies.

Version Downloads Last updated
1.0.0.1 244 6/17/2024

Initial release of the dotnet tool package.