Skip to content

Fix mismatch between GetPackOutputItemsTask and PackTask generated filenames#7137

Open
gekka wants to merge 21 commits into
NuGet:devfrom
gekka:fix-nuget-package-file-name
Open

Fix mismatch between GetPackOutputItemsTask and PackTask generated filenames#7137
gekka wants to merge 21 commits into
NuGet:devfrom
gekka:fix-nuget-package-file-name

Conversation

@gekka
Copy link
Copy Markdown

@gekka gekka commented Feb 14, 2026

Bug

Fixes: Nuget/Home#14711
Fixes: Nuget/Home#12644

Description

This pull request fixes a mismatch between the file name that GetPackOutputItemsTask expects for a generated .nupkg and the actual file name produced by PackTask when a project uses NuspecFile or NuspecProperties (including dynamically generated versions).

This PR also fixes an issue with the OutputFileNamesWithoutVersion property, ensuring Pack respects that setting when computing and emitting the final .nupkg file name so downstream targets receive the expected version‑less output.

PR Checklist

  • Meaningful title, helpful description and a linked NuGet/Home issue
  • Added tests
  • Link to an issue or pull request to update docs if this PR changes settings, environment variables, new feature, etc.

Note

One of this tests invoke msbuild to compare the file name computed by GetPackOutputItemsTask with the actual file produced by PackTask. As a result, the NuGet.Build.Tasks.Pack.Test project acquires a new dependency.
I’m not certain whether this test implementation and approach align with the repository’s conventions, so feedback or guidance would be appreciated, especially if anything is incorrect.

@gekka gekka requested a review from a team as a code owner February 14, 2026 03:10
@gekka gekka requested review from kartheekp-ms and zivkan February 14, 2026 03:10
@dotnet-policy-service dotnet-policy-service Bot added the Community PRs created by someone not in the NuGet team label Feb 14, 2026
The build error was caused by project dependencies defined in the solution file.
Dependencies were configured at the solution level, Azure Pipelines failed to build the affected project.
@zivkan
Copy link
Copy Markdown
Member

zivkan commented Feb 17, 2026

@gekka I'm conceptually fine with what this PR claims to fix. But I see the CI failures are related to changes made by this PR, so I'm not going to do a full review until we get a green CI.

No problem adding the project reference to the test project.

<GetPackOutputItemsTask
PackageOutputPath="$(PackageOutputAbsolutePath)"
NuspecOutputPath="$(NuspecOutputAbsolutePath)"
NuspecInputFilePath="$(NuspecFile)"
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

PackTask already takes a bunch of these properties as input.
I think for ease for maintenance, the property names should be the same.

So instead of NuspecInputFilePath, use NuspecFile.

Something else, we should use the NuspecFileAbsolutePath as https://github.com/NuGet/NuGet.Client/blob/3a2648cbab3528f4105e0dedbe60be26289379eb/src/NuGet.Core/NuGet.Build.Tasks/NuGet.Build.Tasks.Pack.targets#L265C29-L265C51 is using it.

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Change the property name from NuspecInputFilePath to NuspecFile

$(AbsolutePath) doesn’t exist when GetPackOutputItemsTask is called, I changed the logic to generate it from $(NuspecFile) and pass it in.

Ideally, for maintainability, PackTask should use the output filename produced by GetPackOutputItemsTask directly, which would avoid inconsistencies from generating filenames separately. However, that would require larger changes to PackTask, so this pull request applies the minimal fix.

@gekka gekka changed the title Fix mismatch between GetPackOutputItemsTask and PackTask generated filenames [WIP] Fix mismatch between GetPackOutputItemsTask and PackTask generated filenames Feb 20, 2026
@gekka gekka marked this pull request as draft February 20, 2026 11:46
@gekka
Copy link
Copy Markdown
Author

gekka commented Feb 22, 2026

This PR encountered a few failing tests. A portion of them were due to my code, but there were also intermittent failures that don’t seem to be related.
Since the final rerun passed without issues, it seems that some earlier failures were caused by test instability.

