Creating Nuget packages with Rake and Albacore

Creating a Nuget package is quite simple in theory and creating one manually can be done in a few minutes. But simple, repetitive tasks are easy to mess up and a tested build script will never include the wrong config file because it was thinking about coffee and cinnamon buns. Besides who wants to do this by hand every release, it’s a perfect task to be automated.

I helped to change FluentMigrator’s packaging script to create a Nuget package a while ago, so I’ll use that to illustrate how to create a Nuget package with Rake and Albacore.

All Nuget packages need a nuspec file and there are two ways to script this. The first way is to just create a nuspec file manually and then update the version with the build script. Nancy uses this approach, see here. They parse the nuspec files with an XML parser to find the right nodes, such as version, to update. Nancy has a lot of packages so they search after nuspec files and create a package when they find one. With FluentMigrator we only have two packages so I used Albacore and the Nuspec task to create the nuspec file:

desc "create the FluentMigrator nuspec file"
  nuspec :create_spec do |nuspec|
     version = "#{ENV['version']}"

     nuspec.id = "FluentMigrator"
     nuspec.version = version.length == 7 ? version : FLUENTMIGRATOR_VERSION
     nuspec.authors = "Josh Coffman"
     nuspec.owners = "Sean Chambers"
     nuspec.description = "FluentMigrator description which is really long."
     nuspec.title = "Fluent Migrator"
     nuspec.language = "en-US"
     nuspec.projectUrl = "https://github.com/schambers/fluentmigrator/wiki/"
     nuspec.working_directory = "packages/FluentMigrator"
     nuspec.output_file = "FluentMigrator.nuspec"
  end

The only bit of logic here is for setting the version. There is a default version defined as constant but this can be overwritten by passing in a command line parameter.

FluentMigrator has a working directory for packaging where I copied in the files to be included in the Nuget package. Nuget uses conventions for the directory names in the working directory. The three conventions are:

  1. libfor the assembly references to be installed into the target project,
  2. toolsfor the command line tools and powershell scripts and
  3. content for files that are copied into the root of the target project.

The main FluentMigrator package only uses lib and tools. Lib contains two subdirectories one for .NET 3.5 and one for 4.0. So the FluentMigrator dlls are copied into the appropriate lib subdirectories, the .NET 3.5 dll into lib\35 and the 4.0 dll into lib\40. Into the tools directory go all the different runners: the command line runner, the MSBuild runner and the Nant runner as well as all the dlls for the supported sql providers.

Then you just need to run the Nuget command line and call the pack command to create a nupkg file:

def nuget_pack(base_folder, nuspec_path)
    cmd = Exec.new
    output = 'nuget/'
    cmd.command = 'tools/NuGet.exe'
    cmd.parameters = "pack #{nuspec_path} -basepath #{base_folder} -outputdirectory #{output}"
    cmd.execute
  end

There is an Albacore task for this but I’ve gone with the Exec task instead here.  And this is called like this:

nuget_pack('packages/FluentMigrator/', 'packages/FluentMigrator/FluentMigrator.nuspec')

So to recap; the script creates a nuspec file, copies in the relevant files into the convention based working directory and then finally calls NuGet.exe with the pack command to create a Nuget package.

Pretty easy so far, it did get a bit more complicated building the second FluentMigrator package that is dependent on the first package. But I’ll leave that for another post.