October 23rd, 2012

Creating custom package rules for your build

When you build your NuGet packages using nuget.exe, you may have noticed that sometimes you get a warning about the created package. Here’s one example:

A package warning

This warning is generated by one of NuGet’s package analysis rules. Think of these as the NuGet equivalent of an FxCop rule. When nuget.exe creates a package, it automatically executes the package analysis step which in turn invokes all the registered rules. These rules will, one by one, examine the built package and emit the warnings as you see. You can disable this analysis step by supplying the -NoPackageAnalysis parameter to the ‘pack’ command, although we don’t recommend it.

By default, nuget.exe includes a set of built-in rules. These are:

  • InitScriptNotUnderToolsRule: detect whether there is an init.ps1 file that is not placed directly under the tools directory.
  • InvalidFrameworkFolderRule: detect whether there is an unrecognized framework folder name.
  • MisplacedAssemblyRule: detect whether any assembly files are placed outside of the lib folder.
  • MisplacedScriptFileRule: detect whether any Powershell .ps1 files are placed outside of the tools folder.
  • MisplacedTransformFileRule: detect whether a transform file (.pp or .transform) is placed outside of the content folder.
  • MissingSummaryRule: detect whether the Summary attribute is missing when the Description attribute is longer than 300 characters.
  • WinRTNameIsObsoleteRule: detect the presence of a ‘WinRT’ framework folder. (Starting with NuGet 2.1, the ‘WinRT’ moniker is obsolete and we recommend people use ‘NetCore45’ or ‘Windows8’ instead.)

What is not very well-known is that the rule set is extensible, meaning that you can write your own custom rules to cater to your needs. For example, the Microsoft ASP.NET team has written a rule to ensure that all packages that we ship have an Id that start with Microsoft.AspNet.*. In this post, I will show you how easy it is to write a custom package analysis rule.

Develop rule assembly

  1. First, create a class library project that targets .NET 4.0. You can add multiple rules in one assembly.
  2. Add an assembly reference to nuget.exe. You need it for the package rule types. In the Properties window, set both Copy Local and Specific Version to False, because we don’t want to deploy nuget.exe together with our extension assembly.
  3. Add another assembly reference to System.ComponentModel.Composition. You need this because the analysis mechanism is based on MEF.
  4. Now add a class to your project for each rule you want to create. A rule class must derive from the NuGet.IPackageRule interface, and must export it using the MEF Export attribute.
 [Export(typeof(IPackageRule))] 
  public class PackageIdRule : IPackageRule 
 {

}

Here’s the definition of the IPackageRule interface:

 public interface IPackageRule 
{ 
  IEnumerable<PackageIssue> Validate(IPackage package); 
} 

As you can see, it has only one simple Validate method, which receives an instance of the package that has just been built. The method will examine the package and return all the issues that it finds. An issue is represented by an instance of the PackageIssue class.

 public class PackageIssue 
{ 

 public PackageIssue(string title, string description, string solution);
 public PackageIssue(string title, string description, string solution, PackageIssueLevel level);
 public string Description { get; }
 public PackageIssueLevel Level { get; } 
 public string Solution { get; } 
 public string Title { get; }

}

Each issue has a Title, a Description, a Level, which can be either Warning or Error, and a proposed Solution. Note: currently, nuget.exe ignores the Level property and reports all issues as Warning.

Here’s the Validate method that we wrote for the previously mentioned rule to enforce all package Ids start with Microsoft.AspNet.*.

 public IEnumerable<PackageIssue> Validate(IPackage package) 
{ 
  if (!package.Id.StartsWith("Microsoft.AspNet.", StringComparison.Ordinal)) 
     { 
       yield return new PackageIssue(
       title: "Package Id is not valid", 
       description: "The Id of this package doesn't start with 'Microsoft.AspNet.*'", 
       solution: "Rename the Id attribute so that it starts with 'Microsoft.AspNet.*'"); 
     } 
} 

Deploy rule assembly

After you’ve successfully compiled the project, you need to put the assembly into the right place. nuget.exe looks for extension assemblies in three places:

  • The %localappdata%\nuget\commands directory.
  • The current directory from which nuget.exe runs. Important: In this case, your assembly’s name must end with Extensions.dll, or else nuget.exe will ignore it.
  • A list of directories as specified by the NUGET_EXTENSIONS_PATH environment variable, separated by commas.

Summary

When nuget.exe builds a package, it runs a set of analysis rules to spot potential issues with the package. You can write your own custom rules to enforce additional requirements in your organization. A rule is simply a class that drives from the IPackageRule interface and is exported via MEF.

Category
Insights

Author

0 comments

Discussion are closed.