This flakiness timeout failures seem to be coming from NuGet.Protocol.Tests.PackageUpdateResourceTests class.

There’s an existing issue that appears to mention the same type of flakiness.
It’s not the exact same function, but it’s within the same part of the codebase and might be related.

@gekka gekka changed the title [WIP] Fix mismatch between GetPackOutputItemsTask and PackTask generated filenames Fix mismatch between GetPackOutputItemsTask and PackTask generated filenames Feb 22, 2026
@gekka gekka marked this pull request as ready for review February 22, 2026 01:57
@jeffkl jeffkl requested a review from nkolev92 February 24, 2026 23:59
@nkolev92 nkolev92 requested a review from Copilot February 25, 2026 23:30
Comment thread src/NuGet.Core/NuGet.Build.Tasks.Pack/GetPackOutputItemsTask.cs Outdated
Comment thread test/NuGet.Core.Tests/NuGet.Build.Tasks.Pack.Test/BuildFixture.cs Outdated
Comment thread test/NuGet.Core.Tests/NuGet.Build.Tasks.Pack.Test/PackageNameTests.cs Outdated
Copy link
Copy Markdown
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

This PR addresses inconsistencies in how pack output filenames are computed vs. produced when packing with a .nuspec and/or NuspecProperties, and ensures OutputFileNamesWithoutVersion is respected consistently so downstream MSBuild targets see the expected outputs.

Changes:

  • Update _GetOutputItemsFromPack to pass NuspecFile, NuspecProperties, and OutputFileNamesWithoutVersion into GetPackOutputItemsTask.
  • Align PackTaskLogic/GetPackOutputItemsTask handling of NuspecProperties (notably version) and apply excludeVersion when generating filenames.
  • Add new tests/fixtures (including an MSBuild invocation test) to validate that computed output item names match the files actually produced.

Reviewed changes

Copilot reviewed 6 out of 6 changed files in this pull request and generated 7 comments.

Show a summary per file
File Description
test/NuGet.Core.Tests/NuGet.Build.Tasks.Pack.Test/PackageNameTests.cs Adds theory-based coverage comparing GetPackOutputItemsTask output vs. produced pack artifacts.
test/NuGet.Core.Tests/NuGet.Build.Tasks.Pack.Test/NuGet.Build.Tasks.Pack.Test.csproj Adds a project reference needed for the new MSBuild-based test setup.
test/NuGet.Core.Tests/NuGet.Build.Tasks.Pack.Test/BuildFixture.cs Introduces a fixture to locate patched dotnet/MSBuild and built task/targets artifacts for integration-style tests.
src/NuGet.Core/NuGet.Build.Tasks/NuGet.Build.Tasks.Pack.targets Passes nuspec inputs and versionless-output option into _GetOutputItemsFromPack’s GetPackOutputItemsTask.
src/NuGet.Core/NuGet.Build.Tasks.Pack/PackTaskLogic.cs Factors nuspec property parsing into a helper and normalizes/validates version.
src/NuGet.Core/NuGet.Build.Tasks.Pack/GetPackOutputItemsTask.cs Uses nuspec inputs to compute output names consistently and respects OutputFileNamesWithoutVersion.

