Using CI/CD Tools to Enforce SemVer When Migrating NuGet Packages to .NET
When pre-release packages end up in Production, you risk bugs, breaks, and even security breaches. Poor package naming schemes make this all too easy. With a .NET 5+ migration likely in your future, now is the best time to use repackaging to enforce SemVer on your private NuGet packages.
.NET 5 can’t use your .NET Framework Libraries and vice versa, so you can’t just use them as-is. We discovered five methods for migrating NuGet packages between .NET Framework and .NET 5 when migrating our own private packages. While each application and library is unique and must be treated accordingly, these five methods are a great start.
Learn how to enforce semantic versioning inside ProGet and BuildMaster as you migrate your .NET Framework libraries using any of the five methods.
Five Ways to Migrate
As we said in a previous article, we’ve uncovered five methods for migrating your first-party .NET Framework NuGet packages as you prepare to move to .NET 5+. From easiest to hardest, they are abandon, update, fork, multi-target, and bridge.
|Method||When to Use||How to Execute|
|Abandon||Libraries using deprecated technologies||Update the code as needed for security patches and bug fixes and release with CI/CD|
|Update||Most applications will use .NET 5+ but some old applications using .NET Framework endure||Use a wide gap in the SemVer numbers to leave room between the .NET Framework library’s last version number and the first .NET 5+ version number. Release using CI/CD|
|Fork||Framework and 5+ applications that will coexist in the mid-to-long-term||Copy the library, retarget to .NET 5+, fix the code errors, and use CI/CD to publish the package with a new, semantically versioned name|
|Multi-target||Efficiently supporting multiple applications on different .NET platforms and keeping libraries in sync||Compile your C# code, pick the assembly type (.NET Framework or .NET 5+). Since NuGet packages have libraries under a subfolder named after the type of assembly, your package can have both a .NET Framework library (under the .net452 folder to specify version 4.5.2) or a .NET 5+ library (under the net5.0 folder). Use CI/CD to enforce SemVer|
|Bridge||Buy time before making another choice (only if your code supports it)||Use .NET Standard as a bridge between .NET Framework and .NET 5; use CI/CD to semantically version|
You must assess each package in context and make the best decision for each, but some general advice is:
1. Assess how many applications use each library.
2. Assess when (if ever) those applications will migrate to .NET 5
3. Rule out methods that will NOT work for your code
4. And for every method, enforce SemVer
NuGet is built using SemVer, and repackaging helps you enforce SemVer
As we’ve said in a previous article:
What distinguishes a “package” from a random zip file is that, once a package has been created, it should never be modified. Since the version number is part of the embedded metadata of a package, editing the same version will create confusion or even conflicts. Instead, use a new version number to indicate when a package has been altered.
NuGet itself was built on a semantically versioned naming system and its dependencies rely on SemVer. If this wasn’t reason enough to use a SemVer naming scheme, it’s also nearly impossible to confuse because its rules are strict and numerical.
Semantic Versioning (or SemVer) is a software naming system indicating backwards (in)compatibility. SemVer requires three digits: Major.Minor.Patch.
changes that will be incompatible with previous versions
added functionality that will still be backward-compatible (in this example 3.1.0 will be compatible with 3.0.0)
minor bug fixes or security patches that should always be fully backward-compatible
Further quality can be indicated with prerelease tags like -ci or -rc, which should never be used in production.
No matter which of the 5 migration methods you choose, using repackaging to enforce SemVer is the way to do the job right the first time, and using DevOps tools helps you do this automatically. Repackaging is the process of creating a new packages from an existing one, changing only the name to indicate quality or approval. Repackaging is a simple way to enforce SemVer for your private NuGet packages via automation tools.
Using BuildMaster and ProGet to Enforce SemVer
Before you begin, you’ll need:
- A ProGet instance (free-forever is fine)
- A BuildMaster instance (free-forever is fine)
- .NET SDK installed (this tutorial used .NET 5 SDK 5.0.3)
We also recommend having Windows clipboard enabled, so that when you hit Windows Key v, you’ll have a history of your copied items. This is not a necessity, however.
Once these are installed, follow these steps:
Whether you create a new NuGet feed in ProGet or use an existing one, copy the API URL from the top right corner. You’ll need it so BuildMaster and ProGet can communicate.
Next, navigate to ProGet’s settings by clicking the gear icon and select ‘API Keys’ and ‘Create API Key.’
Name it as you like, and select the boxes ‘Grant Access to Feed API’ and ‘Grant Access to Repackaging API,’ then save. Note that you can limit the users who may access the API key with the ‘Impersonate User’ field; otherwise, it’s a global key. ProGet can also auto-generate a key.
Now go into BuildMaster. Create a new application using the NuGet CI/CD application template. Name it as you like, and save.
After the template is applied, you’ll need to create a secure resource to clear the first warning shield.
To do this, click on ‘Secure Resources’ at the top right of the set-up template, select ‘Create Secure Resource,’ and select ‘NuGet Package Feed,’ then ‘Create…’ near the bottom of the pop-up.
Create a new API key/token and name it, then save. Back on the ‘Create Secure Resource’ window, name it and paste the Feed URL you grabbed from ProGet. Then, use the dropdown to select the new API key you just created, and save.
Return to the application set-up template by clicking “Application Setup.’ Clear the warning shields by adding the secure resource name you just created and adding your package name. If you’re just testing this, copy the example package from the description line, ProfitCalc.NuGet (see image).
Your NuGet CI/CD pipeline is now ready to use, so create a new build in BuildMaster. You’ll be prompted to create a release, and then a new build.
If you’ve configured everything correctly, when you go back to ProGet, you’ll see a new package with -ci ending in the ProGet feed.
ProGet clearly indicates that it’s a prerelease version, and you can view the metadata, files, and more by clicking the package name.
As you deploy your package through this NuGet CI/CD pipeline, BuildMaster will continue to push pre-release and finally the stable version of your package to ProGet.
You can customize the BuildMaster pipeline to meet your team’s needs, from adding automated executions to extra manual approvals and more.
Get More Value by Using Both Tools
While ProGet is doing the actual repackaging, combining the two tools is a great way to live the ‘CI/CD lifestyle.’ BuildMaster adds communication and security features to the repackaging process like:
- Shared pipelines (to give better visibility to the whole team)
- Security and access controls (to maintain visibility without compromising security)
- A NuGet-specific application startup template (to democratize DevOps)
- Transparent approvals to prevent applications using prerelease version from going to production (to increase both communication and software quality)