A little while ago I blogged about how I automate my package creation in Umbraco v4 using some custom MSBuild tasks I wrote. Since then, I’ve been lucky enough to join the Umbraco HQ and get some early exposure to Umbraco v5 (aka Jupiter). One of the changes in v5 is around how packages work, moving away from the proprietary package manifests used in v4, in favour of the standard NuSpec manifests used in NuGet packages. This means Umbraco packages are now just plain old NuGet packages.
With this change, I thought I’d have a go at updating my build scripts so that I can continue to automate the package creation process. After a quick search, I couldn’t find any specific MSBuild tasks for NuGet package creation, so I went and wrote some =)
So here is the updated process.
Prerequisites
As I’m using MSBuild, you’ll obviously need to have the .NET framework installed. In addition, you’ll need to download the NuGet.exe command line executable from Codeplex, and my custom MSBuild.NuGet.Tasks dll and targets file.
Project Structure
Now this is completely up to you, but for this example and for my own projects, this is the project structure I use.
- Lib - Referenced 3rd party libraries
- Src - Source code for your application
- MyProject - Project folder
- MyProject.proj - Project file
- MyProject - Project folder
- Tools - Build tools
- MSBuildNuGetTasks - The NuGet MSBuild tasks
- NuGet - The NuGet command line executable
- MyProject.sln – The project solution file
- Package.build.cmd - The command file for triggering the package build process
- Package.build.xml - The package MSBuild script
- Package.nuspec - The package manifest file
Package.nuspec
This is the manifest file for your package. The manifest is responsible for holding all the metadata about your package such as name, version and description aswell as listing the package files and, in the future, your package dependencies.
If you actually looked at the package manifest files in v4, then these will actually look pretty similar. The namespaces might have changed a little, but the function is exactly the same.
What follows is a demo manifest file.
<?xml version="1.0"?>
<package xmlns="http://schemas.microsoft.com/packaging/2010/07/nuspec.xsd">
<metadata>
<id>MyPackage</id>
<version>0.0.0</version>
<title>My Package</title>
<authors>Matt Brailsford</authors>
<owners>Matt Brailsford</owners>
<projectUrl>http://blog.mattbrailsford.com</projectUrl>
<requireLicenseAcceptance>false</requireLicenseAcceptance>
<description>My Package Description</description>
<summary>My Package Summary</summary>
<language />
</metadata>
<files />
</package>
Package.build.xml
This is the build script for the package, and is responsible for orchestrating the build process. This hasn’t actually changed that much from my original build script, with the only real difference being a different packaging task (the ManifestUpdate task has also been updated, but the signature is pretty much the same as before). The options on the pack task exactly match those of the pack command of the NuGet command line tool.
<?xml version="1.0" encoding="utf-8" ?>
<Project xmlns="http://schemas.microsoft.com/developer/msbuild/2003" DefaultTargets="Package">
<!--
****************************************
* IMPORTS
****************************************
-->
<PropertyGroup>
<MSBuildNuGetTasksPath>$(MSBuildProjectDirectory)\Tools\MSBuildNuGetTasks</MSBuildNuGetTasksPath>
</PropertyGroup>
<Import Project="$(MSBuildNuGetTasksPath)\MSBuild.NuGet.Tasks.Targets" />
<!--
****************************************
* PROPERTIES
****************************************
-->
<PropertyGroup>
<PackageVersion>1.0</PackageVersion>
</PropertyGroup>
<PropertyGroup>
<RootDir>$(MSBuildProjectDirectory)</RootDir>
<BuildDir>$(RootDir)\Build</BuildDir>
<PackageDir>$(RootDir)\Package</PackageDir>
<ProjectDir>$(RootDir)\Src\Namespace.MyProject</ProjectDir>
</PropertyGroup>
<!--
****************************************
* TARGETS
****************************************
-->
<!-- CLEAN -->
<Target Name="Clean">
<RemoveDir Directories="$(BuildDir)" Condition="Exists('$(BuildDir)')" />
<RemoveDir Directories="$(PackageDir)" Condition="Exists('$(PackageDir)')" />
<MakeDir Directories="$(BuildDir)" />
<MakeDir Directories="$(PackageDir)" />
</Target>
<!-- COMPILE -->
<Target Name="Compile" DependsOnTargets="Clean">
<MSBuild Projects="$(ProjectDir)\Namespace.MyProject.csproj" />
</Target>
<!-- PREPAIRE FILES -->
<Target Name="PrepairFiles" DependsOnTargets="Compile">
<ItemGroup>
<BinFiles Include="$(ProjectDir)\Bin\Namespace.MyProject.dll" />
<ViewFiles Include="$(ProjectDir)\Views\*.cshtml" />
<PackageFile Include="$(RootDir)\Package.nuspec" />
</ItemGroup>
<Copy SourceFiles="@(BinFiles)" DestinationFolder="$(BuildDir)\lib" />
<Copy SourceFiles="@(ViewFiles)" DestinationFolder="$(BuildDir)\content\views" />
<Copy SourceFiles="@(PackageFile)" DestinationFolder="$(BuildDir)" />
</Target>
<!-- MANIFEST -->
<Target Name="Manifest" DependsOnTargets="PrepairFiles">
<ItemGroup>
<ManifestFiles Include="$(BuildDir)\**\*" Exclude="$(BuildDir)\Package.nuspec" />
</ItemGroup>
<ManifestUpdate
ManifestFile="$(BuildDir)\Package.nuspec"
WorkingDirectory="$(BuildDir)"
Version="$(PackageVersion)"
Files="@(ManifestFiles)" />
</Target>
<!-- PACKAGE -->
<Target Name="Package" DependsOnTargets="Manifest">
<Pack NuGetExePath="$(RootDir)\Tools\NuGet\NuGet.exe"
ManifestFile="$(BuildDir)\Package.nuspec"
BasePath="$(BuildDir)"
OutputDirectory="$(PackageDir)"
Verbose="true" />
</Target>
</Project>
Package.build.cmd
This is a simple command script and is responsible for triggering the build process. Again, this hasn’t changed greatly from before, with the only change being and update to the msbuild executable used.
C:\WINDOWS\Microsoft.NET\Framework\v4.0.30319\msbuild.exe Package.build.xml
Conclusion
All in all, there haven’t been any huge changes to the script from before, with the majority of the changes being hidden away in the MSBuild tasks. Hopefully this will make it easier for people who used my script before to make the leap into v5. As an added bonus, since Umbraco packages in v5 are just plain old NuGet packages, these MSBuild tasks can actually be used for creating any other kind of NuGet packages aswell.
Happy packaging =)
Thank you for the great article. I was wondering how to automate this so I could easily integrate the readme.txt into the package manifest.
I will try this out with my next package!