This posts goal is to have a continuous integration server (Jenkins) pull a MSBuild project from a remote repository, build it and upload it to a private NuGet server. The NuGet feeds is hosted via ProGet with an authorization mechanism to restrict user access. Since I’m only using the free version of ProGet, this tutorial won’t cover setting up different feeds for different users.
Install & Configure ProGet
There’s a free edition of ProGet available that can host private NuGet (and more) feeds. Installation and initial configuration is nicely covered by their documentation, so I won’t go into much detail here. There was just one bit missing in the documentation that caused some troubles for me: When you’re using MS SQL Server, it’s possible that your default collation is different to the one ProGet will try to set during the database initialization, so make sure to create the ProGet database with SQL_Latin1_General_CP1_CI_AS collation and you’ll not run into issues, otherwise ProGet might fail to apply its database schema during installation.
To keep your feeds private, go into the settings page of ProGet -> Assign Privileges and remove the one that grants anonymous users View Only rights.
Set Up a Private Feed on the Server
In ProGets configuration section, go to Manage Feeds -> Create Feed -> NuGet and assign a name for that feed. The only thing you’ll want to set here is the NuGet API Key, since we’re going to push it via a Jenkins Job and we don’t want to have to include user credentials in a build job in plain text. Leave the symbols server option turned on.
Now if you go back to the feed overview, your feed will be listed along the default feed with its Url.
Admittedly, in the free edition, creating a feed isn’t really necessary at all since there’s no ways to customize access based on feeds. It’s all or nothing in the free edition.
Enable NuGet Package Upload with an ApiKey
Later, you’ll want to use the ApiKey to simplify uploading packages via the command line. For this, you have to create a new role with the task Feeds_AddPackage in the Manage Roles section:
Then, assign that role to the Anonymous user in the Manage Priviliges section:
Now, to upload a package, only the ApiKey is required (when one is configured for the feed).
Configure your Project for NuGet Packaging
Creating a NuGet feed is really easy. At first, you must find out where your NuGet.exe is located or simple download the latest executable from https://www.nuget.org. Then, in your project dir, run the nuget.exe spec command. A *.nuspec file is created with the same name as your *.csproj file with this default content:
<?xml version="1.0"?> <package > <metadata> <id>$id$</id> <version>$version$</version> <title>$title$</title> <authors>$author$</authors> <owners>$author$</owners> <licenseUrl>http://LICENSE_URL_HERE_OR_DELETE_THIS_LINE</licenseUrl> <projectUrl>http://PROJECT_URL_HERE_OR_DELETE_THIS_LINE</projectUrl> <iconUrl>http://ICON_URL_HERE_OR_DELETE_THIS_LINE</iconUrl> <requireLicenseAcceptance>false</requireLicenseAcceptance> <description>$description$</description> <releaseNotes>Summary of changes made in this release of the package.</releaseNotes> <copyright>Copyright 2016</copyright> <tags>Tag1 Tag2</tags> </metadata> </package>
All the fields enclosed in $ tags will be automatically populated when you create the package, but you can change them to hard coded values or have your CI system populate them. Just make sure that when you have the placeholder in the file, your Assembly must have the attribute. For example, if in your Project Settings in Visual Studio there is no Description for the assembly, then packaging will fail due to the $description$ tag missing content.
You can do a test run to create a package via nugget.exe pack <PathToYourCsprojFile> -Prop Configuration=Release -Symbols
This will generate a *.nupkg and a *.symbols.nupkg file. The symbols file does contain your *.pdb files.
Next, before you can deploy you need to set the NuGet ApiKey for your ProGet server via this command nuget setapikey <YourApiKey> -source <UrlOfYourFeed>
This will locally (for the current user, so execute the command also with the user that is configured for your Jenkins service) store the ApiKey for that specific feed for pushing packages.
Now pushing the package is really easy with nuget push <PathToYourPackage> -source <PathToYourFeed>
If you do want to include symbols, then upload the *.symbols.nupkg instead of the *.nupkg one. ProGet does not have different endpoints for symbol- and regular packages, it will only accept a single package per project and version and, if this is a symbol package, additionally offer the *.pdb files in its symbol feed while stripping them from the regular feed.
If everything went smooth, your Package is now available on your private NuGet server.
Accessing the Feeds
Since the feeds are now private and protected, it would be advisable to create a user with the View_Only role for accessing the feeds in ProGet.
Once that’s done, you need, on every machine and for every user account that will access the feed, run the following command:
nuget source add -Name <NameOfTheStoredCredentials> -Source <YourNuGetFeedUrl> -User <Username> -Pass <Password>
By default, this will store it in a config file in the current users AppData directory and will be automatically retrieved for this source. The package source will then also be made known for all package restores on your account through NuGet, so you don’t have to specify your custom NuGet feed in Visual Studio. In fact, Visual Studio will be aware of your package source after you’ve entered this command. To search for packages there, set this as your package source in the upper right corner of your NuGet package manager view in Visual Studio. Please note that you can overwrite global feed sources via a project specific NuGet.config file.
Try it out with nuget list -source <YourNuGetFeedUrl>. If it works, you've got time to fetch a coffee while it lists all the feeds (it also relays the official NuGet feed). You could also press Ctrl + C in the cmd window to abort=)
Setting the credentials is an easy way for integrating the private feed in your CI system as well as your IDE, since all you have to do is specify an Url and don’t have to wiggle with the credentials yourself.
Automate NuGet Package Deployment with Jenkins
In Jenkins, you want two build actions:
First, build the project in release mode with the MSBuild plugin (NuGet should do that automatically, but I had issue with NuGet not being able to find the correct MSBuild version)
Then in the second step, you need the following PowerShell script (if you haven’t already, you should install the Jenkins PowerShell plugin):
$workspacePath = $ENV:WORKSPACE $projectName = "Your.Namespace" # Name of the project. Omit ".csproj", this will be added automatically $pathToNuGet = "C:\BuildTools\NuGet\nuget.exe" $configuration = "Release" # The configuration you want to publish $nuGetFeedTarget = "<YourNuGetFeed>" # Find the project file $pathToCsproj = Get-ChildItem $workspacePath -Recurse -Filter ($projectName + ".csproj") # Create the NuGet package "Creating the NuGet package" $nuGetPackArguments = @(("pack"),` ("""" + $pathToCsproj.FullName + """"),` ("-Prop Configuration=" + $configuration),` ("-Symbols")) $nuGetPackArguments $nuGetPackProcess = Start-Process -FilePath $pathToNuGet -ArgumentList $nuGetPackArguments -Wait -PassThru -NoNewWindow if ($nuGetPackProcess.ExitCode -ne 0) { "Error during NuGet package creation, exiting" exit $nuGetPackProcess.ExitCode } else { "Finished NuGet package creation" } # Find the package $pathToPackage = Get-ChildItem $workspacePath -Recurse -Filter ($projectName + "*.symbols.nupkg") # Upload the package $nuGetPublishArguments = @(("push"),` ("-NonInteractive"),` ("""" + $pathToPackage.FullName + """"),` ("-source " + $nuGetFeedTarget)) $nuGetPublishProcess = Start-Process -FilePath $pathToNuGet -ArgumentList $nuGetPublishArguments -Wait -PassThru -NoNewWindow if ($nuGetPublishProcess.ExitCode -ne 0) { "Error during NuGet package publishing, exiting" exit $nuGetPublishProcess.ExitCode } "Finished package publishing"
Set the five variables at the top according to your project setup and then start a Jenkins build. When everything’s configured correctly, you should see something like this in the ProGet web view of your feed:
And in Visual Studio:
Now you’ve just drastically simplified using your own shared libraries!
Oh, and on a side note: You should take into consideration when you perform that Jenkins job. It might be worthwhile to leave it as manual so you don’t get a new NuGet package version with every single commit you do.