Skip to content
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

Update PackageUpdateResource to receive bool to indicate if snupkg are enabled #6249

Merged
merged 9 commits into from
Feb 28, 2025
Merged
Show file tree
Hide file tree
Changes from 7 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
27 changes: 20 additions & 7 deletions src/NuGet.Core/NuGet.Commands/CommandRunners/PushRunner.cs
Original file line number Diff line number Diff line change
Expand Up @@ -49,19 +49,32 @@ public static async Task Run(
}

packageUpdateResource.Settings = settings;
bool allowSnupkg = false;
SymbolPackageUpdateResourceV3 symbolPackageUpdateResource = null;

// figure out from index.json if pushing snupkg is supported
var sourceUri = packageUpdateResource.SourceUri;
if (string.IsNullOrEmpty(symbolSource)
&& !noSymbols
var symbeSourceUri = symbolSource;

if (!string.IsNullOrEmpty(symbolSource) && !noSymbols)
{
//If the symbol source is set we try try to get the symbol package resource to determine if Snupkg are supported.
symbolPackageUpdateResource = await CommandRunnerUtility.GetSymbolPackageUpdateResource(sourceProvider, symbolSource, CancellationToken.None);
if (symbolPackageUpdateResource != null)
{
allowSnupkg = true;
symbeSourceUri = symbolPackageUpdateResource.SourceUri.AbsoluteUri;
}
}
else if (!noSymbols
&& !sourceUri.IsFile
&& sourceUri.IsAbsoluteUri)
{
symbolPackageUpdateResource = await CommandRunnerUtility.GetSymbolPackageUpdateResource(sourceProvider, source, CancellationToken.None);
if (symbolPackageUpdateResource != null)
{
symbolSource = symbolPackageUpdateResource.SourceUri.AbsoluteUri;
allowSnupkg = true;
symbolSource = symbeSourceUri = symbolPackageUpdateResource.SourceUri.AbsoluteUri;
}
}

Expand All @@ -71,7 +84,7 @@ public static async Task Run(
// Precedence for symbol package API key: -SymbolApiKey param, config, package API key (Only for symbol source from SymbolPackagePublish service)
if (!string.IsNullOrEmpty(symbolSource))
{
symbolApiKey ??= CommandRunnerUtility.GetApiKey(settings, symbolSource, symbolSource);
symbolApiKey ??= CommandRunnerUtility.GetApiKey(settings, symbeSourceUri, symbolSource);

// Only allow falling back to API key when the symbol source was obtained from SymbolPackagePublish service
if (symbolPackageUpdateResource != null)
Expand All @@ -80,16 +93,16 @@ public static async Task Run(
}
}

await packageUpdateResource.Push(
await packageUpdateResource.PushAsync(
packagePaths,
symbolSource,
symbeSourceUri,
timeoutSeconds,
disableBuffering,
_ => apiKey,
_ => symbolApiKey,
noServiceEndpoint,
skipDuplicate,
symbolPackageUpdateResource,
allowSnupkg,
packageSource.AllowInsecureConnections,
logger);
}
Expand Down
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
#nullable enable
~NuGet.Protocol.Core.Types.PackageUpdateResource.PushAsync(System.Collections.Generic.IList<string> packagePaths, string symbolSource, int timeoutInSecond, bool disableBuffering, System.Func<string, string> getApiKey, System.Func<string, string> getSymbolApiKey, bool noServiceEndpoint, bool skipDuplicate, bool allowSnupkg, bool allowInsecureConnections, NuGet.Common.ILogger log) -> System.Threading.Tasks.Task
~NuGet.Protocol.PackageVulnerabilityMetadata.PackageVulnerabilityMetadata(System.Uri advisoryUrl, int severity) -> void
~NuGet.Protocol.Plugins.PluginFile.PluginFile(string filePath, System.Lazy<NuGet.Protocol.Plugins.PluginFileState> state) -> void
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
#nullable enable
~NuGet.Protocol.Core.Types.PackageUpdateResource.PushAsync(System.Collections.Generic.IList<string> packagePaths, string symbolSource, int timeoutInSecond, bool disableBuffering, System.Func<string, string> getApiKey, System.Func<string, string> getSymbolApiKey, bool noServiceEndpoint, bool skipDuplicate, bool allowSnupkg, bool allowInsecureConnections, NuGet.Common.ILogger log) -> System.Threading.Tasks.Task
~NuGet.Protocol.PackageVulnerabilityMetadata.PackageVulnerabilityMetadata(System.Uri advisoryUrl, int severity) -> void
~NuGet.Protocol.Plugins.PluginFile.PluginFile(string filePath, System.Lazy<NuGet.Protocol.Plugins.PluginFileState> state) -> void
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
#nullable enable
~NuGet.Protocol.Core.Types.PackageUpdateResource.PushAsync(System.Collections.Generic.IList<string> packagePaths, string symbolSource, int timeoutInSecond, bool disableBuffering, System.Func<string, string> getApiKey, System.Func<string, string> getSymbolApiKey, bool noServiceEndpoint, bool skipDuplicate, bool allowSnupkg, bool allowInsecureConnections, NuGet.Common.ILogger log) -> System.Threading.Tasks.Task
~NuGet.Protocol.PackageVulnerabilityMetadata.PackageVulnerabilityMetadata(System.Uri advisoryUrl, int severity) -> void
~NuGet.Protocol.Plugins.PluginFile.PluginFile(string filePath, System.Lazy<NuGet.Protocol.Plugins.PluginFileState> state) -> void
36 changes: 25 additions & 11 deletions src/NuGet.Core/NuGet.Protocol/Resources/PackageUpdateResource.cs
Original file line number Diff line number Diff line change
Expand Up @@ -67,7 +67,7 @@ public async Task Push(
await Push(packagePaths, symbolSource, timeoutInSecond, disableBuffering, getApiKey, getSymbolApiKey, noServiceEndpoint, skipDuplicate, symbolPackageUpdateResource, allowInsecureConnections: false, log);
}

public async Task Push(
public async Task PushAsync(
IList<string> packagePaths,
string symbolSource, // empty to not push symbols
int timeoutInSecond,
Expand All @@ -76,7 +76,7 @@ public async Task Push(
Func<string, string> getSymbolApiKey,
bool noServiceEndpoint,
bool skipDuplicate,
SymbolPackageUpdateResourceV3 symbolPackageUpdateResource,
bool allowSnupkg,
bool allowInsecureConnections,
ILogger log)
{
Expand All @@ -94,7 +94,7 @@ public async Task Push(
{
// Push nupkgs and possibly the corresponding snupkgs.
await PushPackagePath(packagePath, _source, symbolSource, apiKey, getSymbolApiKey, noServiceEndpoint, skipDuplicate,
symbolPackageUpdateResource, allowInsecureConnections, requestTimeout, log, tokenSource.Token);
allowSnupkg, allowInsecureConnections, requestTimeout, log, tokenSource.Token);
}
else // Explicit snupkg push
{
Expand All @@ -106,13 +106,29 @@ await PushPackagePath(packagePath, _source, symbolSource, apiKey, getSymbolApiKe
string symbolApiKey = getSymbolApiKey(symbolSource);

await PushSymbolsPath(packagePath, symbolSource, symbolApiKey,
noServiceEndpoint, skipDuplicate, symbolPackageUpdateResource, allowInsecureConnections,
noServiceEndpoint, skipDuplicate, allowSnupkg, allowInsecureConnections,
requestTimeout, log, tokenSource.Token);
}
}
}
}

public async Task Push(
IList<string> packagePaths,
string symbolSource, // empty to not push symbols
int timeoutInSecond,
bool disableBuffering,
Func<string, string> getApiKey,
Func<string, string> getSymbolApiKey,
bool noServiceEndpoint,
bool skipDuplicate,
SymbolPackageUpdateResourceV3 symbolPackageUpdateResource,
bool allowInsecureConnections,
ILogger log)
{
await PushAsync(packagePaths, symbolSource, timeoutInSecond, disableBuffering, getApiKey, getSymbolApiKey, noServiceEndpoint, skipDuplicate, allowSnupkg: symbolPackageUpdateResource is not null, allowInsecureConnections, log);
}

[Obsolete("Use Push method which takes multiple package paths.")]
public Task Push(
string packagePath,
Expand Down Expand Up @@ -207,17 +223,16 @@ private async Task PushSymbolsPath(string packagePath,
string apiKey,
bool noServiceEndpoint,
bool skipDuplicate,
SymbolPackageUpdateResourceV3 symbolPackageUpdateResource,
bool allowSnupkg,
bool allowInsecureConnections,
TimeSpan requestTimeout,
ILogger log,
CancellationToken token)
{
bool isSymbolEndpointSnupkgCapable = symbolPackageUpdateResource != null;
// Get the symbol package for this package
string symbolPackagePath = GetSymbolsPath(packagePath, isSymbolEndpointSnupkgCapable);
string symbolPackagePath = GetSymbolsPath(packagePath, allowSnupkg);

IEnumerable<string> symbolsToPush = LocalFolderUtility.ResolvePackageFromPath(symbolPackagePath, isSnupkg: isSymbolEndpointSnupkgCapable);
IEnumerable<string> symbolsToPush = LocalFolderUtility.ResolvePackageFromPath(symbolPackagePath, isSnupkg: allowSnupkg);
bool symbolsPathResolved = symbolsToPush != null && symbolsToPush.Any();

//No files were resolved.
Expand Down Expand Up @@ -259,7 +274,7 @@ private async Task PushPackagePath(string packagePath,
Func<string, string> getSymbolApiKey,
bool noServiceEndpoint,
bool skipDuplicate,
SymbolPackageUpdateResourceV3 symbolPackageUpdateResource,
bool allowSnupkg,
bool allowInsecureConnections,
TimeSpan requestTimeout,
ILogger log,
Expand Down Expand Up @@ -290,8 +305,7 @@ private async Task PushPackagePath(string packagePath,
// Push corresponding symbols, if successful.
if (packageWasPushed && !string.IsNullOrEmpty(symbolSource))
{
bool isSymbolEndpointSnupkgCapable = symbolPackageUpdateResource != null;
string symbolPackagePath = GetSymbolsPath(nupkgToPush, isSnupkg: isSymbolEndpointSnupkgCapable);
string symbolPackagePath = GetSymbolsPath(nupkgToPush, isSnupkg: allowSnupkg);

// There may not be a snupkg with the same filename. Ignore it since this isn't an explicit snupkg push.
if (!File.Exists(symbolPackagePath))
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -561,6 +561,173 @@ public void PushCommand_Server_Nupkg_ByFilename_SnupkgExists_Conflict()
}
}

[Fact]
public void PushCommand_Server_Nupkg_ByWildcard_NupkgAndSnupkgPushed()
{
// Arrange
using (SimpleTestPathContext pathContext = new SimpleTestPathContext())
{
var nuget = Util.GetNuGetExePath();

string packageId = "packageWithSnupkg";

//Create a nupkg in test directory.
string version = "1.1.0";
Util.CreateTestPackage(packageId, version, pathContext.WorkingDirectory);
string nupkgFileName = Util.BuildPackageString(packageId, version, NuGetConstants.PackageExtension);
string snupkgFileName = Util.BuildPackageString(packageId, version, NuGetConstants.SnupkgExtension);
string snupkgFullPath = Path.Combine(pathContext.WorkingDirectory, snupkgFileName);
//Create snupkg in test directory.
WriteSnupkgFile(snupkgFullPath);

string wildcardPush = "*.nupkg";
var sourcePushUrl = string.Empty;

CommandRunnerResult result = null;

using (var server = CreateAndStartMockV3Server(pathContext.WorkingDirectory, out string sourceName))
{
sourcePushUrl = SetupMockServerAlwaysCreate(server);
pathContext.Settings.AddSource(sourceName, sourceName, "true");

// Act
//Since this is V3, this will trigger 2 pushes: one for nupkgs, and one for snupkgs.
result = CommandRunner.Run(
nuget,
pathContext.WorkingDirectory,
$"push {wildcardPush} -Source {sourceName} -Timeout 110",
timeOutInMilliseconds: 120000,
testOutputHelper: _testOutputHelper); // 120 seconds
}

// Assert
string genericFileNotFoundError = WITHOUT_FILENAME_MESSAGE_FILE_DOES_NOT_EXIST;
string pushingMessage = "Pushing {0} to '{1}'";

//Both should push, but to different servers
Assert.True(result.Success);
Assert.Contains(MESSAGE_PACKAGE_PUSHED, result.AllOutput); // files pushed
Assert.DoesNotContain(genericFileNotFoundError, result.Errors);
Assert.Contains(string.Format(pushingMessage, nupkgFileName, sourcePushUrl), result.AllOutput); //nupkg push to source
Assert.Contains(string.Format(pushingMessage, snupkgFileName, sourcePushUrl), result.AllOutput); //snupkg push to source
}
}

[Fact]
public void PushCommand_Server_Nupkg_ByWildcard_SeparateSymbolUrl_NupkgAndSnupkgPushedToDifferentSources()
{
// Arrange
using (SimpleTestPathContext pathContext = new SimpleTestPathContext())
{
var nuget = Util.GetNuGetExePath();

string packageId = "packageWithSnupkg";

//Create a nupkg in test directory.
string version = "1.1.0";
Util.CreateTestPackage(packageId, version, pathContext.WorkingDirectory);
string nupkgFileName = Util.BuildPackageString(packageId, version, NuGetConstants.PackageExtension);
string snupkgFileName = Util.BuildPackageString(packageId, version, NuGetConstants.SnupkgExtension);
string snupkgFullPath = Path.Combine(pathContext.WorkingDirectory, snupkgFileName);
//Create snupkg in test directory.
WriteSnupkgFile(snupkgFullPath);

string wildcardPush = "*.nupkg";
var sourcePushUrl = string.Empty;
var symbolPushUrl = string.Empty;

CommandRunnerResult result = null;

using (var server = CreateAndStartMockV3Server(pathContext.WorkingDirectory, out string sourceName))
using (var symbolServer = CreateAndStartMockV3Server(pathContext.WorkingDirectory, out string symbolSourceName))
{
sourcePushUrl = SetupMockServerAlwaysCreate(server);
symbolPushUrl = SetupMockServerAlwaysCreate(symbolServer);
pathContext.Settings.AddSource(sourceName, sourceName, "true");
pathContext.Settings.AddSource(symbolSourceName, symbolSourceName, "true");

// Act
//Since this is V3, this will trigger 2 pushes: one for nupkgs, and one for snupkgs.
result = CommandRunner.Run(
nuget,
pathContext.WorkingDirectory,
$"push {wildcardPush} -Source {sourceName} -SymbolSource {symbolSourceName} -Timeout 110",
timeOutInMilliseconds: 120000,
testOutputHelper: _testOutputHelper); // 120 seconds
}

// Assert
string genericFileNotFoundError = WITHOUT_FILENAME_MESSAGE_FILE_DOES_NOT_EXIST;
string pushingMessage = "Pushing {0} to '{1}'";

//Both should push, but to different servers
Assert.True(result.Success);
Assert.Contains(MESSAGE_PACKAGE_PUSHED, result.AllOutput); // files pushed
Assert.DoesNotContain(genericFileNotFoundError, result.Errors);
Assert.Contains(string.Format(pushingMessage, nupkgFileName, sourcePushUrl), result.AllOutput); //nupkg push to source
Assert.Contains(string.Format(pushingMessage, snupkgFileName, symbolPushUrl), result.AllOutput); //snupkg push to symbol source
}
}

/// <summary>
/// When pushing *.Nupkg, (no skip duplicate) a 409 Conflict is returned and halts the secondary symbols push.
/// </summary>
[Fact]
public void PushCommand_Server_Nupkg_SeparateSymbolUrl_NoSymbolTrue_SnupkgNotPushed()
{
// Arrange
using (SimpleTestPathContext pathContext = new SimpleTestPathContext())
{
var nuget = Util.GetNuGetExePath();

string packageId = "packageWithSnupkg";

//Create a nupkg in test directory.
string version = "1.1.0";
Util.CreateTestPackage(packageId, version, pathContext.WorkingDirectory);
string nupkgFileName = Util.BuildPackageString(packageId, version, NuGetConstants.PackageExtension);
string snupkgFileName = Util.BuildPackageString(packageId, version, NuGetConstants.SnupkgExtension);
string snupkgFullPath = Path.Combine(pathContext.WorkingDirectory, snupkgFileName);
//Create snupkg in test directory.
WriteSnupkgFile(snupkgFullPath);

string wildcardPush = "*.nupkg";
var sourcePushUrl = string.Empty;
var symbolPushUrl = string.Empty;

CommandRunnerResult result = null;

using (var server = CreateAndStartMockV3Server(pathContext.WorkingDirectory, out string sourceName))
using (var symbolServer = CreateAndStartMockV3Server(pathContext.WorkingDirectory, out string symbolSourceName))
{
sourcePushUrl = SetupMockServerAlwaysCreate(server);
symbolPushUrl = SetupMockServerAlwaysCreate(symbolServer);
pathContext.Settings.AddSource(sourceName, sourceName, "true");
pathContext.Settings.AddSource(symbolSourceName, symbolSourceName, "true");

// Act
//Since this is V3, this will trigger 2 pushes: one for nupkgs, and one for snupkgs.
result = CommandRunner.Run(
nuget,
pathContext.WorkingDirectory,
$"push {wildcardPush} -Source {sourceName} -SymbolSource {symbolPushUrl} -noSymbol -Timeout 110",
timeOutInMilliseconds: 120000,
testOutputHelper: _testOutputHelper); // 120 seconds
}

// Assert
string genericFileNotFoundError = WITHOUT_FILENAME_MESSAGE_FILE_DOES_NOT_EXIST;
string pushingMessage = "Pushing {0} to '{1}'";

//Both should push, but to different servers
Assert.True(result.Success);
Assert.Contains(MESSAGE_PACKAGE_PUSHED, result.AllOutput); // files pushed
Assert.DoesNotContain(genericFileNotFoundError, result.Errors);
Assert.Contains(string.Format(pushingMessage, nupkgFileName, sourcePushUrl), result.AllOutput); //nupkg push to source
Assert.DoesNotContain(".snupkg", result.AllOutput); //snupkg not pushed
}
}

/// <summary>
/// When pushing *.Nupkg, (no skip duplicate) a 409 Conflict is returned and halts the secondary symbols push.
/// </summary>
Expand Down Expand Up @@ -918,12 +1085,13 @@ private static void SetupMockServerAlwaysDuplicate(MockServer server)
}));
}

private static void SetupMockServerAlwaysCreate(MockServer server)
private static string SetupMockServerAlwaysCreate(MockServer server)
{
server.Put.Add("/push", (Func<HttpListenerRequest, object>)((r) =>
{
return HttpStatusCode.Created;
}));
return server.Uri + "push";
}


Expand Down
Loading