If a tool is built for multiple .NET versions, the DotnetGlobalToolInstaller will cache only on one of these tool targets, and may try to use it on an incompatible version of .NET on a different pipeline.
Let's say I have a dotnet tool nuget package "foobar" with version "1.2.3" that targets both .NET 10 and .NET 8. These versions are bundled in the same nuget package, but the version actually installed will be the one that matches the most recent .NET SDK installed.
Sequence of events on the same pipeline agent:
Pipeline 1 runs:
- pipeline installs .NET 10 sdk (through UseDotNet@2)
- pipeline invokes this DotnetGlobalToolInstaller for version "1.2.3"
- cache miss for tool version "1.2.3"
- internally runs
dotnet tool install which implicitly installs the .NET 10 version (because the .NET 10 sdk is installed)
- caches this tool version in the agent
Pipeline 2 runs:
- pipeline installs .NET 8 sdk (through UseDotNet@2)
- pipeline invokes this DotnetGlobalToolInstaller for version "1.2.3"
- Expected: cache-miss while trying to find version "1.2.3" (because .NET versions don't match)
- Expected: .NET 8 version of the tool is installed and cached
- Actual: cache-hit for tool version "1.2.3"
- Actual: .NET 10 tool version is used and errors out because the .NET 10 sdk/runtime isn't installed
A couple of ideas to solve this from looking over the documentation:
- Use the .NET moniker as a cache key for the dotnet tool. You can get the .NET version of the installed tool by looking at the
.store/<tool name>/<version>/project.assests.json file in the local tool installation path.
- Set the .NET version during dotnet tool installation by using the framework param. eg
dotnet tool install --framework net9.0, and use it as a cache key. This will install the tool with the framework closest and still compatible with the desired framework. (So .NET 8, if the tool doesn't have a .NET 9 version). You could detect which version of .NET you should be using by running dotnet --version
- As a last resort, add an optional .NET version framework moniker property that gets passed into
dotnet tool install --framework <moniker> and is used as a cache key so that different .NET versions don't overlap.
If a tool is built for multiple .NET versions, the DotnetGlobalToolInstaller will cache only on one of these tool targets, and may try to use it on an incompatible version of .NET on a different pipeline.
Let's say I have a dotnet tool nuget package "foobar" with version "1.2.3" that targets both .NET 10 and .NET 8. These versions are bundled in the same nuget package, but the version actually installed will be the one that matches the most recent .NET SDK installed.
Sequence of events on the same pipeline agent:
Pipeline 1 runs:
dotnet tool installwhich implicitly installs the .NET 10 version (because the .NET 10 sdk is installed)Pipeline 2 runs:
A couple of ideas to solve this from looking over the documentation:
.store/<tool name>/<version>/project.assests.jsonfile in the local tool installation path.dotnet tool install --framework net9.0, and use it as a cache key. This will install the tool with the framework closest and still compatible with the desired framework. (So .NET 8, if the tool doesn't have a .NET 9 version). You could detect which version of .NET you should be using by runningdotnet --versiondotnet tool install --framework <moniker>and is used as a cache key so that different .NET versions don't overlap.