NuGet Package Versioning (SemVer2, Legacy)
  • 15 Jul 2021
  • 4 Minutes to read
  • Dark
    Light
  • PDF

NuGet Package Versioning (SemVer2, Legacy)

  • Dark
    Light
  • PDF

All NuGet packages - whether hosted on NuGet.org or in your own ProGet repository - have version numbers like 4.2.3, 3.1.2.50012, 6.2.0-beta1, etc. The format of these version numbers is called a version number scheme, and NuGet has had several different schemes over the years.

This article helps explain how ProGet interacts with different types of NuGet package version numbers.

NuGet SemVer Package Versioning

Modern versions of ProGet (v5 and later) as well as NuGet (i.e. 4.3/2018+) support SemVer2 versioning for packages. This is a standardized versioning scheme across many packages, and has become intuitive to many software developers.

Best Practices: Use SemVer2

Use SemVer2-compatible versioning for your NuGet packages. We recommend it, Microsoft recommends it, and your future developers will appreciate it.

A SemVer2-formatted version number consists of three integer parts (Major, Minor, and Patch), as well as an optional Prerelease suffix. Each component carries a meaning:

  • Major: Breaking changes
  • Minor: New features, but backwards-compatible
  • Patch: Backwards-compatible bug fixes
  • Prerelease: A nonstable package like alpha, beta, etc.

Example versions include: 1.0.1, 6.11.1231, 4.3.1-rc, 2.2.44-beta1.

See Microsoft's NuGet Package Versioning Documentation to learn more about how to use version numbers in your packages.

Build Metadata

There's a fifth, and rarely used component in SemVer2 called Build metadata. It begins with a plus sign (+) and may contain any of the characters that prerelease suffixes are allowed to contain. Build metadata is ignored when comparing version numbers, so 1.2.3+foo and 1.2.3+bar are considered to be the same version.

In addition, NuGet generally ignores build metadata when requesting packages (e.g. if you specify for 1.2.3+foo, NuGet will only ask for 1.2.3); as such, ProGet generally doesn't require build metadata when requesting packages. We say "generally", because there are bugs and quirks in different versions of NuGet (as well as ProGet).

Best Practices: Avoid Build Metadata in NuGet packages

It's not very useful in NuGet, adds confusion, and you can simply inject additional metadata into your .nuspec as needed.

Legacy NuGet Version Numbers

Prior to supporting SemVer2 versioning, NuGet used a 4-digit versioning scheme (e.g. 3.4.1.5) that is a combination of .NET's System.Version class and SemVer1 (an older, obsolete specification). This format isn't very well documented.

ProGet considered these "Legacy version numbers", and defines them as:

  • four decimal integers (digits0-9) separated by dots (.)
  • optionally have a pre-release indicator, which is a hyphen (-) followed by a sequence of letters (a-z, A-Z), digits (0-9), and hyphens (-). Pre-release indicators must be at least one character long and must start with a letter.

Modern versions of ProGet and NuGet should fully support these types of version numbers.

Quirky Version Numbers

Ancient versions of NuGet (i.e prior to 3.4/2016) had very few rules, and the rules would change from version to version of NuGet. Versions could be 1, 2, 3, or 4 part. In addition, equality rules were a bit strange. For example, 1.0, 1.000, and 1.0.0 were considered different versions numbers in most cases... except for when they weren't. Likewise, 6.4 and 6.04 were almost always different versions, depending on how you requested them from NuGet.

We call these "quirky version numbers", and fortunately they have been disallowed since 2016 on nuget.org. Unfortunately, there are lots of "quirky version" packages created before 2016 that people still want to use.

A great example of a quirky package is Owin. There's a single version (1.0), and it hasn't changed since 2012. It's considered a "quirky version" becuase it's only two-part number.

NuGet.org and Quirky Versions

You cannot upload quirky version packages to NuGet.org, but the old ones still exist.

NuGet.org now only shows a "normalized", three-part version number for quirky packages. Even if the package version (as specified by the nuspec file) is 1.0, NuGet.org will return 1.0.0 as the version number in the API.

You'll notice Owin is listed as 1.0.0, even though the actual version is 1.0. The only way to see the actual version is to download the file, and look in the .nuspec file:

  <metadata>
    <id>Owin</id>
    <version>1.0</version>
    ...snip...
  </metadata>

However, you still request Owin-1.0 by name, directly. NuGet.org will return the one (and only) version of the Owin package.

NuGet Client Support for Quirky Versions

Because not all NuGet servers behave like NuGet.org, the NuGet client (Visual Studio) has to do some interesting workarounds, including "the version dance" to make sure it can find a requested package.

For example, it a package has a dependency on Owin-1.0, then you'll see NuGet make multiple requests to the package source: first 1.0.0, and then 1.0.0.0, then 1.0, and finally 1.

This allows the NuGet client to work with both nuget.org, and legacy NuGet servers.

ProGet Support for Quirky Versions

You can still upload "quirky version" packages to ProGet in many cases, and most quirky packages will work in most cases. However, they may cause headaches in some cases, especially when it comes to connectors, and operations surrounding local packages (like promotion and repackaging).

For example, Owin will show up as 1.0.0 via a connector to NuGet.org, because that's what NuGet.org is reporting. And in most cases, it will work just fine, even though it's really 1.0 behind the scenes.

However, when you pull Owin to ProGet, then ProGet will use the "true" version number (1.0). It will also show Owin 1.0.0.

Old versions (prior to 5.3) has a "Legacy (Quirks) Nuget" feed that would handle all this weirdness, but those feeds couldn't handle SemVer queries properly, since requests like 1.0 were ambiguous (should it return 1.0 or 1.0.0, both of which are valid versions, etc), so it was a big tradeoff. They were fully removed in ProGet 5.3.

Because there are very few packages with quicks like this, we recommend simply downloading the package, editing the nuspec file, and replacing it with a semantic version.


Was this article helpful?