Allow `dotnet restore` to also invoke `dotnet tool restore`
See original GitHub issueIssue
No Guaranteed Availability for Local Tool during Build
Currently, we are in the process of upgrading our application from Core 2.2 to Core 3.1. One of the functionalities we depend on in 2.2 is the ability to reference a tool as a project dependency for guaranteed availability during build. Specifically, we reference Swashbuckle.AspNetCore.Cli
and then have a MSBuild extension to execute it using a <Target>
node in the .csproj
. In 2.2 it looks like:
<PropertyGroup>
<TargetFramework>netcoreapp2.2</TargetFramework>
<AspNetCoreHostingModel>InProcess</AspNetCoreHostingModel>
<OutputType>Exe</OutputType>
</PropertyGroup>
<ItemGroup>
<DotNetCliToolReference Include="Swashbuckle.AspNetCore.Cli" Version="2.1.0-beta1" />
</ItemGroup>
<Target Name="SwaggerToFile" AfterTargets="AfterBuild">
<Exec Command="dotnet swagger tofile --output $(TargetDir)openapi.json "$(TargetPath)" v1" />
</Target>
However, with the move to local tools in Core 3.x there is no longer a guarantee the tool is installed prior to the build. Though we have added the requisite local tool manifest to the repository root and removed the DotNetCliToolReference
as required by the upgrade to 3.1, without modification to the custom Target
the build will error unless the developer has manually run dotnet tool restore
.
Solutions
Option 1 - Extend Custom Target (Works, Non-Ideally)
The solution that does currently work is to modify the custom Target
to also call dotnet tool restore
. This looks like this:
<PropertyGroup>
<TargetFramework>netcoreapp3.1</TargetFramework>
</PropertyGroup>
<Target Name="SwaggerToFile" AfterTargets="AfterBuild">
<Exec Command="dotnet tool restore" />
<Exec Command="dotnet swagger tofile --output $(TargetDir)openapi.json "$(TargetPath)" v1" />
</Target>
This however is not ideal as it prevents the developer from using the tool directly until they have built once or manually run dotnet tool restore
themselves. While a minor inconvenience, this is a gotcha that requires extra documentation and developer training to avoid–it is, to borrow a phrase, not the pit of success.
Option 2 - Add 2nd Custom Target to Extend Restore (Doesn’t Currently Work, Ideal)
The ideal solution would then be to extend the dotnet restore
command to also call dotnet tool restore
when this project is being restored. This is better because Visual Studio will automatically restore a solution/project when it is loaded and developers are already in the habit of running dotnet restore
when they see complaints about missing packages. I tried to do this myself with a 2nd custom target but MSBuild does not seem to respect the extension of the Restore
target which I assume exists based on this documentation. My attempt at making this work looks like:
<PropertyGroup>
<TargetFramework>netcoreapp3.1</TargetFramework>
</PropertyGroup>
<!-- Doesn't work :( -->
<Target Name="LocalToolRestore" AfterTargets="Restore">
<Exec Command="dotnet tool restore" />
</Target>
<Target Name="SwaggerToFile" AfterTargets="AfterBuild">
<Exec Command="dotnet swagger tofile --output $(TargetDir)openapi.json "$(TargetPath)" v1" />
</Target>
Why this doesn’t currently work I’m not sure, though I guess there may be a problem with the working directory or context in which dotnet restore
is run; that, or the Restore
target simply isn’t currently extensible via a .csproj
the way Build
and similar targets are. Whatever the case, I believe that for tools which will be used during or after build it would be a significantly improved experience if this extension were possible (it does not have to be mandatory behavior, an opt-in is fine).
Disclaimer
It is entirely possible that I have forgotten a key step to making Option 2 work or have not fully understood the implications of the move to local tooling in 3.x. If this is the case, I welcome instruction on how Option 2 is possible today or explanation about how this is an abuse of local tooling (which I expect would then lead to an issue on the Swashbuckle repo).
Issue Analytics
- State:
- Created 3 years ago
- Reactions:22
- Comments:5
Top GitHub Comments
The other way of thinking about this, perhaps, is for the dotnet tooling itself to detect that a: there is a tool defined in the config with that name (in the config), and b: that is isn’t already installed: just… install it? So: on demand install of the tool. Then the question of whether or not it is already installed becomes invisible to the consumer.
Related: #4241