-
Notifications
You must be signed in to change notification settings - Fork 694
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
[Pack] Handle empty Version tag in nuspec error #6092
Conversation
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
A few questions.
@@ -86,7 +86,9 @@ private static void ReadMetadataValue(ManifestMetadata manifestMetadata, XElemen | |||
manifestMetadata.Id = value; | |||
break; | |||
case "version": | |||
manifestMetadata.Version = NuGetVersion.Parse(value); | |||
NuGetVersion version; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
How does pack behave when someone passes an actual empty or unparseable version on the commandline?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
We do a NuGetVersion.TryParse
and if that fails we throw:
Error NU5010: Version string specified for package reference ' ' is invalid.
Error NU5010: Version string specified for package reference 'sdf' is invalid.
Empty string:
Missing option value for: '-Version'
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Can you link where that happens?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
manifestMetadata.Version = NuGetVersion.Parse(value); | ||
NuGetVersion version; | ||
NuGetVersion.TryParse(value, out version); | ||
manifestMetadata.Version = version ?? new NuGetVersion(0, 0, 0); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
What if we just allowed it be null instead of a default?
Do we ever need to differentiate between that and a 0,0,0?
Would we detect that no version has been set somewhere different?
Another idea would be, what if we didn't set a version element in the nuspec.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This version is used to create the nupkg name, so in this case an empty version in the nuspec will create the package as package.0.0.0.nupkg
so most likely there is a way to detect that no version has been set later in the code.
Another idea would be, what if we didn't set a version element in the nuspec.
I'm not sure what would this cause, for me it looks like version is very important
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'm not sure what would this cause, for me it looks like version is very important
I was thinking as more of a test case actually. What would happen if there was no version specified in the nuspec when we attempt to pack and just pass it on the CLI.
That feels like the closest analog to this test case.
Version is a mandatory element, so I'm just worried about the potential fall out of setting a default version.
How are token replacements done? Can we parse the CLI version and validate it before the we read the manifest?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I was thinking as more of a test case actually. What would happen if there was no version specified in the nuspec when we attempt to pack and just pass it on the CLI.
Currently, if you don't have the version tag it will fail saying that the file is incorrect, if it is empty, it will fail because we try to parse that empty string and fail if the parsing returns null.
Can we parse the CLI version and validate it before the we read the manifest?
We do validate the CLI version before reading the manifest, and after reading the manifest we check if we specified a CLI version and replace it with it.
So I think after this conversation I think the best solution is:
- Continue validating the CLI version
- Don't immediately break when there is an empty Version in the nuspec
- When we would do the "replacement", if CLI version is invalid and there is no version in nuspec break. Otherwise continue as how it is right now. (if CLI version is valid it would override the .nuspec version)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I was thinking, what if we validate once?
Can we inject the CLI version into the nuspec before reading the manifest, or is that not possible?
My fear with not validating the manifest is that we're just introducing an unusual behavior that may lead to missing out a mandatory metadata.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Sorry for the late reply, I think I was able to address this concern, I'm validating the CLI version and updating the manifest version if it's a valid version.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The code looks fine to me, but since I don't know whether this is better, so just commenting for now.
src/NuGet.Core/NuGet.Packaging/PackageCreation/Authoring/ManifestReader.cs
Outdated
Show resolved
Hide resolved
9c783f0
to
67ed60d
Compare
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Thanks for addressing my earlier feedback. The implementation looks good to me but some of the tests are unclear.
test/NuGet.Core.Tests/NuGet.Commands.Test/PackCommandRunnerTests.cs
Outdated
Show resolved
Hide resolved
test/NuGet.Core.Tests/NuGet.Commands.Test/PackCommandRunnerTests.cs
Outdated
Show resolved
Hide resolved
test/NuGet.Core.Tests/NuGet.Commands.Test/PackCommandRunnerTests.cs
Outdated
Show resolved
Hide resolved
test/NuGet.Core.Tests/NuGet.Commands.Test/PackCommandRunnerTests.cs
Outdated
Show resolved
Hide resolved
test/NuGet.Core.Tests/NuGet.Commands.Test/PackCommandRunnerTests.cs
Outdated
Show resolved
Hide resolved
src/NuGet.Core/NuGet.Packaging/PackageCreation/Authoring/ManifestReader.cs
Outdated
Show resolved
Hide resolved
test/NuGet.Core.Tests/NuGet.Commands.Test/PackCommandRunnerTests.cs
Outdated
Show resolved
Hide resolved
test/NuGet.Core.Tests/NuGet.Commands.Test/PackCommandRunnerTests.cs
Outdated
Show resolved
Hide resolved
test/NuGet.Core.Tests/NuGet.Commands.Test/PackCommandRunnerTests.cs
Outdated
Show resolved
Hide resolved
test/NuGet.Core.Tests/NuGet.Commands.Test/PackCommandRunnerTests.cs
Outdated
Show resolved
Hide resolved
@@ -143,6 +145,16 @@ public static Manifest ReadFrom(Stream stream, Func<string, string> propertyProv | |||
// Deserialize it | |||
var manifest = ManifestReader.ReadManifest(document); | |||
|
|||
// Update manifest metadata version if version was provided by the CLI command | |||
if (propertyProvider is not null && propertyProvider.Target.GetType().Name.Equals("PackArgs")) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think I'd rather see a public API change, rather than adding a condition based on a delegate's type name, or other uses of reflection.
Before I spend time looking into the code paths too deeply myself, what other approaches have you considered? I was wondering if having an additional Action<Manifest>
delegate (or Func<Manifest, Manifest>
delegate, so the method signature/public API wouldn't need to change if Manifest
eventually becomes immutable) would allow this method to provide extensibility after deserialization and before validation. But looking just a little bit into the code, that would need a bunch of public APIs on PackageBuilder as well, to pass through to here. I still feel like it might be better than checking the delegate's type name though
Did you try not putting this condition, and always overriding the version when propertyProvider
has a value for it? What negative side effects are there?
also, the PR description talks about setting the value to 0.0.0, which is no longer happening, so the PR description needs to be updated.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Did you try not putting this condition, and always overriding the version when propertyProvider has a value for it? What negative side effects are there?
Yes, that was my first implementation but realized that some tests were failing. After doing some debugging, I realized that this function is used in multiple scenarios, the one I'm trying to solve is when the customer is using a nuspec.
The tests that were failing is in the scenario when we are using pack and a .csproj and nuspec. That path calls Manifest.ReadFrom
using another propertyProvider which represents the project file (.csproj) not the nuspec, so the information wasn't correct.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I looked at other approach's but given that this is very specific scenario I wanted to do minimal changes.
src/NuGet.Core/NuGet.Packaging/PackageCreation/Authoring/ManifestReader.cs
Outdated
Show resolved
Hide resolved
test/NuGet.Core.Tests/NuGet.Commands.Test/PackCommandRunnerTests.cs
Outdated
Show resolved
Hide resolved
c2236c1
to
8a02c4a
Compare
Bug
Fixes: NuGet/Home#7987
Context of the bug
When running nuget pack with a .nuspec file that contains an empty version and passing -Version on the command line, nuget will fail with the error:
However, you put in a bogus version identifier in the nuspec file, the same command will run happily, with the version number passed on the command line.
Description
After having conversations, right now the solution is to override the nuspec version with the CLI version that the customer specified.
This PR makes that when the Version tag is empty it creates an0.0.0
version which will be used to pack the package.Currently how it works is that we read the .nuspec and validate that Version is not null, this validation is used by the
nuget spec
andnuget pack
commands.Removing an error is a breaking change, so I'm not sure if this is the best solution but for me the scenario for an emptyVersion
tag is not common and setting the0.0.0
version sounds less disruptive. Also, I tried to lag a warning but looks like the logger is not close to the validation close.Since we run this validation when reading the file, I cannot check if we are using the
-Version
property easily, the other option for to fix this is to move that validation to another place where we can check it against pack args.PR Checklist