How to use CI/CD pipelines for your NuGet Packages
by Eric Seng, on Oct 4, 2021 7:30:00 AM
CI/CD for NuGet packages isn’t just a pain – it sometimes feels downright impossible. Many teams get frustrated at CI/CD for their NuGet pipelines since it conflicts with the best practices they’ve implemented for their application pipelines.
CI/CD helps your team produce, test, and deliver NuGet packages faster and better. But many teams choose to completely skip it because of the frustration that comes with a ton of builds, a ton of packages, most of which will never be used, and how it “simply doesn’t seem to work.”
In this article, I’ll show you why and how to do CI/CD with your NuGet packages.
Challenges with CI/CD & NuGet
Teams who are new to building/using their own NuGet packages with CI/CD are often frustrated because of two main challenges they face:
- An overwhelming number of packages, many of which will never be used.
- Teams are restricted and unable to use approved/tested pre-release packages in production. Alternatively, your team just uses pre-release packages
Without CI, you just make a NuGet package when you were ready to publish it. But with CI you've got dozens and dozens of packages to deal with, a lot more library changes, more CI builds, and more difficulty keeping track of all the version numbers that are coming from CI.
The disconnect many teams run into is when they try to apply application pipeline best practices for their NuGet packages. Although the benefits are the same and the end goal is to create more stable software, using application CI/CD practices to NuGet will only ever cause frustration.
✔ CI/CD for Applications
It's pretty straightforward: a build artifact will only make its way to the final stage (Production), once it's been tested in a number of environments.
⚠ CI/CD for NuGet Packages?
This is where there's a lot of frustration: a NuGet package is basically unusable until it’s at the last stage of the pipeline (Publishing).
The CI/CD and NuGet Disconnect
This frustration and disconnect come from these three best practices your team is following:
- Packages are Immutable (Read-only). Once published, a package file cannot be modified. You can't "edit" a version number of a package, or change its status, because the version number is part of the metadata embedded in the file.
- Untested code shouldn't be deployed. Rebuilding a NuGet package can produce different software due to wildcard version dependencies, and that means you need to test code you've just built before you can deploy it.
- Deploy only stable (non-prerelease) packages. By the name alone, it doesn't make sense to deploy prerelease packages to a production environment. Only stable versions should ever be released with your application.
Three Non-options for NuGet C/CD
When a team starts CI for their NuGet Packages they can expect a ton of builds and packages, most of which will never be used. Most teams see only three options to choose from, all of which are far from perfect.
✗Use new Version Numbers at build time
How: Every time you make a new build, you create a new version. Use three-digit versioning (e.g. 3.4.2, 3.4.3, 3.4.112) or Use four-digit versioning (e.g. 220.127.116.11, 18.104.22.168, 22.214.171.124) for every new unstable package version.
Pro: It’s clearly communicated which version is the latest.
Con: Neither of these follows SemVer and neither communicate which package is stable. Furthermore, choosing to use three-digit versioning means you have to sacrifice an entire number (major, minor, or patch) to communicate the pre-release version.
✗Overwrite packages when you publish
How: Download your package (e.g. 3.4.2), overwrite every time you build, and then re-upload it.
Pro: Your team isn’t overwhelmed by tons of new packages and builds.
Con: This breaks the immutability rule, there are many "Versions" of "version 3.4.0" and it’s impossible to know when it’s stable. On top of this, overwriting creates issues with caching. Visual Studio (and CI servers) generally won't download a package already downloaded. Team members will have to clear the package caches to use the most recent v3.4.0.
✗Deploy prerelease packages
How: Add pre-release labels to the end of your package (e.g. -ci.1, -ci.2, ci.11). A package is tested in your application and when it passes, is ready to be released to production.
Pro: The package's quality is clearly labeled, and you can apply all your standard CI/CD best practices to this pipeline. This is the best option to choose.
Con: You don't use the pre-release, unstable version of the package in your application because your team follows proper CI/CD best practices. So you have to create a whole new stable version of the package and send it through the pipeline all over again in a CI/CD loop.
This is where many teams start to feel frustrated and inundated with an overwhelming amount of packages. Your team is forced to choose between wasting your time creating multiple packages that will never be consumed, or completely breaking SEMVER.
✔ The Secret to CI/CD for NuGet
While it may seem difficult to reconcile package immutability, Semantic Versioning, and Continuous Integration for your NuGet packages, using a technique called "repackaging" will let you use these best practices.
Repackaging creates a new package from an existing package, using exactly the same content but changing the name. For example, Build 8 yields 1.0.0-ci.8 for testing; once approved and repackaged, a release candidate (1.0.0-rc.8) is created. Then finally, a stable version, 1.0.0 will be created for deployment in production applications.
How to Repackage a NuGet Package
A NuGet package is really just a zip file with a .nupkg extension, which makes repackaging take just a few simple steps:
- downloading the package
- opening the package as a zip file
- editing the .nuspec manifest file and changing the version number
- publishing the package file
Obviously, this is not something you'd want to do manually. You can write a script to tell your CI/CD tool to include repackaging in the pipeline (our BuildMaster can do this), or you can use a package server with built-in repackaging, like ProGet.
CI/CD for NuGet packages can feel nearly impossible when your team follows application CI/CD best practices. However, by adapting repackaging into your CI/CD pipeline, you can ensure that what goes to production is exactly what you have tested.
While CI/CD is important, there's a lot more to successfully using NuGet in the Enterprise. Sign up for our free guide to learn more!