{"id":16776,"date":"2016-05-26T21:20:39","date_gmt":"2016-05-26T14:20:39","guid":{"rendered":"https:\/\/blogs.msdn.microsoft.com\/visualstudioalm\/?p=16776"},"modified":"2019-02-14T17:33:59","modified_gmt":"2019-02-15T01:33:59","slug":"versioning-nuget-packages-cd-3","status":"publish","type":"post","link":"https:\/\/devblogs.microsoft.com\/devops\/versioning-nuget-packages-cd-3\/","title":{"rendered":"Versioning NuGet packages in a continuous delivery world: part 3"},"content":{"rendered":"<p>This is the third and final post in a series covering strategies for versioning a NuGet package. If you missed <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\/\">part 1<\/a> or <a title=\"Versioning NuGet packages in a continuous delivery world: part 2\" href=\"https:\/\/blogs.msdn.microsoft.com\/visualstudioalm\/2016\/05\/18\/versioning-nuget-packages-cd-2\/\">part 2<\/a>, you should read those first. Today\u2019s post walks through a specific workflow that Git users could adopt, using a really powerful tool called GitVersion. GitVersion comes with some expectations about the layout of your branches, so it may not be for everyone.<\/p>\n<p>Let\u2019s walk through using Package Management, Team Build, and GitVersion to manage version numbers. Because it\u2019s more complicated than previous walkthroughs, I\u2019ve chosen to be more verbose and detailed in my explanation.<\/p>\n<p>I\u2019ve decided that I\u2019ll follow the <a href=\"http:\/\/nvie.com\/posts\/a-successful-git-branching-model\/\">GitFlow<\/a> branching model for this project. There\u2019s a great tool called <a href=\"http:\/\/gitversion.readthedocs.org\/\">GitVersion<\/a> which lets me <a href=\"http:\/\/gitversion.readthedocs.org\/en\/latest\/git-branching-strategies\/gitflow\/\">translate<\/a> GitFlow branches and tags directly into semantic versions. For brevity, I\u2019m omitting a lot of experimentation and dead-ends I went down before finally settling on this flow. I encourage you to play around with GitFlow and GitVersion yourself.<\/p>\n<p>Start by creating a new class library called MyComponent in Visual Studio. Let\u00a0Visual Studio create a new Git repo for you.<\/p>\n<p>Right-click the project and choose Manage NuGet Packages. From NuGet.org, add GitVersionTask.<\/p>\n<p><a href=\"https:\/\/devblogs.microsoft.com\/wp-content\/uploads\/sites\/6\/2019\/05\/nugetpackagemanager.png\"><img decoding=\"async\" class=\"alignnone size-mediumlarge wp-image-16855\" src=\"https:\/\/devblogs.microsoft.com\/devops\/wp-content\/uploads\/sites\/6\/2016\/05\/nugetpackagemanager-500x136.png\" alt=\"NuGet Package Manager\" width=\"500\" height=\"136\" \/><\/a><\/p>\n<p>Next, edit AssemblyInfo.cs to comment out the explicit assembly versioning. GitVersionTask will take care of versioning the assembly.<\/p>\n<p><a href=\"https:\/\/devblogs.microsoft.com\/wp-content\/uploads\/sites\/6\/2019\/05\/assemblyinfo.png\"><img decoding=\"async\" class=\"alignnone size-mediumlarge wp-image-16865\" src=\"https:\/\/devblogs.microsoft.com\/devops\/wp-content\/uploads\/sites\/6\/2016\/05\/assemblyinfo-500x163.png\" alt=\"Edit AssemblyInfo.cs\" width=\"500\" height=\"163\" \/><\/a><\/p>\n<p>The last thing to do on the client side before building is to set up GitVersion\u2019s config file. While the defaults are great for most people, I personally prefer the <a href=\"http:\/\/gitversion.readthedocs.org\/en\/latest\/reference\/continuous-deployment\/\">continuous deployment<\/a> style of versioning. In the solution folder, add a file GitVersionConfig.yaml with the following contents:<\/p>\n<div><code> mode: ContinuousDeployment\nbranches: {}\n<\/code><\/div>\n<p>Commit the changes and build the solution. Look at the properties of the built DLL and notice all the GitVersion magic represented in the Product version field.<\/p>\n<p><a href=\"https:\/\/devblogs.microsoft.com\/wp-content\/uploads\/sites\/6\/2019\/05\/dllinfo.png\"><img decoding=\"async\" class=\"alignnone size-mediumlarge wp-image-16875\" src=\"https:\/\/devblogs.microsoft.com\/devops\/wp-content\/uploads\/sites\/6\/2016\/05\/dllinfo-250x350.png\" alt=\"DLL info showing automatic version number\" width=\"250\" height=\"350\" \/><\/a><\/p>\n<p>Next, push your code to a Git repo on Visual Studio Team Services. Everything we do from here on out will support creating and versioning a NuGet package using the tools VSTS provides plus the <a href=\"https:\/\/marketplace.visualstudio.com\/items?itemName=gittools.gitversion\">GitVersion extension<\/a>. Go ahead and install GitVersion from the Marketplace into your VSTS account. This will introduce a new build step that we\u2019ll need later. Also, if you haven\u2019t installed <a href=\"https:\/\/marketplace.visualstudio.com\/items?itemName=ms.feed\">Package Management<\/a> in your account, do that as well.<\/p>\n<p>In the Code hub, click the \u201cbuild: setup now\u201d badge.<\/p>\n<p><a href=\"https:\/\/devblogs.microsoft.com\/wp-content\/uploads\/sites\/6\/2019\/05\/setupnow.png\"><img decoding=\"async\" class=\"alignnone size-full wp-image-16885\" src=\"https:\/\/devblogs.microsoft.com\/devops\/wp-content\/uploads\/sites\/6\/2016\/05\/setupnow.png\" alt=\"Build: Setup Now\" width=\"496\" height=\"220\" \/><\/a><\/p>\n<p>Leave the defaults to create a new Visual Studio build. Then, make the following changes to your newly-created build:<\/p>\n<ul>\n<li>Add four new steps: GitVersion Task (available in the Build category), PowerShell (Utility category), NuGet Packager, and NuGet Publisher (latter two available in the Package category).<\/li>\n<li>Move GitVersion Task up so that it\u2019s right after NuGet Installer. Check the box which says \u201cUpdate AssemblyInfo files\u201d.<\/li>\n<li>Move PowerShell up so that it\u2019s right after GitVersion. Change its type to \u201cInline Script\u201d. In the Inline Script box, add the following code:\n<code>\n$UtcDateTime = (Get-Date).ToUniversalTime()\n$FormattedDateTime = (Get-Date -Date $UtcDateTime -Format \"yyyyMMdd-HHmmss\")\n$CI_Version = \"$env:GITVERSION_MAJORMINORPATCH-ci-$FormattedDateTime\"\nWrite-Host (\"##vso[task.setvariable variable=CI_Version;]$CI_Version\")<\/code>We\u2019ll cover what this script does a bit later.<a href=\"https:\/\/devblogs.microsoft.com\/wp-content\/uploads\/sites\/6\/2019\/05\/posh.png\"><img decoding=\"async\" class=\"alignnone size-mediumlarge wp-image-16785\" src=\"https:\/\/devblogs.microsoft.com\/devops\/wp-content\/uploads\/sites\/6\/2016\/05\/posh-500x188.png\" alt=\"PowerShell step\" width=\"500\" height=\"188\" \/><\/a><\/li>\n<li>Move NuGet Packager up so that it\u2019s right after Visual Studio Test. For Path to CSProj or NuSpec, make sure it\u2019s targeting <code>***.csproj<\/code> (that\u2019s the current default, but if you\u2019re re-using an existing build definition, you might have something different there). In the Versioning section, change Automatic Package Versioning to \u201cUse an environment variable\u201d. For Environment Variable, put \u201cCI_Version\u201d.<a href=\"https:\/\/devblogs.microsoft.com\/wp-content\/uploads\/sites\/6\/2019\/05\/pack.png\"><img decoding=\"async\" class=\"alignnone size-full wp-image-16795\" src=\"https:\/\/devblogs.microsoft.com\/devops\/wp-content\/uploads\/sites\/6\/2016\/05\/pack.png\" alt=\"NuGet Packager step\" width=\"456\" height=\"246\" \/><\/a><\/li>\n<li>Finally, leave the NuGet Publisher step at the end. Change its Feed Type to \u201cInternal NuGet Feed\u201d and set the URL to the URL of a feed in your account.<a href=\"https:\/\/devblogs.microsoft.com\/wp-content\/uploads\/sites\/6\/2019\/05\/publish.png\"><img decoding=\"async\" class=\"alignnone size-mediumlarge wp-image-16805\" src=\"https:\/\/devblogs.microsoft.com\/devops\/wp-content\/uploads\/sites\/6\/2016\/05\/publish-500x90.png\" alt=\"NuGet Publisher step\" width=\"500\" height=\"90\" \/><\/a><\/li>\n<\/ul>\n<p>Save this build definition. The steps should appear in this order:<a href=\"https:\/\/devblogs.microsoft.com\/wp-content\/uploads\/sites\/6\/2019\/05\/steps1.png\"><img decoding=\"async\" class=\"alignnone size-mediumlarge wp-image-16806\" src=\"https:\/\/devblogs.microsoft.com\/devops\/wp-content\/uploads\/sites\/6\/2016\/05\/steps1-289x350.png\" alt=\"Build steps\" width=\"289\" height=\"350\" \/><\/a><\/p>\n<p>Let\u2019s queue a build to see what you\u2019ve set up, then we\u2019ll walk through it step by step. The first thing you\u2019ll likely notice is that your build numbering has changed: GitVersion has automatically called it something like \u201cBuild 0.1.0-ci.2\u201d. That means you\u2019re building a pre-release build (-ci.2) of version 0.1.0. So far so good.<\/p>\n<p>Next, swing over to the Package hub and find the feed where you published your package. You should see the very first version of your shiny new NuGet package, properly semantically versioned as a prerelease of 0.1.0. If you were to queue another build right now, you\u2019d get a new prerelease package of the same version of the code.<a href=\"https:\/\/devblogs.microsoft.com\/wp-content\/uploads\/sites\/6\/2019\/05\/packagepane.png\"><img decoding=\"async\" class=\"alignnone size-mediumlarge wp-image-16816\" src=\"https:\/\/devblogs.microsoft.com\/devops\/wp-content\/uploads\/sites\/6\/2016\/05\/packagepane-500x322.png\" alt=\"Package pane showing our new package\" width=\"500\" height=\"322\" \/><\/a><\/p>\n<p>So what happened here? The magic is in three build tasks: GitVersion (which selected our semantic version), the PowerShell script (which added a timestamp to make the package version unique), and the NuGet Packager task (which reads the computed version number from an environment variable). Even if you don\u2019t speak PowerShell, you can probably understand the first 3 lines of code. We get the UTC date and time, format it into a string, then append it to the environment variable that GitVersion had already set (<code>$env:GITVERSION_MAJORMINORPATCH<\/code>). The last line is the Team Build way to add\/alter an environment variable for later build steps to read.<\/p>\n<p>Now that we\u2019ve exercised the whole workflow, let\u2019s see what happens when we bump the version. Say we\u2019ve done some development on our component and are preparing for a 1.0 release. Under GitFlow, we\u2019re likely to do some final cleanup in a release branch, so let\u2019s go back to Git and do that. In your solution, create a new branch called \u201crelease-1.0\u201d.<a href=\"https:\/\/devblogs.microsoft.com\/wp-content\/uploads\/sites\/6\/2019\/05\/branch.png\"><img decoding=\"async\" class=\"alignnone size-mediumlarge wp-image-16825\" src=\"https:\/\/devblogs.microsoft.com\/devops\/wp-content\/uploads\/sites\/6\/2016\/05\/branch-348x350.png\" alt=\"Branch creation\" width=\"348\" height=\"350\" \/><\/a><\/p>\n<p>Make and check in a small code change on the branch. Publish the branch to VSTS (<code>git push --set-upstream origin release-1.0<\/code>\u00a0if you&#8217;re command-line oriented) and queue a build of your release-1.0 branch.<a href=\"https:\/\/devblogs.microsoft.com\/devops\/wp-content\/uploads\/sites\/6\/2016\/05\/queuebuild-351x350.png\"><img decoding=\"async\" class=\"alignnone size-mediumlarge wp-image-16835\" src=\"https:\/\/devblogs.microsoft.com\/devops\/wp-content\/uploads\/sites\/6\/2016\/05\/queuebuild-351x350.png\" alt=\"Queue another build\" width=\"351\" height=\"350\" \/><\/a><\/p>\n<p>Once the build finishes, check your feed again \u2013 you\u2019ll find a CI package of a 1.0.0 build of your component! GitVersion automatically determined that this component should be versioned 1.0.0 because of the branch name.<a href=\"https:\/\/devblogs.microsoft.com\/wp-content\/uploads\/sites\/6\/2019\/05\/buildoutput.png\"><img decoding=\"async\" class=\"alignnone size-mediumlarge wp-image-16845\" src=\"https:\/\/devblogs.microsoft.com\/devops\/wp-content\/uploads\/sites\/6\/2016\/05\/buildoutput-500x154.png\" alt=\"Now the package is versioned 1.0\" width=\"500\" height=\"154\" \/><\/a><\/p>\n<h2>Releasing a package<\/h2>\n<p>Great, now you can produce prerelease packages of your components. So once you\u2019ve decided that a particular package is the one you want to release, how do you do it? Well, as <a href=\"http:\/\/www.xavierdecoster.com\/semantic-versioning-auto-incremented-nuget-package-versions\">Xavier explains<\/a>, you need to \u201cstrip off the pre-release tag\u201d.<\/p>\n<p>In the simple scenario that I walked through here, you have two easy options:<\/p>\n<ol>\n<li>Like part 1, don\u2019t worry about stripping the prerelease tag. Whatever \u201crelease\u201d means to you (perhaps uploading your package to NuGet.org), simply promote the CI package straight to release. Your consumers will know it\u2019s a \u201creleased\u201d version because of where they got it rather than metadata on the package itself.<\/li>\n<li>Repack (but don\u2019t rebuild!) your package with the new version number. A NuGet package is a zip file with a particular set of files in particular places. If you pull out the .nuspec from the root of the zip file, rewrite its &lt;version&gt; property, and re-insert it into the package, you\u2019ve changed the version of that package without rebuilding its contents.<\/li>\n<\/ol>\n<p>Note that option 2 only works if none of the packages output by your build depend on each other. If you produce only a single package, you\u2019re probably safe; if you produce multiple packages, you\u2019re probably <em>not<\/em> safe. As discussed in <a title=\"Versioning NuGet packages in a continuous delivery world: part 2\" href=\"https:\/\/blogs.msdn.microsoft.com\/visualstudioalm\/2016\/05\/18\/versioning-nuget-packages-cd-2\/\">part 2<\/a>, we&#8217;re considering\u00a0how to take a build as input, find all the packages produced by that build, find all their inter-dependencies, and then rewrite versions\/dependency specifiers with updated version numbers.<\/p>\n<h2>A recap<\/h2>\n<p>In <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\/\">part 1<\/a>, we covered semantic versioning and how to automatically create prerelease packages in a continuous delivery model. <a title=\"Versioning NuGet packages in a continuous delivery world: part 2\" href=\"https:\/\/blogs.msdn.microsoft.com\/visualstudioalm\/2016\/05\/18\/versioning-nuget-packages-cd-2\/\">Part 2<\/a> discussed some additional tools we want to develop to make these flows easier. Part 3, this post, covered version numbers managed with GitFlow and GitVersion. Feedback is always welcomed, here in the comments or via Send-a-Smile. We read every Send-a-Smile we get.<\/p>\n","protected":false},"excerpt":{"rendered":"<p>This is the third and final post in a series covering strategies for versioning a NuGet package. If you missed part 1 or part 2, you should read those first. Today\u2019s post walks through a specific workflow that Git users could adopt, using a really powerful tool called GitVersion. GitVersion comes with some expectations about [&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-16776","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 the third and final post in a series covering strategies for versioning a NuGet package. If you missed part 1 or part 2, you should read those first. Today\u2019s post walks through a specific workflow that Git users could adopt, using a really powerful tool called GitVersion. GitVersion comes with some expectations about [&hellip;]<\/p>\n","_links":{"self":[{"href":"https:\/\/devblogs.microsoft.com\/devops\/wp-json\/wp\/v2\/posts\/16776","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=16776"}],"version-history":[{"count":0,"href":"https:\/\/devblogs.microsoft.com\/devops\/wp-json\/wp\/v2\/posts\/16776\/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=16776"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/devblogs.microsoft.com\/devops\/wp-json\/wp\/v2\/categories?post=16776"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/devblogs.microsoft.com\/devops\/wp-json\/wp\/v2\/tags?post=16776"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}