NuGet 101: Creating and consuming a NuGet Package
Introduction
In this section we’re going to learn how to use pack and push commands in order to create a NuGet Package (.nupkg) over NuGet CLI and dotnet CLI. After that there will be a simple project to consume related packages.
If you are not familiar with the concept of Package Management (NuGet) checkout previous stories;
NuGet 101: Introduction to NuGet
NuGet 101: Building a private NuGetServer
Prerequisites
- Host URL: URL of the local or remote repository. (nuget.domain.com)
- API Key: In order to authenticate with repository, you need to give a API Key (token).
Creating packages
Creating a package starts with the compiled code (typically .NET assemblies) that you want to package and share with others, either through the public nuget.org gallery or a private gallery within your organization. The package can also include additional files such as a readme that is displayed when the package is installed, and can include transformations to certain project files.
Whatever the case, creating a package begins with deciding which assemblies and other files to package. You then create a manifest file, referred to as a .nuspec
file, to describe the package's contents along with its identifier, version number, copyright information, MSBuild props and targets, and much more.
When you’ve prepared all the necessary files in the appropriate folders and have created the appropriate .nuspec
file, you then use the nuget pack
command (or the MSBuild pack target) to put everything together into a .nupkg
file. You're then ready to deploy the package to whatever host makes it available to other developers.
Hands-on
In this case our server will be local. Lets check prerequisites;
Host URL: http://localhost:13781/
API Key: dbb9bea8–2ce9–47c5-b07f-2c58a0617686
Project Structure
Lets say we are developing a .net core application which provides low level logging and SMTP features.
Projects:
- Core.Logging — A class library which provides logging features
- Core.Mail — A class library which provides mail features
- Core.SMTP — A class library which provides a SMTP sender
- Consumer — A console application to test and implement dependencies.
Note that, we want to pack and publish only Core.Mail project which is depended with Core.Logging and Core.SMTP. As you can see Core.SMTP project is depended on a package called FluentEmail.Smtp
Using Visual Studio Pack command
In VisualStudio, we can pack a project easily with right clicking specific project and select pack option.
Lets check output screen.
VS tries to pack all depended projects. Not good right? So whats in the Core.Mail.1.0.0.nupkg file then? We can open it from NuGet Package Explorer or simply rename the file extension to zip or rar and extract it.
The version number of the package is related to Assembly version of the current project. Thats why out package version is 1.0.0 and package name “Core.Mail.1.0.0.nupkg”. Also there is a UI screen to modify those values in the Properties Tab → Package
Dependencies of the package are correct but it containts only single .dll file which is Core.Mail.dll. This is not the expected result. In order to run properly there must be three dll file. (Core.Mail.dll, Core.Logging.dll and Core.SMTP.dll)
This behavior is not recommended for .net core or .net standard projects at the moment. (April 2018)
What about using dotnet cli?
- cd to project directory
- dotnet build
- dotnet pack
- dotnet nuget push
dotnet build --configuration Release
dotnet pack --no-build --include-symbols
dotnet nuget push --source {host url} --api-key {apikey} {package file}
dotnet cli does not support project-to-project references. Checkout Microsoft’s explanation from here: https://docs.microsoft.com/en-us/dotnet/core/tools/dotnet-pack?tabs=netcore2x#description
“Project-to-project references aren’t packaged inside the project. Currently, you must have a package per project if you have project-to-project dependencies”
Also dotnet community reports this issue from GitHub, but still there is no improvement. https://github.com/NuGet/Home/issues/3891
Using nuget.exe
nuget.exe generates packages from two option:
To get the latest version of nuget.exe go to https://www.nuget.org/downloads and download the recommended latest version to your computer. And put it to your solution level directory.
Then we are going to open a command prompt or powershell to execute nuget commands. Open cmd and navigate to your nuget.exe folder.
- cd to nuget.exe
- nuget spec
- nuget pack {nuspec file}
- nuget push {package file} {apikey} -Source {host url}
Run “nuget spec” to generate .nuspec file.
nuget spec Core.Mail\Core.Mail.csproj
Here is the result:
This is cool but we need to modify some attributes to working properly. There are lots of details at https://docs.microsoft.com/en-us/nuget/reference/nuspec page which could be useful for customizing a .nuspec file.
Required metadata elements:
- id: The case-insensitive package identifier, which must be unique across nuget.org or whatever gallery the package resides in. IDs may not contain spaces or characters that are not valid for a URL, and generally follow .NET namespace rules. See Choosing a unique package identifier for guidance.
- version: The version of the package, following the major.minor.patch pattern. Version numbers may include a pre-release suffix as described in Package versioning.
- description: A long description of the package for UI display.
- authors: A comma-separated list of packages authors, matching the profile names on nuget.org. These are displayed in the NuGet Gallery on nuget.org and are used to cross-reference packages by the same authors.
- dependencies: A collection of zero or more
<dependency>
elements specifying the dependencies for the package. Each dependency has attributes of id, version, include (3.x+), and exclude (3.x+). See Dependencies below. - references: (1.5+) A collection of zero or more
<reference>
elements naming assemblies in the package'slib
folder that are added as project references. Each reference has a file attribute.<references>
can also contain a<group>
element with a targetFramework attribute, that then contains<reference>
elements. If omitted, all references inlib
are included. See Specifying explicit assembly references below. - contentFiles: (3.3+) A collection of
<files>
elements that identify content files to include in the consuming project. These files are specified with a set of attributes that describe how they should be used within the project system. See Specifying files to include in the package below.
pack with nuget.exe
Modify Core.Mail.csproj.nuspec elements which are shown below and pack again.
nuget pack Core.Mail\Core.Mail.csproj.nuspec
Lets check the new package which is now Core.Mail.1.0.1.nupkg
OK! Thats what expected :) Time to push!
Publishing packages
push with nuget.exe
Run push command to publish specific package to repository.
nuget push Core.Mail.1.0.1.nupkg dbb9bea8-2ce9-47c5-b07f-2c58a0617686 -Source http://localhost:13781/nuget
Consuming packages
To consume a nuget package we need to modify nuget.config. (Package Sources) We can edit via Visual Studio:
Tools → Options → NuGet Package Manager → Package Sources
Best practice is adding a nuget.config file to solution level of the project. With this approach we don’t need to modify each developer machine. If a developer gets latest version of source code, he or she will get the solution level nuget.config. Thus should modify package source automatically.
Add a nuget.config file to solution level as shown below:
Installing package
Lets check Package Sources and install specific package
Consumer → Right Click → Manage NuGet Packages.
Change the package source from opened window. There should be LocalNuget selection in the dropdown:
Click Install and run Consumer project
Restoring packages for C:\Users\02484454\source\repos\NuGet.Example.Netcore\Consumer\Consumer.csproj...
GET http://localhost:13781/nuget/FindPackagesById()?id='Core.Mail'
OK http://localhost:13781/nuget/FindPackagesById()?id='Core.Mail' 16ms
GET https://api.nuget.org/v3-flatcontainer/core.mail/index.json
GET http://nuget.packages.kocsistem.com.tr/nuget/FindPackagesById()?id='Core.Mail'
GET http://nuget.packages.iotp360.kocsistem.com.tr/nuget/FindPackagesById()?id='Core.Mail'
GET http://localhost:13781/nuget/Packages(Id='Core.Mail',Version='1.0.1')/Download
OK http://localhost:13781/nuget/Packages(Id='Core.Mail',Version='1.0.1')/Download 30ms
Installing Core.Mail 1.0.1.
NotFound https://api.nuget.org/v3-flatcontainer/core.mail/index.json 908ms
OK http://nuget.packages.kocsistem.com.tr/nuget/FindPackagesById()?id='Core.Mail' 1391ms
OK http://nuget.packages.iotp360.kocsistem.com.tr/nuget/FindPackagesById()?id='Core.Mail' 1405ms
Installing NuGet package Core.Mail 1.0.1.
Committing restore...
Writing lock file to disk. Path: C:\Users\02484454\source\repos\NuGet.Example.Netcore\Consumer\obj\project.assets.json
Restore completed in 339,52 ms for C:\Users\02484454\source\repos\NuGet.Example.Netcore\Consumer\Consumer.csproj.
Successfully installed 'Core.Mail 1.0.1' to Consumer
Successfully installed 'FluentEmail.Core 2.3.0' to Consumer
Successfully installed 'FluentEmail.Smtp 2.3.0' to Consumer
Executing nuget actions took 1,04 sec
Time Elapsed: 00:00:01.6496336
========== Finished ==========
Result:
There we can see Logger class log messages. It worked ;) But i don’t like Console.Write() method. Lets change it to Console.WriteLine() for better indenting and push an update with different version.
Updating package
Change Logger class Log method with Console.WriteLine
Update Core.Mail.csproj.nuspec file’s version number to 1.0.2. Run nuget pack again.
nuget pack Core.Mail\Core.Mail.csproj.nuspec
Push new package to repository:
nuget push Core.Mail.1.0.2.nupkg dbb9bea8-2ce9-47c5-b07f-2c58a0617686 -Source http://localhost:13781/nuget
And check package sources screen from Consumer application, you should see the latest version to update.
Update and run Consumer again
/cheers
You can download the source code from here:
References
https://docs.microsoft.com/en-us/nuget/tools/nuget-exe-cli-reference
https://docs.microsoft.com/en-us/nuget/reference/nuspec
https://docs.microsoft.com/en-us/dotnet/core/tools/csproj
https://www.nuget.org/downloads
https://docs.microsoft.com/en-us/dotnet/core/tools/dotnet-pack
https://github.com/NuGet/Home/issues/3891
https://docs.microsoft.com/en-us/nuget/consume-packages/configuring-nuget-behavior