Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Cannot install globally a .NET tool on Linux when the version contains uppercase #39105

Closed
meziantou opened this issue Feb 28, 2024 · 6 comments
Assignees
Labels
Area-Tools good first issue Issues that would be a good fit for someone new to the repository. Narrow in scope, well-defined.
Milestone

Comments

@meziantou
Copy link
Contributor

meziantou commented Feb 28, 2024

Describe the bug

Cannot install globally a .NET tool on Linux when the version contains an uppercase character

To Reproduce

Be sure to run dotnet tool update on Linux or a case-sensitive file-system

dotnet new console
dotnet pack /p:PackAsTool=true /p:version=1.0.0-abc
dotnet pack /p:PackAsTool=true /p:version=1.0.0-Dummy

# ✔️ works
dotnet tool update test --version 1.0.0-abc --global --add-source ./bin/Release/

# ❌ crash
dotnet tool update test --version 1.0.0-Dummy --global --add-source ./bin/Release/

Exceptions (if any)

Skipping NuGet package signature verification.
Unhandled exception: System.IO.FileNotFoundException: Could not find file '/home/repro/.dotnet/tools/.store/.stage/pfgaxwyq.dh2/test/1.0.0-dummy/test.1.0.0-dummy.nupkg'.
File name: '/home/repro/.dotnet/tools/.store/.stage/pfgaxwyq.dh2/test/1.0.0-dummy/test.1.0.0-dummy.nupkg'
   at Interop.ThrowExceptionForIoErrno(ErrorInfo errorInfo, String path, Boolean isDirError)
   at Microsoft.Win32.SafeHandles.SafeFileHandle.Open(String path, OpenFlags flags, Int32 mode, Boolean failForSymlink, Boolean& wasSymlink, Func`4 createOpenException)
   at Microsoft.Win32.SafeHandles.SafeFileHandle.Open(String fullPath, FileMode mode, FileAccess access, FileShare share, FileOptions options, Int64 preallocationSize, UnixFileMode openPermissions, Int64& fileLength, UnixFileMode& filePermissions, Boolean failForSymlink, Boolean& wasSymlink, Func`4 createOpenException)
   at System.IO.Strategies.OSFileStreamStrategy..ctor(String path, FileMode mode, FileAccess access, FileShare share, FileOptions options, Int64 preallocationSize, Nullable`1 unixCreateMode)
   at System.IO.File.Open(String path, FileMode mode, FileAccess access, FileShare share)
   at NuGet.Protocol.LocalFolderUtility.<>c__DisplayClass48_0.<GenerateNupkgMetadataFile>b__0()
   at NuGet.Common.ConcurrencyUtilities.ExecuteWithFileLocked(String filePath, Action action)
   at NuGet.Repositories.NuGetv3LocalRepository.GetPackage(String packageId, NuGetVersion version, String path)
   at NuGet.Repositories.NuGetv3LocalRepository.FindPackageImpl(String packageId, NuGetVersion version)
   at NuGet.Repositories.NuGetv3LocalRepository.FindPackage(String packageId, NuGetVersion version)
   at Microsoft.DotNet.Cli.ToolPackage.ToolPackageDownloader.CreateAssetFile(PackageId packageId, NuGetVersion version, DirectoryPath packagesRootPath, DirectoryPath assetFileDirectory, String runtimeJsonGraph, String targetFramework)
   at Microsoft.DotNet.Cli.ToolPackage.ToolPackageDownloader.<>c__DisplayClass8_0.<InstallPackage>b__0()
   at Microsoft.DotNet.Cli.TransactionalAction.Run[T](Func`1 action, Action commit, Action rollback)
   at Microsoft.DotNet.Tools.Tool.Install.ToolInstallGlobalOrToolPathCommand.<>c__DisplayClass18_0.<Execute>b__1()
   at Microsoft.DotNet.Tools.Tool.Install.ToolInstallGlobalOrToolPathCommand.RunWithHandlingInstallError(Action installAction)
   at Microsoft.DotNet.Tools.Tool.Install.ToolInstallGlobalOrToolPathCommand.Execute()
   at Microsoft.DotNet.Tools.Tool.Update.ToolUpdateGlobalOrToolPathCommand.Execute()
   at System.CommandLine.Invocation.InvocationPipeline.Invoke(ParseResult parseResult)
   at Microsoft.DotNet.Cli.Program.ProcessArgs(String[] args, TimeSpan startupTime, ITelemetry telemetryClient)

