One of the responsibilities I have come to own is our group’s NuGet package publishing. My group owns all of the ASP.NET MVC, Web API, Web Pages, Razor, and SignalR packages, among others; and there are quite a few of them. When it’s time to publish a set of packages, we go through a bit of a dance:
- Gather all of the packages that are going to be published together into a single location
- We have packages that are built out of different repositories that need to be brought together
- Each team that has packages to be published as part of a larger release will put their packages into a designated TFS folder (don’t judge, but TFS is perfect for this)
- Then, when we have QA sign-off on all of the packages, we will publish them all from that folder
- Upload all of the packages to nuget.org, but then immediately mark them as unlisted
- When all of the other release assets are ready, and we’re ready to “flip the switch,” list all of the packages
To make this process smooth, I have created a few PowerShell functions that I use each time. These are wrapped up in a PowerShell module so that I can easily reuse them. Here is the repository for the module:
https://github.com/jeffhandley/JeffHandley.NuGetPS
Within that repository, the NuGetPackagePublishing.psm1 file exports the following functions.
Submit-Package
NAME Submit-Package SYNOPSIS Submits a NuGet package (or set of packages) to the gallery, but as hidden (unlisted). SYNTAX Submit-Package [-packageId <Object>] [-packageVersion <Object>] [-packageFile <Object>] [-apiKey <Object>] [-galleryUrl <Object>] [<CommonParameters>] Submit-Package [-packagesConfig <Object>] [-apiKey <Object>] [-galleryUrl <Object>] [<CommonParameters>] DESCRIPTION Uploads the specified package (or all packages from a packages.config file) to the gallery and then immediately marks the package(s) as unlisted by running the nuget delete command. RELATED LINKS REMARKS To see the examples, type: "get-help Submit-Package -examples". For more information, type: "get-help Submit-Package -detailed". For technical information, type: "get-help Submit-Package -full".
Hide-NuGetPackage
NAME Hide-NuGetPackage SYNOPSIS Hides a package from the NuGet gallery SYNTAX Hide-NuGetPackage [-packageId <Object>] [-packageVersion <Object>] [-apiKey <Object>] [-galleryUrl <Object>] [<CommonParameters>] Hide-NuGetPackage [-packagesConfig <Object>] [-apiKey <Object>] [-galleryUrl <Object>] [<CommonParameters>] DESCRIPTION Marks the specified NuGet package as unlisted, hiding it from the gallery. RELATED LINKS REMARKS To see the examples, type: "get-help Hide-NuGetPackage -examples". For more information, type: "get-help Hide-NuGetPackage -detailed". For technical information, type: "get-help Hide-NuGetPackage -full".
Show-NuGetPackage
NAME Show-NuGetPackage SYNOPSIS Shows a package on the NuGet gallery, listing an already-published but unlisted package. SYNTAX Show-NuGetPackage [-packageId <Object>] [-packageVersion <Object>] [-apiKey <Object>] [-galleryUrl <Object>] [<CommonParameters>] Show-NuGetPackage [-packagesConfig <Object>] [-apiKey <Object>] [-galleryUrl <Object>] [<CommonParameters>] DESCRIPTION Marks the specified NuGet package as listed, showing it on the gallery. RELATED LINKS REMARKS To see the examples, type: "get-help Show-NuGetPackage -examples". For more information, type: "get-help Show-NuGetPackage -detailed". For technical information, type: "get-help Show-NuGetPackage -full".
Set-NuGetPackageVisibility
NAME Set-NuGetPackageVisibility SYNOPSIS Sets a package's visibility within the NuGet gallery SYNTAX Set-NuGetPackageVisibility [-action <Object>] [-packageId <Object>] [-packageVersion <Object>] [-apiKey <Object>] [-galleryUrl <Object>] [<CommonParameters>] Set-NuGetPackageVisibility [-action <Object>] [-packagesConfig <Object>] [-apiKey <Object>] [-galleryUrl <Object>] [<CommonParameters>] DESCRIPTION Hide (unlist) a package from the gallery or show (list) a package on the gallery. RELATED LINKS REMARKS To see the examples, type: "get-help Set-NuGetPackageVisibility -examples". For more information, type: "get-help Set-NuGetPackageVisibility -detailed". For technical information, type: "get-help Set-NuGetPackageVisibility -full".
Ship It!
With the above module imported, by using PowerShell's Import-Module command, I can then very easily take a directory of NuGet packages and publish them all at once. There's still a little bit that I don't have automated, and that is creating a packages.config file to use as a parameter to the functions. To do that, my process is presently to do the following:
- In Windows Explorer, select all of the files
- Shift+Right-Click and choose "Copy as Path"
- Paste the paths into a text editor like Sublime Text
- Use search/replace to change the file paths into a packages.config file format
Here is the regular expression that proves to be handy:
Find: ^"{root file path}\\(\w\w\w)\\([\D]*[a-zA-Z])\.([\w\.\-]*)\.nupkg"$
Replace: <package culture="$1" id="$2" version="$3" />
For the packages I published a few minutes ago, the packages.config file looks like this:
<packages> <package id="Microsoft.Owin.Host.SystemWeb" version="1.0.0-rc1" /> <package id="Microsoft.AspNet.FriendlyUrls" version="1.0.0-rc1" /> <package id="Microsoft.AspNet.FriendlyUrls.ViewSwitcher" version="1.0.0-rc1" /> <package id="Microsoft.AspNet.Mvc.Facebook" version="0.3.0-rc" /> <package id="Microsoft.AspNet.SignalR" version="1.0.0-rc1" /> <package id="Microsoft.AspNet.SignalR.Client" version="1.0.0-rc1" /> <package id="Microsoft.AspNet.SignalR.Core" version="1.0.0-rc1" /> <package id="Microsoft.AspNet.SignalR.Hosting.Utils" version="1.0.0-rc1" /> <package id="Microsoft.AspNet.SignalR.JS" version="1.0.0-rc1" /> <package id="Microsoft.AspNet.SignalR.Owin" version="1.0.0-rc1" /> <package id="Microsoft.AspNet.SignalR.Redis" version="1.0.0-rc1" /> <package id="Microsoft.AspNet.SignalR.ServiceBus" version="1.0.0-rc1" /> <package id="Microsoft.AspNet.SignalR.SystemWeb" version="1.0.0-rc1" /> <package id="Microsoft.AspNet.SignalR.Utils" version="1.0.0-rc1" /> <package id="Microsoft.AspNet.WebApi.HelpPage" version="0.3.0-rc" /> <package id="Microsoft.AspNet.WebApi.HelpPage.VB" version="0.3.0-rc" /> <package id="Microsoft.AspNet.WebApi.OData" version="0.3.0-rc" /> <package id="Microsoft.AspNet.WebApi.Tracing" version="0.3.0-rc" /> </packages>
Once I have that packages.config (which I also check into the TFS repository with the packages), I can then run the following two commands:
- Upload the packages as unlisted
Submit-Package -packagesConfig packages.config -apiKey <apikey> - List the packages that have already been uploaded
Show-NuGetPackage -packagesConfig packages.config -apiKey <apikey>
That's it. Now, with only 18 packages being published, this might seem like overkill. However, for the RTM release ASP.NET MVC 4, Web Pages 2, etc., we had 491 NuGet packages that were published! With that volume, having the process scripted out like this was crucial. And now I like to follow the same process regardless of the number of packages. If you too have lots of NuGet packages that you publish and you want to simplify your process, feel free to grab the NuGetPackagePublishing.psm1 file and use it within your process.
Other Tips
Here are some other tips to help with your NuGet Publishing:
- nuget.exe push accepts wildcards. That means you can run nuget.exe push *.nupkg to publish all nupkg files in a folder.
I don't use this approach because my process typically involves uploading as unlisted and then listing the packages later. In order to do that, I need to have the package Id and Version, which is why I have the packages.config file approach. - nuget.exe delete will unlist a package. You may have noticed this from my process above, but it's a little-known feature.
- nuget.org supports POST and DELETE operations for a package, allowing you to set the package's visibility through a simple web request. The URL for the web request is https://nuget.org/api/v2/Package/<packageId>/<packageVersion>?apiKey=<apiKey>. You can see this in use within the SetVisibility method in the NuGetPackageVisibility.psm1 file.