dotnet / command-line-api

Command line parsing, invocation, and rendering of terminal output.
https://github.com/dotnet/command-line-api/wiki
MIT License
3.35k stars 376 forks source link

dotnet-suggest: Retrieve "real/final" path for symlink compatibility #2301

Open ralish opened 7 months ago

ralish commented 7 months ago

The dotnet-suggest tool will fail to work if .NET global tools are launched from a path other than the default calculated path.

The logic is implemented in the GlobalToolsSuggestionRegistration and DotnetProfileDirectory classes and can be summarised as:

An important takeaway from the above is it doesn't appear to be possible to change the .dotnet directory name itself, only its parent directory. Assume that a system has the .dotnet directory symlinked to another location (e.g. %USERPROFILE%\Apps\.dotnet) which is also present in the %PATH%. In this case .NET global tools will be launched from %USERPROFILE%\Apps\.dotnet\tools\some-tool.exe (the target .dotnet directory could also be named differently, but it won't change the result).

The GlobalToolsSuggestionRegistration implementation of FindRegistration will subsequently fail to recognise this is a global tool as it will calculate the tools shim path as one of the two options above (depending on if DOTNET_CLI_HOME is set) while the executable to find suggestions for will be seen to be under a different location.

This is admittedly an edge case, but it's still a brittle approach (and was surprisingly tricky to debug!). I have an aversion to my user profile directory being littered with dot directories, so often symlink them to other locations. My case is on Windows, but reading the code it looks like it should fail as well under Linux or macOS.

The solution is probably to resolve all symlinks in the path so that it's "static". On Linux that would be using something like realpath(3) while on Windows GetFinalPathNameByHandle should be suitable. It may be worth adding extra logging as well for some of these failure cases as it would have saved a lot of time.

Thanks in advance for all the hard work, and if I get a chance over the Xmas break I might have a go at implementing this if the .NET team are open to the suggested approach.

ralish commented 7 months ago

Noting this is similar to #2201.