NuGet product used (NuGet.exe | VS UI | Package Manager Console | dotnet.exe): Nuget.exe
NuGet version (x.x.x.xxx): 4.9.4
Worked before? If so, with which NuGet version: No
This is related to #2039
As of today, running 'nuget install pkgFoo -version X' will create an installation directory based on the version number embedded in pkgFoo's nuspec. Nuget may or may not normalize that version number, depending on what NugetVersion.ToString() decides.
As a result of this, the installation directory cannot be predicted ahead of time.
This is inconvenient for a number of reasons in our team's tooling. For example, our build system needs to be able to predict outputs ahead of time.
While changing the default behavior might not be possible due to back-compat concerns, it should be possible to add a command line flag / nuget.config setting / environment variable to ensure versions are always normalized.
What are your thoughts?
I think I agree from the perspective that Nuget (or any other packaging tooling) should not be changing any version number given to it, unless its been given a specific command or instruction to do so. Keeping the version number given would make the entire nuget system predictable as far as filesystem pathing and dependency referencing are concerned.
Currently the nuget team has only offered to accept a PR for a -VerbatimVersion switch that will bypass the current forced version normalization.
Nuget may or may not normalize that version number, depending on what NugetVersion.ToString() decides.
NuGet will use the trimmed version of what's written in the nuspec. Namely "1.0.0 " becomes "1.0.0".
If there's an original string, NuGetVersion.ToString() will just print the original string.
So it should be predictable.
Do you any cases of something different happening here?
A repro would help us understand if the behavior you are seeing is to be expected.
Currently the nuget team has only offered to accept a PR for a -VerbatimVersion switch that will bypass the current forced version normalization.
@StingyJack
That's a different concern, as that's only for the pack scenario.
a better example would be "1.1.2.0" becomes "1.1.2". This isn't predictable.
I manually generated a package with a "1.2.3.0" version.
At install time it was normalized in both restore & nuget install cases.
@nkolev92 - what happens if the package was created before the version normalization change, or was created by a nuget.exe pre dating that?
The decision is made by the consuming tooling at install time.
So it'd remove the trailing 0s where appropriate.
@nkolev92 - here's a concrete example using this package: https://www.nuget.org/packages/Moq/4.2.1502.911
nuget install moq -version 4.2.1502.911There's no way to know that the install directory will have a leading 0 without actually downloading the package and looking in the .nuspec file.
This happens because of this line in the source code, which calls NugetVersion.ToString() (not guaranteed to normalize) on the nuspec version.
@nkolev92 - does my example help clarify the ask?
Yeah it does.
I meant to suggest that I understood your ask in https://github.com/NuGet/Home/issues/8159#issuecomment-497438630, but reading it again I definitely wasn't clear enough :)
@nkolev92 - what's the status of this ask? Is it likely to gain any traction in the near future?
If your team is OK with it conceptually and just lacks time to implement it, I could code up a PR. I'd want to make sure we all agree on a design first, though.
I have not analyzed the potential impact. Of the top of my head, I'm worried about how this behaves when the installation directories are mixed.
There's also the question of how do we enable this.
The global packages folder is user based, and shared among different projects.
@rrelyea can help prioritize as needed.
@nkolev92 - this wouldn't actually impact the global packages cache.
When I run nuget install moq -version 4.2.1502.911, it creates the following directories:
The latter is normalized; the former is not.
Each dir contains a .nupkg. The nupkg names match the dir names. They do not match each other.
The ask here is effectively to make install to use the same dir/nupkg-naming convention (normalized version wins) as the global packages cache.
Ah, ok, i thought this was a general ask.
This makes it less dangerous then.
@nkolev92 the version number we give to our software and packages is not dangerous.
Nuget changing the value we specify is what is dangerous because it leads to unexpected and unpredictable results.
I came to this page because I encountered a case where the truncation example given by @StingyJack causes Pack to fail. Following is the relevant portion of my build log.
AfterBuild:
Info: Assembly WizardWrx.DiagnosticInfo.dll version = 7.21.2.0
Package:
NuGet.exe pack DiagnosticInfo.csproj -Properties Configuration=Release -OutputDir Package -IncludeReferencedProjects
Attempting to build package from 'DiagnosticInfo.csproj'.
MSBuild auto-detection: using msbuild version '15.9.21.664' from 'C:\Program Files (x86)\Microsoft Visual Studio\2017\Community\MSBuild\15.0\bin'.
Packing files from 'F:\Source_Code\Visual_Studio\Projects\WizardWrx_Libs\WizardWrx_NET_API\DiagnosticInfo\bin\Release'.
Using 'DiagnosticInfo.nuspec' for metadata.
Found packages.config. Using packages listed as dependencies
Successfully created package 'Package\WizardWrx.DiagnosticInfo.7.21.2.nupkg'.
EXEC : warning : NU5125: The 'licenseUrl' element will be deprecated. Consider using the 'license' element instead.
Publish:
NuGet.exe push WizardWrx.DiagnosticInfo.7.21.2.0.nupkg
File does not exist (WizardWrx.DiagnosticInfo.7.21.2.0.nupkg).
F:\Source_Code\Visual_Studio\Projects\WizardWrx_Libs\WizardWrx_NET_API\DiagnosticInfo\DiagnosticInfo.csproj(110,5): error MSB3073: The command "NuGet.exe push WizardWrx.DiagnosticInfo.7.21.2.0.nupkg" exited with code 1.
Build FAILED.
Relevant sections of csproj follow.
<PropertyGroup>
<Configuration Condition=" '$(Configuration)' == '' ">Debug</Configuration>
<Platform Condition=" '$(Platform)' == '' ">AnyCPU</Platform>
<ProjectGuid>{A248E5A4-B42B-4B10-8F52-E58B06A0BC18}</ProjectGuid>
<OutputType>Library</OutputType>
<AppDesignerFolder>Properties</AppDesignerFolder>
<RootNamespace>WizardWrx</RootNamespace>
<AssemblyName>WizardWrx.DiagnosticInfo</AssemblyName>
<TargetFrameworkVersion>v4.5</TargetFrameworkVersion>
<FileAlignment>512</FileAlignment>
<TargetFrameworkProfile />
<!-- <Deterministic>true</Deterministic> -->
<!-- 2019/07/04 10:08:32 - DAG - Added for NuGet package builder -->
<PackageDir>Package</PackageDir>
</PropertyGroup>
<!-- 2019/07/04 10:15:50 - DAG Everything from this point to the end is new, and was added to support NuGet package generation. -->
<Target Name="AfterBuild">
<!-- Load up the information from the assembly -->
<GetAssemblyIdentity AssemblyFiles="$(OutputPath)$(AssemblyName).dll">
<Output TaskParameter="Assemblies" ItemName="OutputAssemblyInfo" />
</GetAssemblyIdentity>
<Message Text="Info: Assembly $(AssemblyName).dll version = %(OutputAssemblyInfo.Version)" />
</Target>
<Target Name="Package" AfterTargets="AfterBuild" Condition=" '$(Configuration)' == 'Release'">
<!-- Ensure the Package directory exists for this project -->
<MakeDir Directories="$(PackageDir)" />
<!-- Package the project -->
<Exec WorkingDirectory="$(BuildDir)" Command="NuGet.exe pack $(ProjectName).csproj -Properties Configuration=$(Configuration) -OutputDir $(PackageDir) -IncludeReferencedProjects" />
</Target>
<!-- Publish the project. -->
<Target Name="Publish" AfterTargets="Package" Condition=" '$(Configuration)' == 'Release'">
<Exec WorkingDirectory="$(PackageDir)" Command="NuGet.exe push $(AssemblyName).%(OutputAssemblyInfo.Version).nupkg" />
</Target>
To date, I've been using AssemblyVersion strings similar to the following in AssemblyInfo.cs.
[assembly: AssemblyVersion ( "7.21.116.*" )]
However, the corresponding line in AssemblyInfo.cs of the WizardWrx.DiagnosticInfo project is as follows.
[assembly: AssemblyVersion ( "7.21.2.0" )]
This change became necessary to conform to the new Deterministic project property, which eventually I disabled so that I _can_ use the wild card Revision substring. For now, that's how I'll resolve it, so that I can publish today. Nevertheless, the addition of the Determinstic property suggests that the days of wild card build and revision numbering are numbered.
Further investigation led to the creation of the following custom task.
<!-- 2019/12/15 - DAG Copy custom task NuGetPackageNameFixup from AnyCSV. -->
<UsingTask TaskName="NuGetPackageNameFixup"
TaskFactory="CodeTaskFactory"
AssemblyFile="$(MSBuildToolsPath)\Microsoft.Build.Tasks.Core.dll">
<ParameterGroup>
<RawNugetPackageVersion ParameterType="System.String" Required="true" />
<ActualNugetPackageVersion ParameterType="System.String" Output="true" />
</ParameterGroup>
<Task>
<Code Type="Fragment" Language="cs"><![CDATA[
ActualNugetPackageVersion = RawNugetPackageVersion.EndsWith ( ".0" ) ? RawNugetPackageVersion.Substring(0,RawNugetPackageVersion.Length-2) : RawNugetPackageVersion;
Log.LogMessage ( "Custom Task NuGetPackageNameFixup: RawNugetPackageVersion = "+ RawNugetPackageVersion );
Log.LogMessage ( "Custom Task NuGetPackageNameFixup: ActualNugetPackageVersion = "+ ActualNugetPackageVersion );
]]></Code>
</Task>
</UsingTask>
The task is consumed as follows.
<!-- Publish the project. -->
<Target Name="Publish"
AfterTargets="Package"
Condition=" '$(Configuration)' == 'Release'">
<NuGetPackageNameFixup RawNugetPackageVersion="%(OutputAssemblyInfo.Version)">
<Output PropertyName="NuGetPackageVersion"
TaskParameter="ActualNugetPackageVersion" />
</NuGetPackageNameFixup>
<Exec WorkingDirectory="$(PackageDir)"
Command="NuGet.exe push $(AssemblyName).$(NuGetPackageVersion).nupkg" />
</Target>
This is cumbersome and expensive because the above fragment must be incorporated into the project file of _every_ NuGet package that adopts SemVer version numbering, unless there is a way to import them that I have yet to discover. Nevertheless, this is how I must proceed until this issue is properly resolved in the NuGet client.
By cross referencing https://github.com/NuGet/Home/issues/3050, I hope to close a loop.
Most helpful comment
a better example would be "1.1.2.0" becomes "1.1.2". This isn't predictable.