Overview
I'm developing a number of .NET Core applications and I've met my current sprint commitments early. Until my next sprint begins, I'm using the slack to look into setting up a CI pipeline for my projects (this hasn't been hugely necessary as I'm the only person working on these projects, but it would be helpful to automate some of the stuff I'm currently doing manually).
However, I'm confused about the version numbering scheme I should adopt to support this.
Environment
In case it's helpful information, I'm attempting to set up this workflow using the following tools:
- Visual Studio for code development
- BitBucket to use as a remote repository
- BitBucket Pipelines to use for CI builds
- MyGet to use as the package feed and the location to push packages to during builds
Details
All the technical setup for this has gone pretty smoothly so far but I'm stumped trying to figure out what version numbers I should assign to builds on different branches while still complying with semantic versioning and a GitFlow-style workflow.
Let's say that my previous release of some project X is 1.1.0. If I commit some change on develop
and publish it to my repository (triggering a build), what version should be assigned to the NuGet package produced from that code?
Quoting nvie's recommendation here:
It is exactly at the start of a release branch that the upcoming release gets assigned a version number—not any earlier. Up until that moment, the develop branch reflected changes for the "next release", but it is unclear whether that "next release" will eventually become 0.3 or 1.0, until the release branch is started. That decision is made on the start of the release branch and is carried out by the project’s rules on version number bumping.
This makes sense to me and would suggest that - until I create a release branch - I should stick with version numbering like e.g. 1.1.0-unstable0023
. However, in semantic versioning schemes a version like this is taken to represent a release leading up to v1.1.0 i.e. an earlier build, which is not what I want.
To further complicate things, the dotnet CLI lets you assign the version suffix (e.g. during a CI build) but not any other parts of the version (the major, minor or patch number) - these are determined strictly from the project.json
file corresponding to the project that's being built.
For what it's worth, here's what my bitbucket-pipelines.yml
looks like for my first adapted project so far:
image: microsoft/dotnet:onbuild
pipelines:
branches:
develop:
- step:
script:
-BUILD_CONFIGURATION=Debug
# Generate build number
# Note: may adapt this to use GitVersion.exe instead
- BUILD_NUMBER=`git log --oneline | wc -l`
- echo "Build number':' ${BUILD_NUMBER} (will be appended to the generated NuGet package version)"
# Install NuGet
- apt-get update && apt-get install -y nuget
# Add credentials
- nuget setapikey $MYGET_API_KEY -source https://www.myget.org/F/company/api/v3/index.json -configFile NuGet.Config
- nuget sources update -name "Company MyGet Feed" -source https://www.myget.org/F/company/api/v3/index.json -user $MYGET_USER -pass $MYGET_PASS -StorePasswordInClearText -configFile NuGet.Config
# Restore and test projects
- dotnet restore
- dotnet test test/<<Company>>.<<Product>>Web.BaseTypes.Tests
- dotnet test test/<<Company>>.<<Product>>Web.DeviceComponents.Tests
- dotnet test test/<<Company>>.<<Product>>Web.Utility.Tests
# Pack projects
- dotnet pack --configuration $BUILD_CONFIGURATION --version-suffix=build$BUILD_NUMBER project.json src/<<Company>>.<<Product>>Web.BaseTypes
- dotnet pack --configuration $BUILD_CONFIGURATION --version-suffix=build$BUILD_NUMBER project.json src/<<Company>>.<<Product>>Web.DeviceComponents
- dotnet pack --configuration $BUILD_CONFIGURATION --version-suffix=build$BUILD_NUMBER project.json src/<<Company>>.<<Product>>Web.Utility
- dotnet pack --configuration $BUILD_CONFIGURATION --version-suffix=build$BUILD_NUMBER project.json src/<<Company>>.<<Product>>Web.Utility.JsonPathGrammar
# Push generated packages
# TODO
Current versioning strategy
At the moment, I assign version numbers following this format for release builds:
<major>.<minor>.<patch>
I bump the version number as soon as I start a release branch, so any pre-release builds from a release branch will be generated just by appending a -betaxxxx
suffix:
<major>.<minor>.<patch>-beta0000, <major>.<minor>.<patch>-beta0001, ...
If I need to produce a pre-release version and I haven't started a release branch yet, I will manually bump the version numbers in the relevant project.json
files and produce builds like this:
<major>.<minor>.<patch>-unstablexxxx
Where xxxx
is the padded number of commits on develop
since the previous release.
If anyone has suggestions on a good version numbering scheme that works for .NET Core projects being adapted for a CI/CD workflow, I'd appreciate that a lot.
3 Answers 3
In my experience there is a distinct difference between "internal revision/tag identifiers" and "public release/version numbers."
You need to better understand the specific requirements you have from versioning.
Most of my cases are end user applications. In this scenario a single version number is sufficient, all I require from the version is to match it to a commit ID.
Other, less frequent, cases are libraries. In this case there is an additional requirement to match to an API version. v123.2
and v124.1
refers to API versions 124
and 123
, while 1
and 2
refers to a sequential patch number (it might refer to some bug corrected in both API versions).
This a summary of my requirements. You need to understand yours and be aware to not fall to a situation where mantaining version numbers hinders development, that is not the point. Latelly I've been using a build process to inject the actual commit id, but not sure if I like it that much.
You need to have as a simple versioning schema as you can. It leads to high maintenance and pain to have it to complex.
The primary reason for having version numbers is to signal expectations about a new release. Like if the major version didn't change, expect the new release to be backward compatible. But that is only necessary if you have clients consuming your software like a library or an API.
It is unclear to me what the use-cases you need to support by versioning, maybe you can clarify on that aspect?
In my case, I just use the build number as the version number of my project, because I don't have any external consumers that I need to worry about.
To distinguish between development and release versions I use branches, if it is built from master it is a release, other branches get a postfix to the version number.
Explore related questions
See similar questions with these tags.
Major.Minor.Build.Revision
. Would that satisfy your CI/CD workflow?Major.Minor.Patch
for release versions.beta
suffix, which I would argue is not really necessary). What problems remain?