dotnet / dotnet/command-line-api
dotnet-suggest: Retrieve "real/final" path for symlink compatibility
- Dominant language
- C#
- Stars
- 3.7k
- Forks
- 428
- PR merge metrics
- No merged PRs in 30d
Description
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:
- If `DOTNET_CLI_HOME` is set, use that path as the base directory for the `.dotnet\tools` directory
- Otherwise, use `%USERPROFILE%` (Windows) or `$HOME` (Linux) as the base directory for `.dotnet\tools`
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)](https://man.archlinux.org/man/realpath.3.en) while on Windows [GetFinalPathNameByHandle](https://learn.microsoft.com/en-us/windows/win32/api/fileapi/nf-fileapi-getfinalpathnamebyhandlew) 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.
Contributor guide
Assessment
This issue has not been assessed yet.