Further technical details

.NET SDK:
 Version:           8.0.200
 Commit:            438cab6a9d
 Workload version:  8.0.200-manifests.cdf2cc8e

Runtime Environment:
 OS Name:     ubuntu
 OS Version:  22.04
 OS Platform: Linux
 RID:         linux-x64
 Base Path:   /usr/share/dotnet/sdk/8.0.200/

.NET workloads installed:
There are no installed workloads to display.

Host:
  Version:      8.0.2
  Architecture: x64
  Commit:       1381d5ebd2

.NET SDKs installed:
  8.0.200 [/usr/share/dotnet/sdk]

.NET runtimes installed:
  Microsoft.AspNetCore.App 8.0.2 [/usr/share/dotnet/shared/Microsoft.AspNetCore.App]
  Microsoft.NETCore.App 8.0.2 [/usr/share/dotnet/shared/Microsoft.NETCore.App]

Other architectures found:
  None

Environment variables:
  Not set

global.json file:
  Not found

Learn more:
  https://aka.ms/dotnet/info

Download .NET:
  https://aka.ms/dotnet/download

Was originally opened on NuGet/Home: NuGet/Home#13275

@Forgind
Copy link
Member

Forgind commented Jun 11, 2024

@dsplaisted,

I looked into this, and I think the problem is just because PackageId's ToString method always calls ToLowerInvariant. I tried to see why, and the commit looked like it was just a repo merge commit, not a real commit. Do you know why we're calling ToLowerInvariant here? Can we just remove that?

@Forgind Forgind added the good first issue Issues that would be a good fit for someone new to the repository. Narrow in scope, well-defined. label Jun 11, 2024
@Forgind
Copy link
Member

Forgind commented Jun 11, 2024

Place with ToLowerInvariant:

_id = id?.ToLowerInvariant() ?? throw new ArgumentNullException(nameof(id));

@Forgind Forgind added the needs team triage Requires a full team discussion label Jun 11, 2024
@marcpopMSFT
Copy link
Member

Triage: Per Daniel's understanding, NuGet treats package IDs as all lowercase (CC @nkolev92 ) so there is likely a different path where we didn't lowercase it.

@marcpopMSFT marcpopMSFT removed untriaged Request triage from a team member needs team triage Requires a full team discussion labels Jun 11, 2024
@marcpopMSFT marcpopMSFT added this to the Backlog milestone Jun 11, 2024
@marcpopMSFT
Copy link
Member

Follow-up: Per the semver spec the ID is lowercase but the version (prerelease part) is case sensitive so probably somewhere we're treating it as lowercase when we shouldn't be.

@KalleOlaviNiemitalo
Copy link

Per the semver spec the ID is lowercase

Do you refer to some NuGet-only spec? https://semver.org doesn't seem to mention IDs.

but the version (prerelease part) is case sensitive

NuGet package version is documented to deviate from semver by being case-insensitive: https://github.com/NuGet/docs.microsoft.com-nuget/blob/070022ca55eee4f855088a684254f132c0ba386d/docs/concepts/Package-Versioning.md?plain=1#L269

@KalleOlaviNiemitalo
Copy link

This makes me wonder if there are similar problems with other package version normalisation that NuGet does, e.g. a tool with version 1.0.0.0 being normalised to 1.0.0.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Area-Tools good first issue Issues that would be a good fit for someone new to the repository. Narrow in scope, well-defined.
Projects
None yet
Development

No branches or pull requests

5 participants