-
Notifications
You must be signed in to change notification settings - Fork 748
Fix mismatch between GetPackOutputItemsTask and PackTask generated filenames #7137
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
base: dev
Are you sure you want to change the base?
Changes from 10 commits
0343ad1
c1d3899
a3031e3
6af6b22
1fa323c
684f75d
463a6db
dee289b
b149c8d
5e4993d
3408ffc
9cb19ec
10761cc
0d95d28
2ffb4a8
6642de9
100e6b6
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -29,12 +29,18 @@ public class GetPackOutputItemsTask : Microsoft.Build.Utilities.Task | |
| [Required] | ||
| public string NuspecOutputPath { get; set; } | ||
|
|
||
| public string NuspecFile { get; set; } | ||
|
|
||
| public string[] NuspecProperties { get; set; } | ||
|
|
||
| public bool IncludeSymbols { get; set; } | ||
|
|
||
| public bool IncludeSource { get; set; } | ||
|
|
||
| public string SymbolPackageFormat { get; set; } | ||
|
|
||
| public bool OutputFileNamesWithoutVersion { get; set; } | ||
|
|
||
| /// <summary> | ||
| /// Output items | ||
| /// </summary> | ||
|
|
@@ -43,27 +49,56 @@ public class GetPackOutputItemsTask : Microsoft.Build.Utilities.Task | |
|
|
||
| public override bool Execute() | ||
| { | ||
| NuGetVersion version; | ||
| if (!NuGetVersion.TryParse(PackageVersion, out version)) | ||
| var packageId = PackageId; | ||
| var packageVersion = PackageVersion; | ||
|
|
||
| if (!string.IsNullOrWhiteSpace(NuspecFile)) | ||
| { | ||
| bool hasVersionInNuspecProperties = false; | ||
| if (NuspecProperties != null && NuspecProperties.Length > 0) | ||
| { | ||
| PackArgs packArgs = new PackArgs(); | ||
| PackTaskLogic.SetPackArgsPropertiesFromNuspecProperties(packArgs, MSBuildStringUtility.TrimAndExcludeNullOrEmpty(NuspecProperties)); | ||
| if (packArgs.Properties.ContainsKey("version")) | ||
| { | ||
| packageVersion = packArgs.Version; | ||
| hasVersionInNuspecProperties = true; | ||
| } | ||
| if (packArgs.Properties.TryGetValue("id", out var idTemp)) | ||
| { | ||
| packageId = idTemp; | ||
| } | ||
| } | ||
|
|
||
| var nuspecReader = new NuGet.Packaging.NuspecReader(NuspecFile); | ||
| packageId = nuspecReader.GetId(); | ||
| if (!hasVersionInNuspecProperties) | ||
|
||
| { | ||
| packageVersion = nuspecReader.GetVersion().ToNormalizedString(); | ||
| } | ||
| } | ||
|
|
||
| if (!NuGetVersion.TryParse(packageVersion, out var versionTemp)) | ||
| { | ||
| throw new ArgumentException(string.Format( | ||
| CultureInfo.CurrentCulture, | ||
| Strings.InvalidPackageVersion, | ||
| PackageVersion)); | ||
| packageVersion)); | ||
| } | ||
| NuGetVersion version = versionTemp!; | ||
|
|
||
| var symbolPackageFormat = PackArgs.GetSymbolPackageFormat(MSBuildStringUtility.TrimAndGetNullForEmpty(SymbolPackageFormat)); | ||
| var nupkgFileName = PackCommandRunner.GetOutputFileName(PackageId, version, isNupkg: true, symbols: false, symbolPackageFormat: symbolPackageFormat); | ||
| var nuspecFileName = PackCommandRunner.GetOutputFileName(PackageId, version, isNupkg: false, symbols: false, symbolPackageFormat: symbolPackageFormat); | ||
| var nupkgFileName = PackCommandRunner.GetOutputFileName(packageId, version!, isNupkg: true, symbols: false, symbolPackageFormat: symbolPackageFormat, excludeVersion: OutputFileNamesWithoutVersion); | ||
| var nuspecFileName = PackCommandRunner.GetOutputFileName(packageId, version!, isNupkg: false, symbols: false, symbolPackageFormat: symbolPackageFormat, excludeVersion: OutputFileNamesWithoutVersion); | ||
|
|
||
| var outputs = new List<ITaskItem>(); | ||
| outputs.Add(new TaskItem(Path.Combine(PackageOutputPath, nupkgFileName))); | ||
| outputs.Add(new TaskItem(Path.Combine(NuspecOutputPath, nuspecFileName))); | ||
|
|
||
| if (IncludeSource || IncludeSymbols) | ||
| { | ||
| var nupkgSymbolsFileName = PackCommandRunner.GetOutputFileName(PackageId, version, isNupkg: true, symbols: true, symbolPackageFormat: symbolPackageFormat); | ||
| var nuspecSymbolsFileName = PackCommandRunner.GetOutputFileName(PackageId, version, isNupkg: false, symbols: true, symbolPackageFormat: symbolPackageFormat); | ||
| var nupkgSymbolsFileName = PackCommandRunner.GetOutputFileName(packageId, version, isNupkg: true, symbols: true, symbolPackageFormat: symbolPackageFormat, excludeVersion: OutputFileNamesWithoutVersion); | ||
| var nuspecSymbolsFileName = PackCommandRunner.GetOutputFileName(packageId, version, isNupkg: false, symbols: true, symbolPackageFormat: symbolPackageFormat, excludeVersion: OutputFileNamesWithoutVersion); | ||
|
|
||
| outputs.Add(new TaskItem(Path.Combine(PackageOutputPath, nupkgSymbolsFileName))); | ||
| outputs.Add(new TaskItem(Path.Combine(NuspecOutputPath, nuspecSymbolsFileName))); | ||
|
|
||
| Original file line number | Diff line number | Diff line change | ||||||||||||||||||||||||||||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| @@ -0,0 +1,146 @@ | ||||||||||||||||||||||||||||||||||||
| // Copyright (c) .NET Foundation. All rights reserved. | ||||||||||||||||||||||||||||||||||||
| // Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. | ||||||||||||||||||||||||||||||||||||
| #nullable enable | ||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||
| using System; | ||||||||||||||||||||||||||||||||||||
| using System.Collections.Generic; | ||||||||||||||||||||||||||||||||||||
| using System.IO; | ||||||||||||||||||||||||||||||||||||
| using System.Linq; | ||||||||||||||||||||||||||||||||||||
| using Microsoft.Internal.NuGet.Testing.SignedPackages.ChildProcess; | ||||||||||||||||||||||||||||||||||||
| using NuGet.Frameworks; | ||||||||||||||||||||||||||||||||||||
| using NuGet.Test.Utility; | ||||||||||||||||||||||||||||||||||||
| using Xunit; | ||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||
| namespace NuGet.Build.Tasks.Pack.Test | ||||||||||||||||||||||||||||||||||||
| { | ||||||||||||||||||||||||||||||||||||
| [CollectionDefinition(Name)] | ||||||||||||||||||||||||||||||||||||
| public class FixtureCollection | ||||||||||||||||||||||||||||||||||||
| : ICollectionFixture<BuildFixture> | ||||||||||||||||||||||||||||||||||||
| { | ||||||||||||||||||||||||||||||||||||
| internal const string Name = "Build Tests"; | ||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||
| public class BuildFixture : IDisposable | ||||||||||||||||||||||||||||||||||||
| { | ||||||||||||||||||||||||||||||||||||
| #if DEBUG | ||||||||||||||||||||||||||||||||||||
| const string CONFIGURATION = "Debug"; | ||||||||||||||||||||||||||||||||||||
| #else | ||||||||||||||||||||||||||||||||||||
| const string CONFIGURATION = "Release"; | ||||||||||||||||||||||||||||||||||||
| #endif | ||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||
| const string FILENAME_DLL = "NuGet.Build.Tasks.Pack.dll"; | ||||||||||||||||||||||||||||||||||||
|
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Wouldn't be easier to add these tests into DotnetIntegrationTests instead of creating a whole fixture that does the same logic.
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Thanks for pointing that out. Moving this to an integration test would introduce a larger set of changes and dificulty for me. If PackTask is updated in the future to expose the output file name directly,
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. So there's other tests that can invoke tasks: What makes this one different and incompatible with that?
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. As explained in this issue, NuGet.Build.Tasks.Pack.PackTask does not reveal the final output filename unless Execute is actually called. I could not find any existing tests that execute PackTask.Execute, and it appears that no one has attempted to test this area before. In the end, I had to go through the parameters one by one to find a combination that would not produce errors, and then rewrite the test to call the function directly. |
||||||||||||||||||||||||||||||||||||
| const string FILENAME_TARGETS = "NuGet.Build.Tasks.Pack.targets"; | ||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||
| internal readonly bool _isDotNetFramework = false; | ||||||||||||||||||||||||||||||||||||
| internal readonly string _testFrameworkMoniker = "netstandard2.0"; | ||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||
| #if IS_DESKTOP | ||||||||||||||||||||||||||||||||||||
| private const string SdkVersion = "10"; | ||||||||||||||||||||||||||||||||||||
| private const string SdkTfm = "net10.0"; | ||||||||||||||||||||||||||||||||||||
| #endif | ||||||||||||||||||||||||||||||||||||
| internal readonly string _pathDotnetExe; | ||||||||||||||||||||||||||||||||||||
| internal readonly string _pathMSBuildExe; | ||||||||||||||||||||||||||||||||||||
| internal readonly string _pathDllFile; | ||||||||||||||||||||||||||||||||||||
| internal readonly string _pathTargetsFile; | ||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||
| internal readonly IReadOnlyDictionary<string, string> _dotnetEnvironments; | ||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||
| public BuildFixture() | ||||||||||||||||||||||||||||||||||||
| { | ||||||||||||||||||||||||||||||||||||
| _pathDotnetExe = NuGet.Test.Utility.TestFileSystemUtility.GetDotnetCli(); | ||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||
| #if IS_DESKTOP | ||||||||||||||||||||||||||||||||||||
| var _cliDirectory = TestDotnetCLiUtility.CopyAndPatchLatestDotnetCli(SdkVersion, SdkTfm); | ||||||||||||||||||||||||||||||||||||
| #else | ||||||||||||||||||||||||||||||||||||
| string testAssemblyPath = Path.GetFullPath(System.Reflection.Assembly.GetExecutingAssembly().Location); | ||||||||||||||||||||||||||||||||||||
| var _cliDirectory = TestDotnetCLiUtility.CopyAndPatchLatestDotnetCli(testAssemblyPath); | ||||||||||||||||||||||||||||||||||||
| #endif | ||||||||||||||||||||||||||||||||||||
| var dotnetExecutableName = NuGet.Common.RuntimeEnvironmentHelper.IsWindows ? "dotnet.exe" : "dotnet"; | ||||||||||||||||||||||||||||||||||||
| _pathDotnetExe = Path.Combine(_cliDirectory, dotnetExecutableName); | ||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||
| var sdkPath = Directory.EnumerateDirectories(Path.Combine(_cliDirectory, "sdk")) | ||||||||||||||||||||||||||||||||||||
| .Single(d => !string.Equals(Path.GetFileName(d), "NuGetFallbackFolder", StringComparison.OrdinalIgnoreCase)); | ||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||
| _pathMSBuildExe = GetMsBuildExePath(); | ||||||||||||||||||||||||||||||||||||
| _testFrameworkMoniker = GetFrameworkMoniker(typeof(NuGet.Build.Tasks.Pack.GetPackOutputItemsTask), out var isDotNetFramework); | ||||||||||||||||||||||||||||||||||||
| _isDotNetFramework = isDotNetFramework; | ||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||
| var artifactsDirectory = NuGet.Test.Utility.TestFileSystemUtility.GetArtifactsDirectoryInRepo(); | ||||||||||||||||||||||||||||||||||||
| var dllLocation = typeof(NuGet.Build.Tasks.Pack.GetPackOutputItemsTask).Assembly.Location; | ||||||||||||||||||||||||||||||||||||
|
||||||||||||||||||||||||||||||||||||
| var dllLocation = typeof(NuGet.Build.Tasks.Pack.GetPackOutputItemsTask).Assembly.Location; |
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 variable is used by next code
Copilot
AI
Feb 25, 2026
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 if (!Directory.Exists(dllDirectory)) block reassigns dllDirectory to the exact same value, so it has no effect and makes the path resolution logic harder to follow. Either remove the block, or update it to the intended fallback path (if a different directory was meant here).
| if (!System.IO.Directory.Exists(dllDirectory)) | |
| { | |
| dllDirectory = Path.Combine(artifactsDirectory, "NuGet.Build.Tasks.Pack", "bin", CONFIGURATION, _testFrameworkMoniker); | |
| } |
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.
Fix the issue where the same code was executed twice by correcting the first call's arguments.
Copilot
AI
Feb 25, 2026
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.
GetMsBuildExePath always tries to execute vswhere.exe without checking that the file exists. If VS isn't installed on the test machine, CommandRunner.Run will throw (ProcessStartInfo can't start a missing executable) and the fixture will fail before falling back to the .NET Framework MSBuild path. Guard with File.Exists(vswhereexe) (and only run vswhere when present), otherwise keep the fallback path.
| var runresult = CommandRunner.Run( | |
| vswhereexe, | |
| System.Environment.CurrentDirectory, | |
| @" -latest -find MSBuild\**\Bin\MSBuild.exe"); | |
| if (runresult.Success) | |
| { | |
| msbuildexe = new System.IO.StringReader(runresult.Output).ReadLine() ?? ""; | |
| if (File.Exists(vswhereexe)) | |
| { | |
| var runresult = CommandRunner.Run( | |
| vswhereexe, | |
| System.Environment.CurrentDirectory, | |
| @" -latest -find MSBuild\**\Bin\MSBuild.exe"); | |
| if (runresult.Success) | |
| { | |
| msbuildexe = new System.IO.StringReader(runresult.Output).ReadLine() ?? ""; | |
| } |
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.
Add assert
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.
Given the method above only sets it if the version exists, do we even need this check?
is a null check for packargs.version enough?
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 key‑existence check is necessary.
If we rely only on checking whether the value is non‑null, there is a possibility that a value from another key ends up changing the version due to the implementation.