Best practices for writing modules

This page gives some best practices for writing Cake modules. Each guideline describes either a good or bad practice.

The wording of each guideline indicates how strong the recommendation is:

Do guidelines should nearly always be followed. You need a really unusual case for breaking a Do guideline.

Consider guidelines should generally be followed. You can deviate from a Consider guideline if you fully understand the meaning behind the guideline and have a good reason to do so.

Avoid guidelines indicates something you should almost never do.

Naming

§1.1 Do use Cake.xxx.Module as the naming schema for modules, where xxx is a meaningful and unique name that describes what the module does.

Why? Following a convention makes it easier for users to find and work with modules. The automated AddinDiscoverer, which is used for auditing and keeping website up to date, also uses this convention to identify Cake modules.

Example:

Cake.Npm.Module is the name that clearly identifies the module for Cake adds support for the npm package manager when installing tools in your Cake build scripts. This name is used in the GitHub repo, it's the name of the solution file, it's the name of the project file, the name of the generated assembly and finally, it's also the name of the NuGet package.


§1.2 Do use the same name for the GitHub repo, the C# solution name, the assembly generated from the project and the NuGet package.

Why? Using the same name across different artifacts makes it easier for users and improves results of automated processes.

References

Cake reference

§2.1 Do reference the lowest version of Cake with API compatibility to the latest version (currently 5.0.0).

Why? This gives the best support for different versions of Cake. Modules built against newer versions of Cake might not be compatible with previous versions of Cake and vice-versa, modules built against older versions might not be compatible with future versions of Cake (this is especially true when a future version of Cake introduces breaking changes).

Cake versions which are API compatible with a specific module will be shown when the module is added to the Cake website.


§2.2 Do reference a newer version than Cake 5.0.0 if the module requires a specific functionality.

Why? If a specific feature of Cake is required in a module the lowest version of Cake which introduces this feature should be referenced to have access to the feature and the best support for different versions of Cake.


§2.3 Do update module to a newer version of Cake if a version of Cake with breaking changes becomes available.

Why? Cake will output a warning that the module was built against an incompatible version of Cake and the module might no longer work. It is incumbent on module authors to upgrade their references and publish new version of their NuGet packages


§2.4 Avoid dependencies to Cake.Core and Cake.Common in the NuGet package.

Why? Those references are being implicitly added by the Cake engine.

Example:

References to Cake.Core and Cake.Common need to be marked as private assets in the project file:

<ItemGroup>
  <PackageReference Include="Cake.Core" Version="1.0.0" PrivateAssets="All" />
  <PackageReference Include="Cake.Common" Version="1.0.0" PrivateAssets="All" />
</ItemGroup>

When using a nuspec file omit the references to Cake.Core and Cake.Common

.NET target version

§2.5 Do target net8.0.

Why? Targeting to net8.0 should work for most modules to support the latest version of available Cake runners, operating systems and platforms.

§2.6 Avoid targeting additional frameworks such as net7.0 or net9.0.

Why? Due to current limitations (1, 2) on how modules are loaded, Cake attempts to load all assemblies included in the NuGet package of the module (of all targets) which causes warnings to be displayed and the impression that the module was not loaded and/or is not working. Once those limitations are fixed, we'll update this recommendation to align with the recommendations for Cake Addins.

Package metadata

Icons

§3.1 Do define an icon for the NuGet package.

Why? Icons helps the user to identify Cake modules.


§3.2 Consider using the Cake Contrib icon.

Why? When the module doesn't have an own product icon, using the Cake Contrib icon gives a common branding to Cake modules.


§3.3 Avoid using the Cake icon.

Why? The Cake icon is reserved for core Cake packages.


§3.4 Do embed the icon in the package.

Why? Using an icon embedded in the package is more reliable then linking to an icon an external web site which hosts the icon.

Example:

This can be done with the following line in the modules .csproj:

<PackageIcon>PackageIcon.png</PackageIcon>

The modules .csproj should also contain a reference to the png file:

<ItemGroup>
  <None Include="..\PackageIcon.png" Pack="true" PackagePath="" />
</ItemGroup>

Notice the Pack attribute, this is particularly important to ensure the file is embedded in the NuGet package.

Until early 2019, the recommendation was to reference the Cake Contrib icon hosted on the rawgit CDN but rawgit announced that it would shutdown in October 2019 therefore the recommendation changed to reference the Cake Contrib icon hosted on the jsDelivr CDN. This recommendation changed once again in the fall of 2019 when NuGet started supporting embedded icons.

Tags

§3.5 Do add cake-module to the NuGet-Tags.

Why? NuGet can show instructions on how to install a package in Cake since NuGet/NuGetGallery#8381. Only if the correct tag is used, the correct installation instructions can be shown.

Documentation

§4.1 Do follow the Cake Documentation Guidelines.

Why? Proper XML comments and usage of CakeModuleAttribute is important to be able to show documentation for the module when it is added to the Cake website.

§4.2 Do add documentation XML files to the NuGet package.

Why? XML documentation files are used to show documentation for the API provided by the module on the Cake website.

Testing

§5.1 Do test the module with the different runners.

Why? .NET Tool, script runners and Cake Frosting have slight differences on what dependencies are loaded by the runner and how dependencies are loaded.

Runners with which a module is not compatible should be documented in the XML comment of the class implementing the ICakeModule interface.

§5.2 Do test the module on all operating systems supported by the different Cake runners.

Why? Different platforms have differences in for example file system or available tools.

Operating systems with which a module is not compatible should be documented in the XML comment of the class implementing the ICakeModule interface.

§5.3 Do test the module on all platforms supported by the different Cake runners.

Why? Different platforms have slight differences in implementation and might break a module.

Platforms, supported by Cake, with which a module is not compatible should be documented in the XML comment of the class implementing the ICakeModule interface.