[Pack] Handle empty Version tag in nuspec error#6092
Conversation
| break; | ||
| case "version": | ||
| manifestMetadata.Version = NuGetVersion.Parse(value); | ||
| NuGetVersion version; |
There was a problem hiding this comment.
How does pack behave when someone passes an actual empty or unparseable version on the commandline?
There was a problem hiding this comment.
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.
Can you link where that happens?
There was a problem hiding this comment.
| 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.
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.
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.
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.
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.
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.
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.
donnie-msft
left a comment
There was a problem hiding this comment.
The code looks fine to me, but since I don't know whether this is better, so just commenting for now.
9c783f0 to
67ed60d
Compare
donnie-msft
left a comment
There was a problem hiding this comment.
Thanks for addressing my earlier feedback. The implementation looks good to me but some of the tests are unclear.
| 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.
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.
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.
I looked at other approach's but given that this is very specific scenario I wanted to do minimal changes.
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.0version 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 specandnuget packcommands.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 emptyVersiontag is not common and setting the0.0.0version 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
-Versionproperty 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