Becoming a NuGet Author
In the November issue, Phil Haack introduced NuGet, a new package management ecosystem for developers (msdn.microsoft.com/magazine/hh547106). NuGet is a project of the Outercurve Foundation, whose goal is to become a first-class package management system for the Microsoft .NET Framework. The project team consists mostly of Microsoft developers working in collaboration with the developer community. The introduction of NuGet to our development ecosystem gives .NET developers a way to consume, author and publish packages.
At first glance, NuGet might appear to be a tool just for the open source community, but that’s only part of a larger story. NuGet was specifically designed not only to help distribute packages within the open source community, but also to deliver internal packages behind the firewall in an enterprise. This means you can use NuGet in a multifaceted way to install and update packages both from Microsoft and from the open source community at large, as well as your own internal servers.
In this article, I’ll explore what it takes to become a NuGet package author. Incorporating NuGet into your development lifecycle isn’t complicated, and it will yield substantial awesomeness. Once you’ve done this, your package consumption, creation and distribution problems will fade to a distant memory. Next month, I’ll delve into what it takes to host your own packages.
Before starting on the journey to become a package author, let’s briefly recap the larger ecosystem. NuGet, from the point of view of the package distributor, consists of a few essential components, mainly a command-line utility called NuGet.exe (nuget.codeplex.com/releases/view/58939) and a server to host the packages, such as the official NuGet Gallery, nuget.org. As last month’s article demonstrated, you can interact with NuGet in three ways: using NuGet.exe, using NuGet inside of Visual Studio (the Package Manager Console window (View | Other Windows), and using the NuGet Package Explorer (npe.codeplex.com). Those utilities interact with one or more NuGet repositories. On the flip side, nuget.org is a public gallery you can use to store, host and publish NuGet packages. Nuget.org is built using another open source project known as NuGetGallery, which you can find at github.com/nuget/nugetgallery. I will talk more about how to host your own NuGet Gallery in the next issue.
As a package author, you can publish many different packages to a NuGet repository, and each package can have multiple versions. Nuget.org gives its customers a chance to read details about a package, install the package, contact the package owner and, in what should be rare cases, report abuse.
As the package author, you can control items such as version numbers, dependencies and how your package will be installed.
Getting Set Up
To publish a package, assuming you’ll be using nuget.orgas the repository, you need to sign up for an account on the NuGet Gallery. Becoming an author is easy: Just browse to the Contribute to NuGet Gallery section at http://docs.nuget.org/docs/contribute/setting-up-a-local-gallery and select Get Started. Then, click on Register now to get to the registration form and fill out the necessary information to create a new account. Nuget.org will send an e-mail with a URL where you can confirm your e-mail address and account.
After confirming your account, you can log on to the site and get your access key, a unique token that identifies you to the nuget.org repository and enables you to automate various package management tasks, such as pushing an update to your package.
For this article, I’ll demonstrate both the NuGet.exe command line and the NuGet Package Explorer. One point to note: Once you download the command-line version, you’ll probably want to update your system’s path Environment variable to include its location. This makes it easy to use NuGet from anywhere on your system.
Anatomy of a Package
As noted in the last article, a NuGet package is an Open Packaging Convention (OPC) container file with the .nupkg file extension. The format for the package relies heavily on conventions, with a manifest file at the root known as the nuspec file. A sample directory structure initially might look like this:
As you can see, there are three folders at the root:
- lib contains all of the assemblies to be referenced
- content contains files and directories that are copied to the root of your target project
- tools is a place for custom Windows PowerShell scripts that might be run on installation of your package or every time the target project is loaded in Visual Studio
Of these, the lib folder is the most complex. It contains subfolders that correspond to framework dependencies, as shown here:
Root | package.manifest \---lib | MyFirstAssembly.dll \---net11 | MySecondAssembly.dll \---net20 | MySecondAssembly.dll \---sl | MySecondAssembly.dll \---netmf | MySecondAssembly.dll +---content +---tools
If your assembly works in all versions of the .NET Framework (rare indeed!), you need to include it only at the root of your lib folder, as with MyFirstAssembly.dll.
Given how rare that scenario is, the NuGet team strongly discourages the practice. It’s better to make your assembly dependent on a specific framework version. To do so, add a folder for that framework version and include the correct version of the assembly in that folder. As you can see in the example folder, MySecondAssembly.dll has a different version for the .NET Framework 1.1, 2.0, Silverlight and the .NET MicroFramework. This ensures NuGet installs your package properly for the target frameworks.
When your customer installs your package, NuGet will install the correct assemblies based on the target framework for the project. In the previous example, let’s assume your customer is trying to install your package into a project that targets version 4 of the .NET Framework. Because it’s not listed as a framework in the sample lib folder, NuGet will take the closest framework version available and use that. In this example, it would take the assemblies found in the net20 folder and use those.
The content folder is a clone of the target project’s root folder. Anything found in that folder will be copied as is into the target project. For example, if you wanted to copy some images into the target’s /images folder, you’d need to include those images in the /content/images folder.
The tools folder includes any Windows PowerShell scripts that NuGet will invoke during package installation or when the project is opened, or that are used later by the customer. Once the folder is copied to the target project, it’s added to the `$env:Path (PATH) environment variable within the Visual Studio Package Manager Console.
NuGet has a built-in facility to automate the populating of your package—the files node. The files node is a way to explicitly list the files you want copied into your package structure when creating the .nupkg file. This helps automate the overall packing process. The file element is straightforward, defining src, target and exclude attributes. As you might guess, src defines the file you want copied; target defines the destination you want it copied to; and exclude defines what you don’t want copied:
<files> <file src="bin\Debug\*.dll" target="lib" /> <file src="bin\Debug\*.pdb" target="lib" /> <file src="tools\**\*.*" exclude="*.log" /> </files>
If you have another process in place to create your package, you can ignore the files node in the .nuspec file.
The .nuspec File
The nuspec file is your package manifest. It’s a simple XML file that defines your overall package and includes things like name, version number, package references and so on. To create your new manifest, NuGet.exe has a command called spec that you can use as a starting point:
The spec command creates a new file called package.nuspec, a valid file that contains sample data. Figure 1 shows an example file created by spec.
Figure 1 Sample .nuspec File
<?xml version="1.0"?> <package > <metadata> <id>Package</id> <version>1.0</version> <authors>csell5</authors> <owners>csell5</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>Package description</description> <copyright>Copyright 2011</copyright> <tags>Tag1 Tag2</tags> <dependencies> <dependency id="SampleDependency" version="1.0" /> </dependencies> </metadata> </package>
After this file is created, you replace the sample values. For most of the values, you only do this once, though some will change more often. For example, the id of your package shouldn’t change after it’s published, but the version number will change with every release.
You can also run nuget.exe spec against a Visual Studio project file (such as .csproj or .vbproj). In this case, the default values are already populated based on metadata in the project file. Here are some of the simpler elements of the .nuspec file:
- id unique identifier for the package
- title human-friendly title of the package
- description long description of the package
- summary short description of the package
- licenseURl a link to the license
- copyrights copyright details for the package
There are currently 28 different top-level elements. While the .nuspec file is pretty self-explanatory, you can find all of the details at bit.ly/lgQ4J4. Now let’s take a look at a few of the more complex .nuspec elements.
Dependencies and References
We all know that managing dependencies can be difficult, especially when the chain of dependencies becomes long and intermingled. Let’s say you’ve built PackageA. Your package happens to use PackageB, which can also be found in NuGet. Rather than including PackageB within your package, you just need to create a “dependency” on it. When someone starts to install your package, NuGet first inspects your nuspec file for its dependencies. It then looks at each dependency package and examines its dependencies and so on until it builds up a graph of every package it needs to download in order to fulfill all of the dependencies. It then downloads the entire graph of packages and installs them. This feature of NuGet drastically simplifies package creation and installation.
Let’s look at some package dependencies defined as shown in the dependency node here:
<package> <metadata> <dependencies> <dependency id="SampleDependency" version="1.0" /> <dependency id="AnotherSampleDependency" version="[1.2,2.5)" /> </dependencies> </metadata> </package>
You can list as many dependencies as you need. In each case, the id attribute indicates the package you have a dependency on, and the version attribute represents the version range you require. My example here shows a dependency on the SampleDependency project equal to version 1.0 or higher.
The NuGet version range specification gives you the ability to set the particular range of versions you allow. This looks something like version="[1.2, 2.5)", where the square bracket defines inclusion and the parenthesis defines exclusion. This example indicates that any package equal to or greater than 1.2 and less than 2.5 is allowed. NuGet will take the latest version found in that range. For detailed information about the version range specification, please visit bit.ly/qVXWxs.
In some cases, the person installing your package might need to program against types in a .NET Framework assembly. To add the appropriate reference, add the frameworkAssemblies node to the .nuspec file, detailing the list of required framework assemblies, like so:
<package> <metadata> <frameworkAssemblies> <frameworkAssembly assemblyName="System.Something" targetFramework="net40" /> <frameworkAssembly assemblyName="System.SomethingElse" /> </frameworkAssemblies> </metadata> </package>
Many projects require more than just an assembly reference to work correctly. They may need a .config file change, or even some source code modified—and NuGet supports both of these scenarios natively. I’ll focus on .config file transformations here. For more information about transformations, please see bit.ly/jqzry2.
During installation of your NuGet package, NuGet will run the transformation to add your new .config values. To make this happen, you need to add a transformation file to the content folder of your package. This is a valid XML file with the extension “transformation,” whose filename matches the file to which you want to apply the transform. For example, to apply a transformation to a web.config file, you’d include a file named web.config.transformation.
Your transformation file should include only the sections of the configuration file you want added to target file. Let’s say you want to add a new module to your customer’s system.webServer section. Simply add that section in its entirety to the transformation file, like this:
<configuration> <system.webServer> <modules> <add name="NewModule" type="My.NewModule" /> </modules> <system.webServer> </configuration>
NuGet will not replace existing sections with the sections you add but rather merge them together. So if your target already had a modules section with its own module listed, the result of the two files being merged after installation would look something like this:
<configuration> <system.webServer> <modules> <add name="ExistingModule" type="Their.ExistingModule" /> <add name="NewModule" type="My.NewModule" /> </modules> <system.webServer> </configuration>
As you can see, your module is added to the end of the existing stack of modules. If a user wanted to remove your package, just your changes would be removed (assuming you didn’t make any changes to those sections), leaving the rest as it was in the first place.
Versioning is at the core of anything we build. The NuGet package version refers to the package and not necessarily the assemblies contained inside (though it’s customary to keep these in sync). You define the package version number in the .nuspec file, with a format of N.N.N.N, like so.
There are a few properties in the .nuspec file where you can use a replacement token rather than just a static string. The version element is one of these. So instead of defining a static string like 126.96.36.199, you can insert a token, [$version$], which will later be replaced by NuGet.exe. With that token present, the version specified in the assembly’s AssemblyVersionAttribute will be carried through to the .nuspec file:
This is a great option if you want to keep the packages and version in sync, though there are plenty of reasons why you may not choose to do this.
Packing the Package
As mentioned earlier, a NuGet package is an OPC file with a .nupkg file extension. To create a package from the command line you simply call NuGet.exe with the pack command, passing it your .nuspec file:
As with spec, you can run pack against your project file as well. NuGet will build a complete NuGet package (.nupkg file) based solely on the metadata found within your .csproj or .vbproj file. If you’ve already created a .nuspec file, pack would use that .nuspec file:
You’ve just created your first NuGet package, congratulations!
Visual Studio has a great feature that lets developers step through source code on demand. NuGet supports this by giving package authors the ability to create and publish a symbol package. To create a symbol package, use the –symbols option when using pack:
Pack will generate two .nupkg packages, MyProject.nupkg and MyProject.Symbols.nupkg. The .symbols.nupkg can later be pushed to SymbolSource.org using the command NuGet.exe push. For more information about creating symbol packages with NuGet, see bit.ly/jqzry2.
Publishing to NuGet.org
With your package created, it’s now time to push it. Push is the NuGet command to publish your package to the server and it’s used like most modern source control systems. Unlike the commands I’ve previously mentioned, push takes a number of arguments:
- Package path The path to your package
- API key Your unique access token
Optional, could be set using the NuGet.exe setApiKey command
- -source (src) The server where the package is going.
Example: -source http://packages.nuget.org/v1/
Optional, unless you’re pushing to an alternative place
- -CreateOnly (co) Pushes package to gallery but doesn’t publish
Optional, default = false
The following sample command pushes the MyPackage package to NuGet:
You could also use NuGet Package Explorer, as shown in Figure 2.
Figure 2 Publishing with the NuGet Package Explorer
If you built a symbol package, NuGet would automatically find it and push it to both repositories, nuget.org and symbolsource.org. If the target machine is set up to use symbolsource.org as a symbol source, the developer can now step into your package source files on demand from within Visual Studio.
You’re published! If this is your second version of your package, that version will now become the default version. As explained in last month’s article, when someone looks for package updates, your package will now be listed as one with an update.
Chances are your development team has some kind of build and deploy process in place. If you’re like me, you’re now starting to think about ways to integrate NuGet into that process. Clearly you could wrap all the commands I’ve shown here into your build process, but if you’re already using Team Foundation Server (TFS), there’s an easier way.
TFS NuGetter (nugetter.codeplex.com) is an open source project that extends the build process for TFS 2010, performing all of the necessary versioning, packaging and deployment functions in a customizable and repeatable way, with NuGet at its core. Regardless of your package’s destination, TFS NuGetter will save you a great deal of time.
NuGet isn’t a new concept to our industry, but for the .NET developer it may well seem groundbreaking. NuGet provides a much-needed package management facility, useful to everyone from garage developer to large enterprises. It gives you not only a place to publish your packages, but also a place for customers to discover your work. Publish your packages to NuGet and get found!
You can find all of the links used in this article and more at on.csell.net/BeANuGetAuthor.
Thanks to the following technical experts for reviewing this article: David Ebbo, Phil Haack, Mark Nichols and Brandon Satrom