Introduction
Paket is an alternative to the default nuget package manager used in Visual Studio. Paket is completely compatible with nuget, but is much more feature rich and provides real-world benefits for library developers.
Background
Until recently, I had not heard of Paket. I had never even contemplated an alternative to nuget as it does (almost) everything I could want. The only thing I've had to supplement nuget with was OctoPack. And custom scripting to get assembly versions. And a bit of glue logic in PowerShell. Hmm. Maybe there is something better.
Using Paket
First, I suggest you head over the Paket website and do some reading. Paket is different than nuget. They are working on a Visual Studio plugin, but it's very, very unfinished right now. Instead, you'll be manually creating your Paket files from scratch (well, mostly). Paket does include a command to convert a nuget-based project to a Paket-based project, and we'll go over that later.
Installing Paket
You install paket from Nuget.org through the standard Visual Studio package management console:
Next, you want to install the Paket Power-Shell tools.
Now you are ready to convert your project to using Paket through Paket's ConvertFromNuGet command.
In the solution explorer you'll notice that packages.config is gone--replaced with paket.dependencies on the solution, and paket.references on the project.
The paket.dependencies file is used to hold the definitions of the packages being referenced. The paket.references file is used to hold a project-level dependency on a package defined in paket.dependencies.
You can manually modify this files, but once they are created you should simply let Paket manage them through using the Package Manager Console.
Now, let's add a reference to Newtonsoft.Json. Why not? Everyone does. :)
If we look at our paket.dependencies, we will see:
source <a href="https://dotnet.myget.org/F/dotnet-core/api/v3/index.jsonsource">https:
source</a> <a href="https://dotnet.myget.org/F/cli-deps/api/v3/index.json">https:
source <a href="https://api.nuget.org/v3/index.json">https:
framework >= net452
nuget Paket 5.84.0
nuget newtonsoft.json
Now we have newtonsoft.json referenced in our solution. Well done!
Go ahead and write some code in your project. Then it's time to package it and send it to your favorite nuget repository.
To package your project, you need another file: paket.template. This file goes in the project folder(s) you wish to publish in the solution.
An example paket.template for the GPS.SimpleDI project looks like this:
The paket.template file is very powerful. Think of it as a Nuspec file on crack. It is easier to read and edit and has more features. Paket's package builder is very full-featured. All of it is controlled through the paket.template file, both with inclusions and omissions. Properties included (such as authors) in the template are taken verbatim and placed in the nuget manifest. Items that are omitted are referenced from the AssemblyInfo data. This makes it easy to keep your template consistent with your AssemblyInfo file and not worry about mis-matches. Also, it means you only have to manage the version in one place (which nuget.exe does not do at all) by using the AssemblyVersion
attribute defined in the AssemblyInfo
.
Packaging your project is quite easy once you've defined your template. There are many, many options for packaging your project with paket's pack command. I suggest spending some quality time on that page and coming up with the best set of options you need for your projects. Most options are supported in the paket.template file to make life easier.
A couple of things I have done to make my life easier is to add two custom files to my solutions (not the projects). These are the ReleaseNotes.txt file and the deploy.ps1 file. The deploy.ps1 file does all the heavy lifting of compiling, packaging and pushing your project to nuget.org (or whatever repository you like). Deploy.ps1 will read the contents of ReleaseNotes.txt and include them with the paket pack command.
Unfortunately, as of this writing, the Paket PowerShell commandlets do not define a commandlet for the pack command. This is unfortunate as you'll wind up with some non-powershell stuff in your deploy.ps1 file.
For reference, here is my deploy.ps1 for the afore-mentioned GPS.SimpleDI project.
function Deploy-Package
{
param(
[Parameter(Mandatory=$true)][string]$SolutionDir,
[Parameter(Mandatory=$true)][string]$BuildDir,
[Parameter(Mandatory=$true)][string]$Namespace,
[Parameter(Mandatory=$true)][string]$Assembly
)
$proj = $SolutionDir + '\' + $Namespace + '\' + $Namespace + '.csproj'
$assm = $BuildDir + '\' + $Assembly
& "F:\Program Files (x86)\Microsoft Visual Studio\2017\Community\MSBuild\15.0\Bin\amd64\msbuild.exe" $proj /p:Configuration=Release
if($LASTEXITCODE -eq 0) {
$AssemblyVersion =
[Diagnostics.FileVersionInfo]::GetVersionInfo($assm).FileVersion
$packageName = $Namespace +'.' +$AssemblyVersion+'.nupkg'
$package = $BuildDir +'\' +$packageName
$destination = $BuildDir + '\' + $packageName + "\" + $packageName
$sourceDestination = $BuildDir + '\' + $packageName + "\" + $Namespace +'.' +$AssemblyVersion+'.symbols.nupkg'
Set-Location $SolutionDir
$releaseNotes = [IO.File]::ReadAllText($SolutionDir + "\ReleaseNotes.txt")
.paket/paket.exe pack --release-notes $releaseNotes --symbols -v $package
Paket-Push -File $destination -ApiKey $env:NugetAPIKey -url https://www.nuget.org -endpoint /api/v2/package -Verbose
}
}
Clear-Host
#Deploy-Package -SolutionDir %1 -BuildDir %2 -Namespace %3 -Assembly %4
Deploy-Package -SolutionDir 'F:\GPS\SimpleDI\SimpleDI' -BuildDir 'F:\GPS\SimpleDI\SimpleDI\GPS.SimpleDI\bin\Release' -Namespace "GPS.SimpleDI" -Assembly "GPS.SimpleDI.dll"
Some notes:
- The deploy.ps1 file presents a function for building, packaging and pushing the package. This function can be made into a PowerShell module and re-used.
- The location of msbuild.exe should be parameterized.
- The repository APIKEY you use to push your packages needs to be out of view from source control. I use an environment various called NugetAPIKey to hold the key.
- I've used PowerShell commandlets where-ever possible.
Points of Interest
I found out about Paket from a tweet describing how big-bad-Microsoft was trying to squish this up-and-coming package manager. This was brought about by a rather nefarious act by a Paket developer who attempted to sneak in some "official" recognition of Paket into the Nuget gallery website via a Pull Request on GitHub. The Nuget devs initially accepted the PR, thinking it was just some tweaks to a page. But it was soon reversed... and the PR closed... with no real explanation. So now we have two slightly suspicious actions going on. The Paket dude really had no business trying to subvert the normal PR process to benefit his project, and the Nuget guys should have been more forthcoming with reasonings for closing the PR. This drama is still unfolding as this is written. Good luck sorting it out after you take a side (which I don't suggest doing).
Conclusion
Sometimes we walk blindly through life not knowing about greener pastures because of the green pasture we're already in. Nuget is really good. If it wasn't for nuget, Microsoft would be losing ground fast. Nuget is a game changer, and has been since day one.
Paket simply makes the nuget ecosystem better. It doesn't change a single thing about the ecosystem, it simply provides a better mousetrap for utilizing it.