Continuous Delivery of your NuGet Packages with VSTS – SemVer


This post is a continuation of my first post on the topic of NuGet packages.  This post, however, will focus on using Semantic Versioning of your NuGet packages.  The previous post published a NuGet package and used the Package Management Views in VSTS to demonstrate the quality of the package as it moved through the Release Pipeline.

The process to do the Semantic Versioning is similar to the first one with a few exceptions:

  1. The Build Definition no longer executes the NuGet Pack task (this is done during release)
  2. The Artifacts that are produced by build includes everything we need to execute the NuGet Pack task (binaries, .nuspec, scripts, etc.)
  3. Semantic Versioning 2.0.0 support requires NuGet 4.3.0 or higher, you can see the other requirements documented here.
  4. Version numbers do not support leading zeros.  Why is this important? The build number format must not use $(Rev:.rr) or else we will violate the spec and the NuGet pack command will fail.

    If we take a look at the relevant part of the SemVer 2.0 spec:

    A normal version number MUST take the form X.Y.Z where X, Y, and Z are non-negative integers, and MUST NOT contain leading zeroes. X is the major version, Y is the minor version, and Z is the patch version. Each element MUST increase numerically. For instance: 1.9.0 -> 1.10.0 -> 1.11.0.

    A pre-release version MAY be denoted by appending a hyphen and a series of dot-separated identifiers immediately following the patch version. Identifiers MUST comprise only ASCII alphanumerics and hyphen [0-9A-Za-z-]. Identifiers MUST NOT be empty. Numeric identifiers MUST NOT include leading zeroes. Pre-release versions have a lower precedence than the associated normal version. A pre-release version indicates that the version is unstable and might not satisfy the intended compatibility requirements as denoted by its associated normal version. Examples: 1.0.0-alpha, 1.0.0-alpha.1, 1.0.0-0.3.7, 1.0.0-x.7.z.92.

Now that we have that out of the way here is the build definition I have modified from my previous post:

Disabled NuGet Pack task

SNAG-4160

Disable the NuGet pack command as part of build

Copy all files required to create a NuGet package during the release pipeline

SNAG-4162

Copying all project files, including .nuspec

Note this Copy **\*  may not be practical in a production environment in which case you could just list the specific folders and files required (just remember the file paths in your .nuspec).  Here is a much better example suited to real life DevOps:

SNAG-4164

We only copy what we need to execute NuGet pack

 

Fix the Build Number Format so we don’t throw an error when we execute NuGet pack

SNAG-4161

Make sure you have no leading zeros in your Build Number Format string

Now given we have done all of that we should be able to kick off a build to make sure we have all our required artifacts.  We also want to ensure we have no leading zeros in our version number, we do not so we can continue to our new release definition.

SNAG-4165

Artifacts Explorer – Make sure we got all our files

 

The Release Definition to Publish Packages using Semantic Versioning

In this example I have four (4) environments (alpha, beta, prerelease, production) that represent the prerelease and production of my NuGet package

SNAG-4166

The first environment is triggered immediately after the build is completed and creates a NuGet package with an alpha prerelease version. Example: 1.0.0.alpha.1

Here is a list of the tasks the Agent exucutes in each environment shown above:

  • NuGet Tool Installer (we require v4.3.0 or higher)
  • Execute PowerShell Script (this updates the version element in the .nuspec)
  • NuGet (we execute NuGet pack to build our .nupkg)
  • NuGet (we execute NuGet push to publish our package to the VSTS package feed)
  • Execute PowerShell Script (this puts the package into the appropriate View based on the environment name)

Here are some screen captures for each of the tasks:

 

SNAG-4167

NuGet Tool Installer

SNAG-4168

Execute ApplyVersionToAssemblies.ps1

 

SNAG-4169

NuGet pack

SNAG-4170

NuGet push

SNAG-4171

Execute Set-PackageQuality.ps1

 

If we look at the Release of one of the Environments (Beta) we can see that all steps were successful:

SNAG-4174

This is showing the output from the NuGet push task

Here is the output which shows the package version that was published to out feed:

