I am configuring semantic versioning with GitLab for my dotnet core apps and netstandard 2.0 packages.
After reading quite a bit of opinions, some of them contradictory, this is what is clear to me.
A semantic version should be something like
M.m.P.B-abc123
where
M
is major version m
is minor version P
is patch version B
is build version (optional)-abc123
is suffix (optional) in case I use pre-releases. It must start with letterSo the following package versions would be valid:
1.0.0
1.0.1.20190301123
1.0.1.20190301123-beta
1.0.1-rc1
I have the following gitlab script for my versioning
#Stages
stages:
- ci
- pack
#Global variables
variables:
GITLAB_RUNNER_DOTNET_CORE: mcr.microsoft.com/dotnet/core/sdk:2.2
NUGET_REPOSITORY: $NEXUS_NUGET_REPOSITORY
NUGET_API_KEY: $NEXUS_API_KEY
NUGET_FOLDER_NAME: nupkgs
#Docker image
image: $GITLAB_RUNNER_DOTNET_CORE
#Jobs
ci:
stage: ci
script:
- dotnet restore --no-cache --force
- dotnet build --configuration Release
- dotnet vstest *Tests/bin/Release/**/*Tests.dll
pack-beta-nuget:
stage: pack
script:
- export VERSION_SUFFIX=beta$CI_PIPELINE_ID
- dotnet pack *.sln --configuration Release --output $NUGET_FOLDER_NAME --version-suffix $VERSION_SUFFIX --include-symbols
- dotnet nuget push **/*.nupkg --api-key $NUGET_API_KEY --source $NUGET_REPOSITORY
except:
- master
pack-nuget:
stage: pack
script:
- dotnet restore
- dotnet pack *.sln --configuration Release --output $NUGET_FOLDER_NAME
- dotnet nuget push **/*.nupkg --api-key $NUGET_API_KEY --source $NUGET_REPOSITORY
only:
- master
This generates packages such as:
1.0.0
for master branch (stable or production ready) and 1.0.0-beta1234567
for any other branch.
The problem with my approach is that I have VS solutions with multiple projects, each project will be a nuget package and each one has its own version. Sometimes I modify one project but not the other, therefore in theory I shouldn't need to produce a new artifact of the project that I didn't touch nor a new version, of course.
Right now my nuget repository prevents overwriting packages, so If there is a XXX.YYY 1.0.0
and I generate another XXX.YYY 1.0.0
and push it to the repository, it will throw an error and the pipeline will fail.
I have thought that maybe it's not such a bad idea to generate a new package each time I run the CI/CD pipeline, so I considered introducing the build number and have something like XXX.YYY 1.0.0.12345
and, even if I don't touch anything there, the next time a new package XXX.YYY 1.0.0.123499
would be produced.
Is this a correct approach in a continuous deployment scenario? or should I look for a way to make my script smarter and not to produce a new artifact if there is already one with the same version in my nuget repository?
Assuming it's ok to use build numbers always, how do I make sure that only the build number is retrieved from the pipeline but the M.m.P
version numbers remain in my csproj as per the following?
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFramework>netstandard2.0</TargetFramework>
<Description>Whatever</Description>
<VersionPrefix>1.0.1</VersionPrefix>
</PropertyGroup>
</Project>
I would need something like:
dotnet pack *.sln --configuration Release -p:PackageVersion=$FIXED_VERSION.$CI_PIPELINE_ID --output nupkg
but I don't know how to retrieve the <VersionPrefix>
content from the csproj through the CLI.
Any advice, good read or solution for my approach assuming it's valid?
Thanks
When you use Semantic Versioning with Continuous Deployment, version numbers must be checked into source control systems by programmers. If you aren't already using Semantic Versioning, you should. It makes it much easier to figure out how to version your releases.
Now Semantic Release works with plugins and one plugin we wrote can update files with the version number. So we use it to update the verson number in Directory.Build.Props. But instead of the version in the file you could also directly give the version number to the dotnet nuget cli but we found it more convenient to have it in the file.
The semantic version strategy became the industry standard to version applications. The semantic version strategy has been written down in a well-defined specification. You are advised to read it through, since understanding the specification will help you further defining the right version number.
Every time you deploy a new version, you should increment the version number. Continuous Delivery and Continuous Deployment rely on automation. A code check-in triggers an automated build, which is subsequently processed by a Deployment Pipeline, and potentially released to end-users.
The problem with my approach is that I have VS solutions with multiple projects, each project will be a nuget package and each one has its own version. Sometimes I modify one project but not the other, therefore in theory I shouldn't need to produce a new artifact of the project that I didn't touch nor a new version, of course.
Since a continuous integration pipeline is unable to determine if your code should be a new minor or major version you will always have to determine which semantic version your package should get. This makes the whole process a lot easier.
The guys from visual studio team services have this to say about it:
Immutability and unique version numbers In NuGet, a particular package is identified by its name and version number. Once you publish a package at a particular version, you can never change its contents. But when you’re producing a CI package, you can’t know whether it will be version 1.2.3 or just a step along the way towards 1.2.3. You don’t want to burn the 1.2.3 version number on a package that still needs a few bug fixes.
SemVer to the rescue! In addition to Major.Minor.Patch, SemVer provides for a prerelease label. Prerelease labels are a “-” followed by whatever letters and numbers you want. Version 1.0.0-alpha, 1.0.0-beta, and 1.0.0-foo12345 are all prerelease versions of 1.0.0. Even better, SemVer specifies that when you sort by version number, those prerelease versions fit exactly where you’d expect: 0.99.999 < 1.0.0-alpha < 1.0.0 < 1.0.1-beta.
Xavier’s blog post describes “hijacking” the prerelease tag to use for CI. We don’t think it’s hijacking, though. This is exactly what we do on Visual Studio Team Services to create our CI packages. We’ll overcome the paradox by picking a version number, then producing prereleases of that version. Does it matter that we leave a prerelease tag in the version number? For most use cases, not really. If you’re pinning to a specific version number of a dependent package, there will be no impact, and if you use floating version ranges with project.json, it will mean a small change to the version range you specify. Source: https://devblogs.microsoft.com/devops/versioning-nuget-packages-cd-1/
As stated first you still need to pick a version yourself for you new package. For the process before the actual publishing of the final package (master branch in your case) you can use the pre release tag to include the build number as the pre release tag.
Without doing anything smart this will publish a new package for every pipeline that is run. The people at visual studio team services and I do not think that this is a bad thing but this will be up to everyone's personal taste
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With