{"id":16485,"date":"2016-05-18T04:16:36","date_gmt":"2016-05-17T21:16:36","guid":{"rendered":"https:\/\/blogs.msdn.microsoft.com\/visualstudioalm\/?p=16485"},"modified":"2019-02-14T17:34:01","modified_gmt":"2019-02-15T01:34:01","slug":"versioning-nuget-packages-cd-2","status":"publish","type":"post","link":"https:\/\/devblogs.microsoft.com\/devops\/versioning-nuget-packages-cd-2\/","title":{"rendered":"Versioning NuGet packages in a continuous delivery world: part 2"},"content":{"rendered":"<p>This is part 2 in a series of blog posts covering strategies for versioning a NuGet package. If you missed part 1, <a title=\"Versioning NuGet packages in a continuous delivery world: part 1\" href=\"https:\/\/blogs.msdn.microsoft.com\/visualstudioalm\/2016\/05\/03\/versioning-nuget-packages-cd-1\/\">pick it up here<\/a>. Today\u2019s post talks about future improvements we\u2019d like to make to the versioning and releasing flows. This post discusses future work that we haven\u2019t fully designed yet, and we need your input. Along the way, you\u2019ll spot a few specific calls for feedback. <a href=\"mailto:vsopackagingteam@microsoft.com\">Email us<\/a>, Send-a-Smile from within the product, or post comments here in the blog.<!--more--><\/p>\n<h2>Using Release Management to promote packages<\/h2>\n<p>When we left off last time, we\u2019d set up a repo, a build, and a feed to hold CI packages. The final paragraph glossed over how to share packages using Release Management. Let\u2019s take a closer look at that flow to understand the positives and negatives. That will set the stage for talking about improvements our team wants to make in the coming months.<\/p>\n<p>The previous feed was named \u201cVersioningDemo.CI\u201d, since it holds CI outputs. Create a second feed, \u201cVersioningDemo.Release\u201d, which will hold released packages. Copy the NuGet v3 URL of this new feed; you\u2019ll need it in a few steps.<\/p>\n<p><a href=\"https:\/\/devblogs.microsoft.com\/wp-content\/uploads\/sites\/6\/2019\/05\/twofeeds.png\"><img decoding=\"async\" class=\"alignnone size-mediumlarge wp-image-16486\" src=\"https:\/\/devblogs.microsoft.com\/devops\/wp-content\/uploads\/sites\/6\/2016\/05\/twofeeds-500x308.png\" alt=\"Two Feeds\" width=\"500\" height=\"308\" \/><\/a><\/p>\n<p>Before any of this will work, we need to make one additional alteration to the build definition. Go to the build definition from last time, find the <strong>Copy files<\/strong> step, and add a new line to the <strong>Contents<\/strong> entry. That line should read: <code>*.nupkg<\/code>. This will ensure that Release Management gets access to the built packages (but not the restored packages that were inputs to the build).<\/p>\n<p><a href=\"https:\/\/devblogs.microsoft.com\/wp-content\/uploads\/sites\/6\/2019\/05\/copystep.png\"><img decoding=\"async\" class=\"alignnone size-mediumlarge wp-image-16495\" src=\"https:\/\/devblogs.microsoft.com\/devops\/wp-content\/uploads\/sites\/6\/2016\/05\/copystep-500x147.png\" alt=\"Copy step\" width=\"500\" height=\"147\" \/><\/a><\/p>\n<p>Then, switch over to the <strong>Release<\/strong> tab and create a new release definition using the Empty template. Set the artifact source to <strong>Build<\/strong> and the build definition to the one you created last time.<\/p>\n<p><a href=\"https:\/\/devblogs.microsoft.com\/wp-content\/uploads\/sites\/6\/2019\/05\/newdef.png\"><img decoding=\"async\" class=\"alignnone size-mediumlarge wp-image-16525\" src=\"https:\/\/devblogs.microsoft.com\/devops\/wp-content\/uploads\/sites\/6\/2016\/05\/newdef-314x350.png\" alt=\"New Release Definition\" width=\"314\" height=\"350\" \/><\/a><\/p>\n<p>Choose <strong>Add task<\/strong>, and from the Package category, choose <strong>NuGet Publisher<\/strong>. Like last time, switch the feed type to <strong>Internal<\/strong>. Paste the NuGet v3 URL of your release feed into the box. (Future feature: A feed picker so you don\u2019t have to do the copy-and-paste dance with raw URLs.)<\/p>\n<p><a href=\"https:\/\/devblogs.microsoft.com\/wp-content\/uploads\/sites\/6\/2019\/05\/reldef.png\"><img decoding=\"async\" class=\"alignnone size-mediumlarge wp-image-16535\" src=\"https:\/\/devblogs.microsoft.com\/devops\/wp-content\/uploads\/sites\/6\/2016\/05\/reldef-500x308.png\" alt=\"Finished Release Definition\" width=\"500\" height=\"308\" \/><\/a><\/p>\n<p>Create a new release (the \u201c+ Release\u201d button), and select a recent run of your build definition. This will stand in for our \u201cknown good, ready to release\u201d package. Assume you\u2019ve done automated testing on the package, shared it with your canary testers, or done whatever else you normally do to validate a release.<\/p>\n<p><a href=\"https:\/\/devblogs.microsoft.com\/wp-content\/uploads\/sites\/6\/2019\/05\/newrelease.png\"><img decoding=\"async\" class=\"alignnone size-mediumlarge wp-image-16515\" src=\"https:\/\/devblogs.microsoft.com\/devops\/wp-content\/uploads\/sites\/6\/2016\/05\/newrelease-494x350.png\" alt=\"New Release\" width=\"494\" height=\"350\" \/><\/a><\/p>\n<p>Navigate to the release (in my case, it was called Release 1), and if you set Environment 1 to be automatic as in the screenshot above, you should see the release either In Progress or Succeeded. And when you check back in the release feed, you should see your package pushed there. Success!<\/p>\n<p><a href=\"https:\/\/devblogs.microsoft.com\/wp-content\/uploads\/sites\/6\/2019\/05\/buildsucceeded.png\"><img decoding=\"async\" class=\"alignnone size-mediumlarge wp-image-16505\" src=\"https:\/\/devblogs.microsoft.com\/devops\/wp-content\/uploads\/sites\/6\/2016\/05\/buildsucceeded-500x254.png\" alt=\"Release Succeeded\" width=\"500\" height=\"254\" \/><\/a><\/p>\n<h2>A better way to indicate quality: descriptors<\/h2>\n<p>Today, to share packages with other teams, you\u2019d suggest that they take a dependency on your Release feed. Now you\u2019re stuck managing multiple feeds with copies of the same package. Also, you\u2019re going back to the build outputs and re-pushing the package to another feed, which feels wrong conceptually. What you\u2019re really trying to express is a <strong>promotion<\/strong> flow \u2013 you\u2019ve promoted a package from being a CI output to being a released artifact.<\/p>\n<p>We\u2019re working on a feature which will better express this promotion flow. Internally we\u2019ve been calling it a <strong>quality descriptor<\/strong>\u00b8 or more recently just a descriptor. A descriptor is lightweight metadata tag<a href=\"#descriptor-tag\">*<\/a>\u00a0attached to the package. We envision having a small default set of descriptors like \u201cCI\u201d, \u201cbeta\u201d, and \u201crelease\u201d to help you express quality gates \u2013 and of course we\u2019d let you define your own.<\/p>\n<p>What really makes descriptors powerful is the ability to filter on them. You would give your partner teams a filtered view of your feed. The filtered feed would appear to contain only packages which you deem have met that particular quality gate.<\/p>\n<p>The flow I described above would no longer rely on re-pushing the package. Instead, the process would simply toggle the \u201crelease\u201d descriptor. The package would then immediately become available to any partner team consuming the filtered feed.<\/p>\n<p>This is call for feedback #1: <strong>What questions or concerns do you have with descriptors?<\/strong><\/p>\n<p>*We\u2019d love to call the feature \u201ctags\u201d instead of \u201cdescriptors\u201d, but decided that the word <em>tag<\/em> is already overloaded in the packaging world. NuGet has one kind of tags, npm has a different kind of tags, and of course there are already work item tags in VSTS\/TFS.<\/p>\n<h2>Selecting version numbers<\/h2>\n<p>Even with descriptors, I have another gripe with this flow. In order to make my package versions unique, we spent most of part 1 generating a prerelease version string. And so long as your consumers don\u2019t mind taking \u201cprerelease\u201d packages (even when you\u2019ve decorated them with a release descriptor), that\u2019s OK. But there are many reasons to want to strip off the prerelease version when you\u2019re truly ready to release.<\/p>\n<p>Today, you could add conditional logic to your version-number-generating script, then re-run the build to produce a non-prerelease package. But a new build means (potentially) different output which you haven\u2019t tested. As a team, we see two options: Either be smarter at picking version numbers initially, or create a re-versioning tool that can safely strip the prerelease segment of a version number.<\/p>\n<h2>Better selection of version numbers<\/h2>\n<p>The first option is to go back to the drawing board on version numbering. Instead of directly controlling major.minor.patch, you could cede control of patch to a tool which would ensure monotonically increasing numbers. Advantage: Really simple to understand \u2013 each build gets a new, unique version number. Disadvantage: From your consumers\u2019 perspective, you\u2019ll have gaps in your version numbering (because it\u2019s unlikely you\u2019ll release every single build you crank out).<\/p>\n<p>Let\u2019s say you\u2019ve specified major and minor versions somehow; call that X.Y. We imagine a tool that requests the next available patch number, which would start at 0 and work upwards. This would generate versions X.Y.0, X.Y.1, and so on. When you need to change X or Y, the next available patch number would reset to 0.<\/p>\n<h2>Stripping prerelease segments<\/h2>\n<p>The other option is to keep generating prerelease versions, then remove them when ready to release. Advantage: Semantically, this is more accurate \u2013 you\u2019re \u201cworking toward 1.0.0\u201d right up until you determine you\u2019ve landed there. Disadvantage: It\u2019s tricky to actually pull off.<\/p>\n<p>Not surprisingly, in the simple case it\u2019s simple: Open up the nupkg, rewrite the <code>&lt;version&gt;<\/code> element in the nuspec, and you\u2019re done. But what if your build produced multiple packages, some of which depend on each other? Now you need to discover the dependency graph and rewrite the appropriate <code>&lt;dependency&gt;<\/code> elements. It\u2019s not impossible to get this right, but rewriting NuGet nuspecs isn\u2019t your core business, is it? Our team would build a REST API (and associated buttons, build tasks, and release tasks) to do the heavy lifting here.<\/p>\n<p>Call for feedback #2: <strong>How do you feel about each of these version numbering ideas?<\/strong><\/p>\n<h2>Immutability<\/h2>\n<p>The last improvement we\u2019d like to make is a direct result of customer feedback and real-world testing. We\u2019ve long held \u2013 and most package management systems in the world agree \u2013 that once pushed, a package is <a href=\"http:\/\/visualstudio.com\/get-started\/package\/feeds\/immutability\">immutable<\/a>. You\u2019re stuck with it forever. Sure, some places let you <a href=\"https:\/\/www.visualstudio.com\/get-started\/package\/nuget\/remove\">unlist<\/a> or even <a href=\"https:\/\/news.ycombinator.com\/item?id=11340510\">delete<\/a> a package, but you cannot change its contents.<\/p>\n<p>But what if you accidentally shipped a bad package, perhaps one containing malicious content? The standard packaging answer is to unpublish\/delete and publish again with a new version number. And for a public registry like NuGet.org, that\u2019s the only reasonable stance. What\u2019s worse than having a bad package called \u201cfoo 1.0.0\u201d in the wild? Having both a bad and a good package in the wild and being unable to tell at a glance who has the good one and who has the bad thanks to multiple levels of caching, the exact timing of package installs, and more.<\/p>\n<p>As a private package host, though, our customers know their scenarios and their ecosystem best. Many folks have told us they want an option to make feeds mutable or immutable, and they\u2019re willing to deal with any consequences of making that decision. Some people don\u2019t want to \u201cburn\u201d a good version number like 1.0.0 just because they accidentally published the wrong package.<\/p>\n<p>We are considering just such a feature. We would still make feeds immutable by default and would issue a strongly-worded warning when you turn it off. We\u2019re still deciding whether we should require an explicit delete-then-repush flow, or if we would allow direct overwrites upon a new push.<\/p>\n<p>This feature should be used only on non-shared, staging-area feeds with a promotion flow that copies packages to an immutable feed for public consumption.<\/p>\n<p>Call for feedback #3: <strong>How do you envision using mutable feeds?<\/strong><\/p>\n<h2>Wrap up<\/h2>\n<p>Thanks in advance for feedback\u00a0(<a href=\"mailto:vsopackagingteam@microsoft.com\">email<\/a>, Send-a-Smile, or here in the comments) on these future capabilities. And if you\u2019re a Git user, go read\u00a0<a title=\"Versioning NuGet packages in a continuous delivery world: part 3\" href=\"https:\/\/blogs.msdn.microsoft.com\/visualstudioalm\/2016\/05\/26\/versioning-nuget-packages-cd-3\/\">part 3<\/a>, the final in this series, where we\u2019ll take a walk through <a href=\"http:\/\/gitversion.readthedocs.org\/\">GitVersion<\/a>\u2019s features.<\/p>\n","protected":false},"excerpt":{"rendered":"<p>This is part 2 in a series of blog posts covering strategies for versioning a NuGet package. If you missed part 1, pick it up here. Today\u2019s post talks about future improvements we\u2019d like to make to the versioning and releasing flows. This post discusses future work that we haven\u2019t fully designed yet, and we [&hellip;]<\/p>\n","protected":false},"author":719,"featured_media":45953,"comment_status":"closed","ping_status":"closed","sticky":false,"template":"","format":"standard","meta":{"_acf_changed":false,"footnotes":""},"categories":[1,225,223],"tags":[],"class_list":["post-16485","post","type-post","status-publish","format-standard","has-post-thumbnail","hentry","category-devops","category-git","category-package-management"],"acf":[],"blog_post_summary":"<p>This is part 2 in a series of blog posts covering strategies for versioning a NuGet package. If you missed part 1, pick it up here. Today\u2019s post talks about future improvements we\u2019d like to make to the versioning and releasing flows. This post discusses future work that we haven\u2019t fully designed yet, and we [&hellip;]<\/p>\n","_links":{"self":[{"href":"https:\/\/devblogs.microsoft.com\/devops\/wp-json\/wp\/v2\/posts\/16485","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/devblogs.microsoft.com\/devops\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/devblogs.microsoft.com\/devops\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/devblogs.microsoft.com\/devops\/wp-json\/wp\/v2\/users\/719"}],"replies":[{"embeddable":true,"href":"https:\/\/devblogs.microsoft.com\/devops\/wp-json\/wp\/v2\/comments?post=16485"}],"version-history":[{"count":0,"href":"https:\/\/devblogs.microsoft.com\/devops\/wp-json\/wp\/v2\/posts\/16485\/revisions"}],"wp:featuredmedia":[{"embeddable":true,"href":"https:\/\/devblogs.microsoft.com\/devops\/wp-json\/wp\/v2\/media\/45953"}],"wp:attachment":[{"href":"https:\/\/devblogs.microsoft.com\/devops\/wp-json\/wp\/v2\/media?parent=16485"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/devblogs.microsoft.com\/devops\/wp-json\/wp\/v2\/categories?post=16485"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/devblogs.microsoft.com\/devops\/wp-json\/wp\/v2\/tags?post=16485"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}