Using and Authoring .NET Tools: Multi-Targeting, CI, and Best Practices
Andrew Lock explores the complexities of authoring and managing .NET tools, offering practical advice on runtime targeting, manifest management, and CI testing for developers.
Using and Authoring .NET Tools: Multi-Targeting, CI, and Best Practices
By Andrew Lock
.NET tools provide a flexible way to distribute and use command-line and GUI utilities via NuGet, with options to install them either globally or locally for a specific project. This guide explores the practical issues around authoring, targeting different .NET runtimes, and testing tools in continuous integration pipelines.
What are .NET Tools?
.NET tools are applications packaged for distribution on NuGet and installable via the .NET SDK. They can be global tools, available system-wide, or local tools, associated with a project’s codebase through a tool manifest (a JSON file checked into version control).
Example: Creating a Local Tool Manifest
dotnet new tool-manifest
This command creates .config/dotnet-tools.json
, where you can list required tools for your project.
Installing and Running Tools
Install a tool for your project:
dotnet tool install Cake.Tool
Run the installed tool:
dotnet tool run dotnet-cake # or
dotnet dotnet-cake
dotnet cake
Supporting Multiple Runtimes: Multi-Targeting
.NET tools are dependent on the runtime they target. To ensure wide compatibility, consider multi-targeting several frameworks in your .csproj:
<PropertyGroup>
<TargetFrameworks>netcoreapp2.1;netcoreapp3.0;netcoreapp3.1;net5.0;net6.0;net7.0;net8.0;net9.0</TargetFrameworks>
</PropertyGroup>
- Limitation: You must stick to APIs available in the lowest targeted framework.
- Downside: Package size increases, which can slow down package restore and tool startup operations.
Simplifying Runtime Forwarding with RollForward
Instead of targeting every potential runtime, use the <RollForward>
option to let your tool run on newer compatible runtimes:
<PropertyGroup>
<TargetFramework>net6.0</TargetFramework>
<RollForward>Major</RollForward>
</PropertyGroup>
This tells the .NET host to use newer available runtimes. This technique is especially helpful for future .NET versions.
- Caveat: This assumes .NET’s backward compatibility holds, but it isn’t strictly guaranteed.
CI and Testing Tips for .NET Tools
Local Package Testing
- Use
--source
to specify where to install tools from (e.g. a local folder of .nupkg files) - Use
--tool-path
to install to a specific directory without affecting the global tool cache.
Example:
dotnet tool install dd-trace \
--add-source /app/install/. \
--tool-path /tool \
--version 1.2.3
Installing Pre-release Versions
If your tool has a version suffix (e.g. 1.0.0-beta
), you must supply --prerelease
:
dotnet tool install dd-trace \
--add-source /app/install/. \
--tool-path /tool \
--version 1.2.3-preview \
--prerelease
Downgrading Tool Versions
By default, downgrades are blocked. Add --allow-downgrade
to permit installing lower versions:
dotnet tool update -g dotnet-serve --version 1.10.175 --allow-downgrade
Both install
and update
support this flag in recent .NET SDKs.
Key Takeaways
- Use tool manifests for local tool management and reproducible CI environments.
- Multi-target frameworks for the best compatibility at the cost of bigger packages and lowest-common-denominator APIs.
- RollForward is a powerful alternative to multi-targeting, especially for future .NET versions.
- Be aware of pre-release installation and version downgrade behaviors.
- CI testing and local installs are simplified by using
--source
and--tool-path
.
This comprehensive guide ensures your .NET tools are robust, flexible, and CI-friendly, serving diverse runtime environments and development workflows.
This post appeared first on “Andrew Lock’s Blog”. Read the entire article here