2018-03-17T19:04:49.2932124Z ##[section]Starting: NuGet push
2018-03-17T19:04:49.2937456Z ==============================================================================
2018-03-17T19:04:49.2937590Z Task : NuGet
2018-03-17T19:04:49.2937809Z Description : Restore, pack, or push NuGet packages, or run a NuGet command. Supports NuGet.org and authenticated feeds like Package Management and MyGet. Uses NuGet.exe and works with .NET Framework apps. For .NET Core and .NET Standard apps, use the .NET Core task.
2018-03-17T19:04:49.2937989Z Version : 2.0.21
2018-03-17T19:04:49.2938103Z Author : Microsoft Corporation
2018-03-17T19:04:49.2938213Z Help : [More Information](https://go.microsoft.com/fwlink/?LinkID=613747)
2018-03-17T19:04:49.2938329Z ==============================================================================
2018-03-17T19:04:49.9083924Z [command]C:\Windows\system32\chcp.com 65001
2018-03-17T19:04:49.9084369Z Active code page: 65001
2018-03-17T19:04:49.9086750Z SYSTEMVSSCONNECTION exists true
2018-03-17T19:04:49.9087030Z Detected NuGet version 4.5.0.4696 / 4.5.0
2018-03-17T19:04:50.6199934Z Saving NuGet.config to a temporary config file.
2018-03-17T19:04:50.6271979Z ##[warning]No package sources were found in the NuGet.config file at D:\a\r1\a\Nuget\tempNuGet_1232.config
2018-03-17T19:04:50.6301861Z 
[command]D:\a\_tasks\NuGetCommand_333b11bd-d341-40d9-afcf-b32d5ce6f23b\2.0.21\VstsNuGetPush\0.13.0\VstsNuGetPush.exe 
D:\a\r1\a\packages\ContosoLogger.Library.0.1.18076-beta.1.nupkg
-Source https://like10.pkgs.visualstudio.com/_packaging/38fdd1ec-29da-4fd2-8179-c8df385b3532/nuget/v3/index.json -AccessToken ******** -NonInteractive -Verbosity Detailed
2018-03-17T19:05:00.9137246Z Trying to authenticate with auth token.
2018-03-17T19:05:00.9137814Z Successfully authenticated.
2018-03-17T19:05:00.9138077Z Authentication and request took 00:00:04.9081141
2018-03-17T19:05:00.9138383Z Adding package ContosoLogger.Library.0.1.18076-beta.1.nupkg to feed 38fdd1ec-29da-4fd2-8179-c8df385b3532 on https://like10.pkgs.visualstudio.com/.
2018-03-17T19:05:00.9138645Z Trying to add package to feed without uploading.
2018-03-17T19:05:00.9138844Z Adding package to feed.
2018-03-17T19:05:00.9139085Z The package content is not already on the service.
2018-03-17T19:05:00.9139285Z Uploading package content.
2018-03-17T19:05:00.9139822Z Done uploading package content.
2018-03-17T19:05:00.9140135Z Adding package to feed.
2018-03-17T19:05:00.9140337Z Successfully added package to feed.
2018-03-17T19:05:00.9347185Z ##[section]Finishing: NuGet push

VSTS Packages

We will find this package in the Beta view because of the Set-PackageQuality.ps1 script.
SNAG-4175

Package View (Beta)

 

Visual Studio NuGet Package Manager

If we take a look at Visual Studio in the NuGet package manager we can see the release and prerelease versions of our NuGet package

 

SNAG-4176

Sample NuGet Package with Release and Prerelease versions

 

Hope you find this useful and if you have any feedback let me know.

Thanks!

About Wes MacDonald

Wes MacDonald is a DevOps Consultant for LIKE 10 INC., a DevOps consulting firm providing premium support, guidance and services for Azure, Microsoft 365 and Azure DevOps.

19 Responses to “Continuous Delivery of your NuGet Packages with VSTS – SemVer”

  1. what happens to the alpha package when it is propagated to beta?
    any projects using the alpha package will than fail if it is no longer available locally in the project and the NuGet setting not allowing to upgrade the verison.
    Especially you would no longer be able to build an older version of your application to verify a bug because it is no longer buildable.

    Like

  2. Where can I find this “ApplyVersionToAssemblies.ps1” script (the one I found doesn’t take arguments you provide).

    Like

  3. How would you update a project that references this Nuget throughout the release pipeline? I am using project references for the nuget, When checking in, my project would most likely be pointing at beta, but I will need that to be updated down the pipeline.

    Like

    • Hi,

      The project that references the NuGet package must be updated manually by the developer. If you see my other post using Release Views titled “Continuous delivery of your NuGet packages with VSTS” the developer would not need to update the NuGet references unless a new version is put into the release pipeline.

      HTH,
      Wes

      Like

      • Ohh… So you do not rely on using the tag suffix for example “-beta001” for the identification at all.

        So if you have a developer that needs to reference a recent change (prior to release), he would just reference the CI version, and since it can only be in one view at a time, referencing that version will remain the same throughout the pipeline.

        If my assumption is correct,
        The project is unaware of the feed, the feed is just used by the developer to help decide which package to use.

        The only question left, is that when using SemVer2.0 and Nuget, you are supposed to be able to set the package to use up to the next major version if desired.

        Wouldn’t a package in the CI view be picked up in the NuGet restore? If so, I don’t see how this could work unless a requirement for this is only targeting specific versions of a package…

        That is an advantage to having the tag suffix, because you can still have auto latest, without grabbing beta versions

        Like

  4. These are a great couple of posts, but I’m wondering if you’ve had any issues with the nuget pack command not finding or including other nuget dependencies? I’ve followed your posts to a T and can get my package built if I don’t include any references to any other nuget packages, but as soon as I do the nuget pack command fails because it is unable to locate the dependencies (despite the packages folder being included in my build artifact). Have you run into this issue at all?

    Like

  5. These are a great couple of posts, but I am wondering if you’ve run into any issues with the nuget pack command not including as dependencies any reference nuget packages in the project? I am able to publish a nuget package if my project does not reference any other nuget libraries. However, as soon as I take a dependency on another nuget package the nuget pack command fails because it can’t locate the dependency – despite the fact that the packages folder is copied as part of my build artifact. Have you run into this issue at all, and if so, what was your workaround?

    Like

  6. FYI,
    Retention in VSTS will only get rid of non-tagged nuget packages. I would propose removing the set-package quality for the temporary packages. Then set the retention to 20 or so depending on team size.

    So it would look like the below – No tags on alpha
    1.0.1-alpha (Created by CI Builds)
    1.0.1-prerelease (tag with @Prerelease)
    1.0.1 (tag with @Release)

    Also I think that doing a tutorial using GitVersion (with the Mainline option) would be really beneficial to a lot of people. I haven’t found anyone providing tutorials using GitVersion, but it is a really helpful tool for auto-versioning.

    Like

  7. hi Macdonald,

    in the Nuget Pack Step what is the auto version set to in your example above?
    I tried using build number but since it is used in Release the build number option in not available and I get below error :

    “##[error]Autoversion: Getting version number from build option is not supported in releases
    2018-10-09T21:33:55.6771605Z ##[section]Finishing: NuGet pack”

    Like

    • Hi Ashma,

      The Automatic package versioning is set to off. I have a PowerShell task immediately before NuGet Pack which calls ApplyVersionToAssemblies.ps1 with the following arguments: -suffix $(Release.EnvironmentName) -semVer “2.0”

      That PowerShell task updates the .nuspec file with the correct version which you will see in the logs when NuGet Pack processes the .nuspec

      Logfile Example:

      2018-05-22T22:21:02.9288460Z Attempting to build package from ‘ContosoLogger.Library.nuspec’.
      2018-05-22T22:21:02.9290470Z
      2018-05-22T22:21:02.9291943Z Id: ContosoLogger.Library
      2018-05-22T22:21:02.9292463Z Version: 0.1.18076-alpha.1

      Regards,
      Wes

      Like

  8. Hi Wes, Great post which has been really helpful, although Im struggling with some fine detail.

    I am trying to use this for an SSDT project.
    Im struggling to see how the ApplyVersionToAssemblies.ps1 will work as part of the release. It is looking for an .sqlproj file and assemblyinfo in properties folder, thse would only exist during build?

    At release stage, the project has already been built and bin folder deployed to drops folder which only contains artifacts (dlls, nuspec etc).

    Also, I notice you still have the ‘Execute ApplyVersionToAssemblies’ step in your build, the same script which is now called during release? Is that intentional. We do not know the release environment at this point.

    Thanks
    Jason

    Like

    • Hi Jason,

      We only execute this during release to perform semantic versioning of our NuGet package in each stage.

      Your SSDT project only needs this executed during build, the version will be in the .dacpac

      Hope that helps.

      Wes

      Like

  9. Hey Wes,

    Great stuff here. This looks like a great pipeline setup for planned work. My question is let’s say you have released 1.0.0 through this setup with alpha, beta, release versions of the package making it release.

    Now, you have started on version 2.0.0 of the package. How do you handle a bug fix to the 1.0.0 version if you need to get that out before 2.0.0 is released?

    Thanks in advance!

    Adam

    Like

    • Hey Adam,

      Great question, obviously we’d have to allow the editing of Major & Minor variables including specifying a branch or tag so the correct code gets built and tagged so it is published to the proper feed.

      I think that would give you what you need.

      Wes

      Like

      • I’m working on a similar approach that works with DevOps TFVC for a client with a standing Development branch that auto builds pre-release versions similar to your setup. However, we’re reading the Version from the .csproj file. Our thinking is that the developer really needs to think about the “nature of the change” they are making.

        Devopment branch builds auto append a pre-release tag like “-dev.1234” so that you don’t have to keep bumping the version in .csproj for every little commit. These are essentially “release candidate” builds. Once it’s signed off on, the Development branch would get merged to Release branch and that would trigger a Release build and drop the pre-release suffix “-dev.1234” resulting in 1.0.0, for example.

        If we need to do a bug fix/hotfix, we can either make a change directly on Release branch or branch from the Release branch and create a Hotfix branch and merge back into Release for something like 1.0.1.

        The reason we’re going with two different builds is so we can have Debug compilation of -dev pre-packages and Release compilation of production builds (i.e. 1.0.0).

        We’ve got debugging working with debug versions and source symbols, but still trying to get debugging to work with release versions and source symbols.

        I really like your release promotion strategy as it “feels” better than running a separate build from a separate branch. If I could iron out the bug fix/hotfix strategy and debugging of production nuget packages, I think we could simplify with just one build and a multi-phase release/deployment like yours.

        Curious to get your thoughts.

        Like

  10. Hey Wes,

    Great stuff here. This looks like a great pipeline setup for planned work. My question is let’s say you have released 1.0.0 through this setup with alpha, beta, release versions of the package making it release.

    Now, you have started on version 2.0.0 of the package. How do you handle a bug fix to the 1.0.0 version if you need to get that out before 2.0.0 is released?

    Thanks in advance!

    Adam

    Like

Leave a comment

This site uses Akismet to reduce spam. Learn how your comment data is processed.