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:

  1. Gather all of the packages that are going to be published together into a single location
    1. We have packages that are built out of different repositories that need to be brought together
    2. 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)
    3. Then, when we have QA sign-off on all of the packages, we will publish them all from that folder
  2. Upload all of the packages to nuget.org, but then immediately mark them as unlisted
  3. 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:

  1. In Windows Explorer, select all of the files
  2. Shift+Right-Click and choose "Copy as Path"
  3. Paste the paths into a text editor like Sublime Text
  4. 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:

  1. Upload the packages as unlisted
    Submit-Package -packagesConfig packages.config -apiKey <apikey>

  2. 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:

  1. 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.
  2. nuget.exe delete will unlist a package. You may have noticed this from my process above, but it's a little-known feature.
  3. 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.