Comment thread src/NuGet.Core/NuGet.Build.Tasks.Pack/GetPackOutputItemsTask.cs Outdated
Comment thread test/NuGet.Core.Tests/NuGet.Build.Tasks.Pack.Test/PackageNameTests.cs Outdated
Comment thread test/NuGet.Core.Tests/NuGet.Build.Tasks.Pack.Test/PackageNameTests.cs Outdated
Comment thread test/NuGet.Core.Tests/NuGet.Build.Tasks.Pack.Test/PackageNameTests.cs Outdated
Comment thread test/NuGet.Core.Tests/NuGet.Build.Tasks.Pack.Test/BuildFixture.cs Outdated
Comment thread test/NuGet.Core.Tests/NuGet.Build.Tasks.Pack.Test/BuildFixture.cs Outdated
Comment thread test/NuGet.Core.Tests/NuGet.Build.Tasks.Pack.Test/BuildFixture.cs Outdated
@dotnet-policy-service dotnet-policy-service Bot added the Status:No recent activity PRs that have not had any recent activity and will be closed if the label is not removed label Mar 4, 2026
@dotnet-policy-service dotnet-policy-service Bot removed the Status:No recent activity PRs that have not had any recent activity and will be closed if the label is not removed label Mar 8, 2026
@jeffkl jeffkl assigned nkolev92 and unassigned zivkan Mar 17, 2026
@dotnet-policy-service dotnet-policy-service Bot added the Status:No recent activity PRs that have not had any recent activity and will be closed if the label is not removed label Mar 25, 2026
@dotnet-policy-service dotnet-policy-service Bot removed the Status:No recent activity PRs that have not had any recent activity and will be closed if the label is not removed label Mar 29, 2026
@dotnet-policy-service dotnet-policy-service Bot added the Status:No recent activity PRs that have not had any recent activity and will be closed if the label is not removed label Apr 5, 2026
@gekka
Copy link
Copy Markdown
Author

gekka commented Apr 29, 2026

@nkolev92
I’ve addressed all the review comments, and it has been idle for about a month.
When you have a moment, I’d appreciate another review. Thanks!

@dotnet-policy-service dotnet-policy-service Bot removed the Status:No recent activity PRs that have not had any recent activity and will be closed if the label is not removed label Apr 29, 2026
@dotnet-policy-service dotnet-policy-service Bot added the Status:No recent activity PRs that have not had any recent activity and will be closed if the label is not removed label May 6, 2026
@nkolev92 nkolev92 removed the Status:No recent activity PRs that have not had any recent activity and will be closed if the label is not removed label May 6, 2026
@nkolev92
Copy link
Copy Markdown
Member

nkolev92 commented May 6, 2026

Sorry for the delay @gekka.

I'll take another look.

@dotnet-policy-service dotnet-policy-service Bot added the Status:No recent activity PRs that have not had any recent activity and will be closed if the label is not removed label May 14, 2026
@nkolev92 nkolev92 removed the Status:No recent activity PRs that have not had any recent activity and will be closed if the label is not removed label May 14, 2026
nkolev92 and others added 3 commits May 15, 2026 16:04
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Copy link
Copy Markdown
Member

@nkolev92 nkolev92 left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Apologies for the delay.

I have some suggestions that improve the readiability of the test cases and simplify the actual bootstrapping logic.

