4 doesn't go into 3

Written on April 25, 2020

There’s different ways of versioning things. Windows has had a 4-part version scheme that is used by itself and most Windows applications. The convention is <major version>.<minor version>.<build number>.<revision>. .NET supports this through classes like AssemblyVersionAttribute and Version.

SemVer is a 3-part scheme (MAJOR.MINOR.PATCH) with optional pre-release or metadata that has seen wide industry adoption, especially by package management tools (npm, NuGet etc).

Using SemVer-versioned components in an ecosystem that uses 4-part versioning is pretty straight forward. Just tack an extra “.0” on the end if it really wants to see 4 parts.

On the other hand, using 4-part versions in a SemVer ecosystem is hard. You can’t just drop one of the parts unless you know with certainty that it will never, ever be significant.

And that’s the problem. How do you preserve information from 4 things when you’ve only got 3 places to put it?

I hit this problem recently when looking to push some versioned data that’s generated on-prem up to an Azure Pipeline so it could be used in the build process. I decided that Azure Artifacts would be a good way to get the data safely into a place where the Pipeline could access it.

Azure Artifacts has built-in support for a number of package formats (NuGet, npm, Maven and others), but this data wasn’t any of those, so the ‘Universal Package’ type seemed most appropriate. But as I was about to discover, Universal Packages are versioned with SemVer 2 (and no metadata allowed).

As the third part of the 4-part version number was zero, I was thinking I could just drop that to convert to a 3-part version number. But after some investigation it turns out that assumption was wrong - the third part can change.

I’m not the first to encounter this problem, and the final comment on that article by Mitch Denny (an Australian developer who I’m honoured to know, and who was the Program Manager for Azure Artifacts at the time) gives an interesting workaround.

1.2.3.4 could be encoded in SemVer as 1.2.3-4

I’m more used to seeing the pre-release with text (eg. 1.2.3-beta.4), but reviewing the SemVer site (point 9), they actually give 1.0.0-0.3.7 as an example, so this should be ok. It is technically a ‘pre-release’ version, but it has preserved all the data.

There’s one final gotcha, and it relates to the version being pre-release.

I mentioned earlier that I was going to use the Universal Package in an Azure Pipeline. To download the package in the pipeline, you use the Universal Package task.

The problem is that this task, like the CLI tooling doesn’t support pre-release versions using wildcards. This is explicitly called out in the documentation for the Universal Packages Quickstart “Wildcard expressions do not currently support pre-release versions. It is not possible to get the latest pre-release version of a package.”

Next steps, see if using I can use the REST API to obtain the version and pass that to the Pipelines task.

Continued in part 2

Categories: Azure Artifacts, Azure Pipelines, Package Management, Versioning