{

//// without nuspec input
new PackageFileNameTestCase("000",["proj.1.9.0.nupkg" ], "proj", "nusp", "1.9", "", "", false),
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I appreciated the depth of a bunch of these tests.

They were a bit hard to read though, so I have a simplified version that I have on a separate branch. I didn't want to push it on your branch without permission.

Check-out https://github.com/NuGet/NuGet.Client/tree/gekka/fix-nuget-package-file-name.

Another important thing was we actually never really need the csproj, so a bunch of that code is simpler because of that.

There was a lot of good stuff in your tests though, such as case number, so I evolved that into a test case name, and effectively added a named parameter for everything.

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

One small concern is that applying the change as proposed may make the tests a bit harder to read in Visual Studio’s Test Explorer.
Because the name Scenario is sorted in ascending order, it gets pushed toward the end.
20260523

var csprojPath = Path.Combine(testDirectory, FILENAME_PROJECT_FILE);
var nuspecPath = Path.Combine(testDirectory, FILENAME_NUSPEC_FILE);

var csprojContent = $"""
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We never actually needed the csproj, so it can be removed.
It very much simplifies the code.

</PropertyGroup>

<PropertyGroup>
<DefineConstants>$(DefineConstants);TEST_FOR_UNIT</DefineConstants>
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We don't need this anymore either.

That simplification is part of my branch too.

[MemberData(nameof(PackageFileNameTestCases))]
public void GetPackOutputItemsTaskTests_Execute_CheckPackageFileName(PackageFileNameTestCase testCase)
{
var outputItemTask = new NuGet.Build.Tasks.Pack.GetPackOutputItemsTask();
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You probably don't need the namespace qualifier here.
There's a bunch of that used, but it's unnecessary.

<GetPackOutputItemsTask
PackageOutputPath="$(PackageOutputAbsolutePath)"
NuspecOutputPath="$(NuspecOutputAbsolutePath)"
NuspecFile="$(NuspecFileAbsolutePath)"
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

So _CleanPackageFiles dependeds on this target here.

This task now unconditionally opens the nuspec. When that happens, then we don't generate the pack output right?
I think this can in theory mess with clean, but not 100% sure.

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I agree that this behavior is not ideal.
The main purpose of _CleanPackageFiles is simply to prevent old output files from being left behind, but it isn’t strictly required.

If the version number is updated dynamically, the file names change as well, so this task cannot delete the old files in the first place.

}

var nuspecReader = new NuGet.Packaging.NuspecReader(source.NuspecFile);
if (!hasIdInNuspecProperties)
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This may be a regression.

In particular, I think nuspecProperties are supposed to be used for substitution but not authoritatively override.

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

PackTask and GetPackOutputItemsTask were producing different outputs, so this change updates GetPackOutputItemsTask to match file names from PackTask .
PackTask is the authoritative behavior and is too large to modify.
There is also an existing test that has meta name for id and version in nuspec file, and to keep that test unchanged while aligning the outputs.

The processing order in this PR is required.

[PlatformTheory(Platform.Windows)]
// Command line : /p:NuspecProperties=\"id=MyPackage;version=1.2.3;tags=tag1;description="hello world"\"
[InlineData("/p:NuspecProperties=\\\"id=MyPackage;version=1.2.3;tags=tag1;description=\"hello world\"\\\"", "MyPackage",
"1.2.3", "hello world", "tag1")]
// Command line : /p:NuspecProperties=\"id=MyPackage;version=1.2.3;tasg=tag1,tag2;description=""\"
[InlineData("/p:NuspecProperties=\\\"id=MyPackage;version=1.2.3;tags=tag1,tag2;description=\"hello world\"\\\"", "MyPackage",
"1.2.3", "hello world", "tag1,tag2")]
// Command line : /p:NuspecProperties=\"id=MyPackage;version=1.2.3;tags=;description="hello = world"\"
[InlineData("/p:NuspecProperties=\\\"id=MyPackage;version=1.2.3;tags=;description=\"hello = world\"\\\"", "MyPackage",
"1.2.3", "hello = world", "")]
// Command line : /p:NuspecProperties=\"id=MyPackage;version=1.2.3;tags="";description="hello = world with a %3B"\"
[InlineData("/p:NuspecProperties=\\\"id=MyPackage;version=1.2.3;tags=\"\";description=\"hello = world with a %3B\"\\\"",
"MyPackage", "1.2.3", "hello = world with a ;", "")]
public void PackCommand_PackProject_PacksFromNuspecWithTokenSubstitution(
string nuspecProperties,
string expectedId,
string expectedVersion,
string expectedDescription,
string expectedTags
)
{
var nuspecFileContent = @"<?xml version=""1.0""?>
<package>
<metadata>
<id>$id$</id>
<version>$version$</version>

@dotnet-policy-service dotnet-policy-service Bot added the Status:No recent activity PRs that have not had any recent activity and will be closed if the label is not removed label May 31, 2026
@dotnet-policy-service
Copy link
Copy Markdown
Contributor

This PR has been automatically marked as stale because it has no activity for 7 days. It will be closed if no further activity occurs within another 30 days of this comment. If it is closed, you may reopen it anytime when you're ready again, as long as you don't delete the branch.

@nkolev92 nkolev92 removed the Status:No recent activity PRs that have not had any recent activity and will be closed if the label is not removed label Jun 1, 2026
@nkolev92
Copy link
Copy Markdown
Member

nkolev92 commented Jun 1, 2026

I'll look again.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Community PRs created by someone not in the NuGet team

Projects

None yet

4 participants