From f29e88b06aa01a829173021fa14d9dd86ef8fb0a Mon Sep 17 00:00:00 2001 From: Nigusu Yenework Date: Fri, 29 Aug 2025 16:36:54 -0700 Subject: [PATCH 01/15] start --- .../PackageDownload/PackageDownloadArgs.cs | 66 ++++ .../PackageDownload/PackageDownloadCommand.cs | 132 ++++++++ .../PackageDownload/PackageDownloadLogger.cs | 135 ++++++++ .../PackageDownload/PackageDownloadRunner.cs | 293 +++++++++++++++++ .../NuGet.CommandLine.XPlat.csproj | 1 + .../NuGet.CommandLine.XPlat/Program.cs | 3 +- .../Strings.Designer.cs | 143 +++++++++ .../NuGet.CommandLine.XPlat/Strings.resx | 61 ++++ .../xlf/Strings.cs.xlf | 87 ++++++ .../xlf/Strings.de.xlf | 87 ++++++ .../xlf/Strings.es.xlf | 87 ++++++ .../xlf/Strings.fr.xlf | 87 ++++++ .../xlf/Strings.it.xlf | 87 ++++++ .../xlf/Strings.ja.xlf | 87 ++++++ .../xlf/Strings.ko.xlf | 87 ++++++ .../xlf/Strings.pl.xlf | 87 ++++++ .../xlf/Strings.pt-BR.xlf | 87 ++++++ .../xlf/Strings.ru.xlf | 87 ++++++ .../xlf/Strings.tr.xlf | 87 ++++++ .../xlf/Strings.zh-Hans.xlf | 87 ++++++ .../xlf/Strings.zh-Hant.xlf | 87 ++++++ .../PackageDownloadRunnerTests.cs | 294 ++++++++++++++++++ 22 files changed, 2258 insertions(+), 1 deletion(-) create mode 100644 src/NuGet.Core/NuGet.CommandLine.XPlat/Commands/PackageDownload/PackageDownloadArgs.cs create mode 100644 src/NuGet.Core/NuGet.CommandLine.XPlat/Commands/PackageDownload/PackageDownloadCommand.cs create mode 100644 src/NuGet.Core/NuGet.CommandLine.XPlat/Commands/PackageDownload/PackageDownloadLogger.cs create mode 100644 src/NuGet.Core/NuGet.CommandLine.XPlat/Commands/PackageDownload/PackageDownloadRunner.cs create mode 100644 test/NuGet.Core.Tests/NuGet.CommandLine.Xplat.Tests/PackageDownloadRunnerTests.cs diff --git a/src/NuGet.Core/NuGet.CommandLine.XPlat/Commands/PackageDownload/PackageDownloadArgs.cs b/src/NuGet.Core/NuGet.CommandLine.XPlat/Commands/PackageDownload/PackageDownloadArgs.cs new file mode 100644 index 00000000000..f005286b5cc --- /dev/null +++ b/src/NuGet.Core/NuGet.CommandLine.XPlat/Commands/PackageDownload/PackageDownloadArgs.cs @@ -0,0 +1,66 @@ +// 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. + +using System; +using System.Collections.Generic; +using System.Globalization; +using NuGet.Common; + +namespace NuGet.CommandLine.XPlat.Commands.PackageDownload; + +internal class PackageDownloadArgs +{ + public PackageDownloadArgs(string packageId, IList sources, string outputDirectory, ILogger logger) + { + PackageId = packageId; + Sources = sources; + OutputDirectory = outputDirectory; + Logger = logger; + } + + public string PackageId { get; set; } + public string Version { get; set; } + public IList Sources { get; set; } + public string OutputDirectory { get; set; } + public string ConfigFile { get; set; } + + public bool IncludePrerelease { get; set; } + public bool DownloadOnly { get; set; } + public bool AllowInsecureConnections { get; set; } + public bool Interactive { get; set; } + + private Verbosity _verbosity = Verbosity.Normal; + public Verbosity Verbosity + { + get => _verbosity; + set => _verbosity = value; + } + + public ILogger Logger { get; set; } + + public void SetVerbosity(string level) + { + if (string.IsNullOrWhiteSpace(level)) + { + _verbosity = Verbosity.Normal; + return; + } + + switch (level.Trim().ToLowerInvariant()) + { + case "quiet": + _verbosity = Verbosity.Quiet; + break; + case "normal": + _verbosity = Verbosity.Normal; + break; + case "detailed": + _verbosity = Verbosity.Detailed; + break; + default: + throw new ArgumentException(string.Format(CultureInfo.CurrentCulture, Strings.Error_InvalidVerbosity, level)); + } + } +} + +internal enum Verbosity { Quiet, Normal, Detailed } diff --git a/src/NuGet.Core/NuGet.CommandLine.XPlat/Commands/PackageDownload/PackageDownloadCommand.cs b/src/NuGet.Core/NuGet.CommandLine.XPlat/Commands/PackageDownload/PackageDownloadCommand.cs new file mode 100644 index 00000000000..0969cf218a1 --- /dev/null +++ b/src/NuGet.Core/NuGet.CommandLine.XPlat/Commands/PackageDownload/PackageDownloadCommand.cs @@ -0,0 +1,132 @@ +// 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. + +using System; +using System.Collections.Generic; +using System.CommandLine; +using System.CommandLine.Help; +using NuGet.CommandLine.XPlat.Commands; +using NuGet.CommandLine.XPlat.Commands.PackageDownload; + +namespace NuGet.CommandLine.XPlat +{ + internal class PackageDownloadCommand + { + public static void Register(Command rootCommand, Func getLogger) + { + var downloadCommand = new DocumentedCommand( + "download", + Strings.pkgDownload_descritpion, + "https://aka.ms/dotnet/package/download"); + + // Arguments + var packageId = new Argument("PackageId") + { + Description = Strings.pkgDownload_packageIdDescription, + Arity = ArgumentArity.ExactlyOne, + }; + + // Options + var allowInsecureConnections = new Option("--allow-insecure-connections") + { + Description = Strings.pkgDownload_AllowInsecureConnectionsDescritption, + Arity = ArgumentArity.Zero + }; + + var configFile = new Option("--configfile") + { + Description = Strings.pkgDownload_configFileDesciption, + Arity = ArgumentArity.ExactlyOne + }; + + var downloadOnly = new Option("--download-only") + { + Description = Strings.pkgDownload_downloadOnlyDeciption, + Arity = ArgumentArity.Zero + }; + + var help = new HelpOption() + { + Arity = ArgumentArity.Zero + }; + + var interactive = new Option("--interactive") + { + Description = Strings.pkgDownload_interactiveDecription, + Arity = ArgumentArity.Zero + }; + + var outputDirectory = new Option("--output-directory") + { + Description = Strings.pkgDownload_OutputDirectoryDescription, + Arity = ArgumentArity.ExactlyOne + }; + + var prerelease = new Option("--prerelease") + { + Description = Strings.pkgDownload_prereleaseDescription, + Arity = ArgumentArity.Zero + }; + + var sources = new Option>("--source") + { + Description = Strings.pkgDownload_sourcesDescription, + Arity = ArgumentArity.OneOrMore + }; + + var verbosity = new Option("--verbosity") + { + Description = Strings.pkgDownload_verbosityDescription, + Arity = ArgumentArity.ExactlyOne + }; + + var version = new Option("--version") + { + Description = Strings.pkgDownload_versionDescription, + Arity = ArgumentArity.ExactlyOne + }; + + + downloadCommand.Arguments.Add(packageId); + downloadCommand.Options.Add(allowInsecureConnections); + downloadCommand.Options.Add(configFile); + downloadCommand.Options.Add(downloadOnly); + downloadCommand.Options.Add(help); + downloadCommand.Options.Add(interactive); + downloadCommand.Options.Add(outputDirectory); + downloadCommand.Options.Add(prerelease); + downloadCommand.Options.Add(sources); + downloadCommand.Options.Add(verbosity); + downloadCommand.Options.Add(version); + + downloadCommand.SetAction(async (parserResult, cancellationToken) => + { + ILoggerWithColor logger = getLogger(); + + try + { + var args = new PackageDownloadArgs(parserResult.GetValue(packageId), parserResult.GetValue(sources), parserResult.GetValue(outputDirectory), logger) + { + Version = parserResult.GetValue(version), + IncludePrerelease = parserResult.GetValue(prerelease), + DownloadOnly = parserResult.GetValue(downloadOnly), + AllowInsecureConnections = parserResult.GetValue(allowInsecureConnections), + Interactive = parserResult.GetValue(interactive), + ConfigFile = parserResult.GetValue(configFile) + }; + + args.SetVerbosity(parserResult.GetValue(verbosity)); + + return await PackageDownloadRunner.RunAsync(args, cancellationToken); + } + catch (ArgumentException ex) + { + logger.LogError(ex.Message); + return ExitCodes.InvalidArguments; + } + }); + + rootCommand.Subcommands.Add(downloadCommand); + } + } +} diff --git a/src/NuGet.Core/NuGet.CommandLine.XPlat/Commands/PackageDownload/PackageDownloadLogger.cs b/src/NuGet.Core/NuGet.CommandLine.XPlat/Commands/PackageDownload/PackageDownloadLogger.cs new file mode 100644 index 00000000000..d832b1c0132 --- /dev/null +++ b/src/NuGet.Core/NuGet.CommandLine.XPlat/Commands/PackageDownload/PackageDownloadLogger.cs @@ -0,0 +1,135 @@ +// 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. + +using System; +using System.Threading.Tasks; +using NuGet.Common; + +namespace NuGet.CommandLine.XPlat.Commands.PackageDownload +{ + internal sealed class PackageDownloadLogger : ILogger + { + private readonly ILogger _primary; + private readonly Verbosity _verbosity; + + public PackageDownloadLogger( + ILogger primary, + Verbosity verbosity) + { + _primary = primary ?? throw new ArgumentNullException(nameof(primary)); + _verbosity = verbosity; + } + + public void LogDebug(string data) + { + if (_verbosity == Verbosity.Detailed) + { + _primary.LogDebug(data); + } + } + public void LogVerbose(string data) + { + if (_verbosity == Verbosity.Detailed) + { + _primary.LogVerbose(data); + } + } + public void LogInformation(string data) + { + if (_verbosity == Verbosity.Detailed) + { + _primary.LogInformation(data); + } + } + public void LogMinimal(string data) + { + if (_verbosity != Verbosity.Quiet) + { + _primary.LogMinimal(data); + } + } + public void LogWarning(string data) + { + if (_verbosity != Verbosity.Quiet) + { + _primary.LogWarning(data); + } + } + public void LogError(string data) + { + _primary.LogError(data); + } + + public void LogInformationSummary(string data) + { + if (_verbosity == Verbosity.Detailed) + { + _primary.LogInformationSummary(data); + } + } + + public void Log(LogLevel level, string data) + { + if (_verbosity == Verbosity.Quiet) + { + if (level == LogLevel.Error) + { + _primary.Log(level, data); + } + } + else if (_verbosity == Verbosity.Normal) + { + if (level == LogLevel.Error + || level == LogLevel.Warning + || level == LogLevel.Minimal + || level == LogLevel.Information) + { + _primary.Log(level, data); + } + } + else + { + _primary.Log(level, data); + } + } + public Task LogAsync(LogLevel level, string data) + { + if (_verbosity == Verbosity.Quiet) + { + if (level == LogLevel.Error) + { + return _primary.LogAsync(level, data); + } + + return Task.CompletedTask; + } + else if (_verbosity == Verbosity.Normal) + { + if (level == LogLevel.Error + || level == LogLevel.Warning + || level == LogLevel.Minimal + || level == LogLevel.Information) + { + return _primary.LogAsync(level, data); + } + + return Task.CompletedTask; + } + else + { + return _primary.LogAsync(level, data); + } + } + + public void Log(ILogMessage message) + { + Log(message.Level, message.Message); + } + + public Task LogAsync(ILogMessage message) + { + return LogAsync(message.Level, message.Message); + } + } +} + diff --git a/src/NuGet.Core/NuGet.CommandLine.XPlat/Commands/PackageDownload/PackageDownloadRunner.cs b/src/NuGet.Core/NuGet.CommandLine.XPlat/Commands/PackageDownload/PackageDownloadRunner.cs new file mode 100644 index 00000000000..f120503f16b --- /dev/null +++ b/src/NuGet.Core/NuGet.CommandLine.XPlat/Commands/PackageDownload/PackageDownloadRunner.cs @@ -0,0 +1,293 @@ +// 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. + +using System; +using System.Collections.Generic; +using System.Globalization; +using System.IO; +using System.Linq; +using System.Threading; +using System.Threading.Tasks; +using NuGet.Commands; +using NuGet.Configuration; +using NuGet.Credentials; +using NuGet.Packaging; +using NuGet.Packaging.Core; +using NuGet.Packaging.PackageExtraction; +using NuGet.Packaging.Signing; +using NuGet.Protocol; +using NuGet.Protocol.Core.Types; +using NuGet.Repositories; +using NuGet.Versioning; + +namespace NuGet.CommandLine.XPlat.Commands.PackageDownload +{ + internal static class PackageDownloadRunner + { + public static async Task RunAsync(PackageDownloadArgs args, CancellationToken token) + { + // Custom logger for package downloads. + // It wraps the provided logger and uses verbosity levels to determine which messages to log. + var logger = new PackageDownloadLogger(args.Logger, args.Verbosity); + + DefaultCredentialServiceUtility.SetupDefaultCredentialService(logger, !args.Interactive); + ISettings settings = Settings.LoadDefaultSettings( + Directory.GetCurrentDirectory(), + args.ConfigFile, + new XPlatMachineWideSetting()); + PackageSourceProvider sourceProvider = new PackageSourceProvider(settings); + var packageSources = GetPackageSources(args.Sources, sourceProvider); + + // Check for insecure sources + if (!args.AllowInsecureConnections) + { + var insecureSources = packageSources.Where(source => source.SourceUri.AbsoluteUri.StartsWith("http://", StringComparison.OrdinalIgnoreCase)); + + if (insecureSources.Any()) + { + if (insecureSources.Count() == 1) + { + logger.LogError(string.Format( + CultureInfo.CurrentCulture, + Strings.Error_HttpServerUsage, + "download", + insecureSources.First())); + } + else + { + logger.LogError(string.Format( + CultureInfo.CurrentCulture, + Strings.Error_HttpServerUsage_MultipleSources, + "download", + string.Join(", ", insecureSources))); + } + + return ExitCodes.Error; + } + } + + var cache = new SourceCacheContext() + { + NoCache = true, + DirectDownload = true + }; + +#pragma warning disable CA1031 // Do not catch general exception types + try + { + logger.LogMinimal(string.Format( + CultureInfo.CurrentCulture, + Strings.PkgDownload_Starting, + args.PackageId, + string.IsNullOrEmpty(args.Version) ? "latest version" : args.Version)); + + bool download = await InstallPackageAsync( + packageSources, + cache, + args, + settings, + logger, + token); + + if (download) + { + logger.LogMinimal(string.Format( + CultureInfo.CurrentCulture, + Strings.PkgDownload_Succeeded, + args.PackageId, + string.IsNullOrEmpty(args.Version) ? "latest version" : args.Version, + args.OutputDirectory)); + + return ExitCodes.Success; + } + else + { + logger.LogError(string.Format( + CultureInfo.CurrentCulture, + Strings.PkgDownload_Failed, + args.PackageId, + string.IsNullOrEmpty(args.Version) ? "latest version" : args.Version)); + + return ExitCodes.Error; + } + } + catch (Exception ex) + { + logger.LogError(ex.ToString()); + return ExitCodes.Error; + } +#pragma warning restore CA1031 // Do not catch general exception types + } + + private static async Task InstallPackageAsync( + IEnumerable sources, + SourceCacheContext cache, + PackageDownloadArgs args, + ISettings settings, + Common.ILogger logger, + CancellationToken token) + { + bool versionDefined = !string.IsNullOrEmpty(args.Version); + NuGetVersion versionToDownload = null; + SourceRepository downloadSourceRepository = null; + + if (versionDefined) + { + versionToDownload = NuGetVersion.Parse(args.Version); + } + + var extractionContext = new PackageExtractionContext( + PackageSaveMode.Defaultv3, + PackageExtractionBehavior.XmlDocFileSaveMode, + ClientPolicyContext.GetClientPolicy(settings, logger), + logger); + + var resolver = new VersionFolderPathResolver(args.OutputDirectory); + var userPackageFolder = new NuGetv3LocalRepository(args.OutputDirectory); + + // no-op if already installed + if (versionDefined && userPackageFolder.Exists(args.PackageId, versionToDownload)) + { + logger.LogMinimal(string.Format( + CultureInfo.CurrentCulture, + Strings.PkgDownload_AlreadyInstalled, + args.PackageId, + versionToDownload.ToNormalizedString(), + args.OutputDirectory)); + + return true; + } + + // Try each source until we can download and install the requested version + foreach (var source in sources) + { + var repo = Repository.Factory.GetCoreV3(source); + var finder = await repo.GetResourceAsync(token); + + if (versionDefined) + { + // download the defined version at the first source that has it + PackageIdentity identity = new PackageIdentity(args.PackageId, versionToDownload); + + try + { + bool installed = await PackageExtractor.InstallFromSourceAsync( + source.Source, + identity, + async (destination) => + { + using var nupkg = new MemoryStream(); + bool ok = await finder.CopyNupkgToStreamAsync( + identity.Id, identity.Version, nupkg, cache, logger, token); + + if (!ok) throw new InvalidOperationException("Package not found."); + + nupkg.Position = 0; + await nupkg.CopyToAsync(destination, 81920); + }, + resolver, + extractionContext, + token); + + if (installed) + { + return true; + } + + continue; + } + catch (InvalidOperationException) + { + // Package not found at this source, try the next one + continue; + } + } + else + { + // If the version is not defined + // update the version to download value to be the latest + var versions = await finder.GetAllVersionsAsync(args.PackageId, cache, logger, token); + var candidate = versions? + .Where(v => args.IncludePrerelease || !v.IsPrerelease) + .OrderByDescending(v => v) + .FirstOrDefault(); + + if (candidate != null && (versionToDownload == null || candidate > versionToDownload)) + { + versionToDownload = candidate; + downloadSourceRepository = repo; + } + } + } + + // If the version was not defined, and we found a version across one of the sources, install it now + if (!versionDefined && versionToDownload != null && downloadSourceRepository != null) + { + // no-op if already installed + if (userPackageFolder.Exists(args.PackageId, versionToDownload)) + { + logger.LogInformation(string.Format( + CultureInfo.CurrentCulture, + Strings.PkgDownload_AlreadyInstalled, + args.PackageId, + versionToDownload.ToNormalizedString(), + args.OutputDirectory)); + + return true; + } + + // download the latest version found across all sources + PackageIdentity identity = new PackageIdentity(args.PackageId, versionToDownload); + var findPackageByIdResource = await downloadSourceRepository.GetResourceAsync(token); + + try + { + return await PackageExtractor.InstallFromSourceAsync( + downloadSourceRepository.PackageSource.Source, + identity, + async (destination) => + { + using var nupkg = new MemoryStream(); + bool ok = await findPackageByIdResource.CopyNupkgToStreamAsync( + identity.Id, identity.Version, nupkg, cache, logger, token); + if (!ok) throw new InvalidOperationException("Package not found."); + nupkg.Position = 0; + await nupkg.CopyToAsync(destination, 81920); + }, + resolver, + extractionContext, + token); + } + catch (InvalidOperationException) + { + // install failed + return false; + } + } + + return false; + } + + private static IEnumerable GetPackageSources(IList sources, IPackageSourceProvider sourceProvider) + { + IEnumerable configuredSources = sourceProvider.LoadPackageSources() + .Where(s => s.IsEnabled); + + IEnumerable packageSources; + + if (sources != null && sources.Count > 0) + { + // Use sources specified on command line + packageSources = sources + .Select(s => PackageSourceProviderExtensions.ResolveSource(configuredSources, s)); + } + else + { + // Use all configured sources + packageSources = configuredSources; + } + + return packageSources; + } + } +} diff --git a/src/NuGet.Core/NuGet.CommandLine.XPlat/NuGet.CommandLine.XPlat.csproj b/src/NuGet.Core/NuGet.CommandLine.XPlat/NuGet.CommandLine.XPlat.csproj index 850e30c7035..7be310985ad 100644 --- a/src/NuGet.Core/NuGet.CommandLine.XPlat/NuGet.CommandLine.XPlat.csproj +++ b/src/NuGet.Core/NuGet.CommandLine.XPlat/NuGet.CommandLine.XPlat.csproj @@ -12,6 +12,7 @@ + diff --git a/src/NuGet.Core/NuGet.CommandLine.XPlat/Program.cs b/src/NuGet.Core/NuGet.CommandLine.XPlat/Program.cs index f625524ad53..0269fc44435 100644 --- a/src/NuGet.Core/NuGet.CommandLine.XPlat/Program.cs +++ b/src/NuGet.Core/NuGet.CommandLine.XPlat/Program.cs @@ -97,6 +97,7 @@ public static int MainInternal(string[] args, CommandOutputLogger log, IEnvironm { var packageCommand = new Command("package"); rootCommand.Subcommands.Add(packageCommand); + PackageDownloadCommand.Register(packageCommand, getHidePrefixLogger); PackageSearchCommand.Register(packageCommand, getHidePrefixLogger); PackageUpdateCommand.Register(packageCommand, interactiveOption); @@ -246,7 +247,7 @@ private static bool IsSystemCommandLineParsedCommand(string[] args) if (args.Length >= 2 && arg0 == "package") { string arg1 = args[1]; - if (arg1 == "search" || arg1 == "update") + if (arg1 == "search" || arg1 == "update" || arg1 == "download") { return true; } diff --git a/src/NuGet.Core/NuGet.CommandLine.XPlat/Strings.Designer.cs b/src/NuGet.Core/NuGet.CommandLine.XPlat/Strings.Designer.cs index 467fe9b47b9..e551b4cb7b1 100644 --- a/src/NuGet.Core/NuGet.CommandLine.XPlat/Strings.Designer.cs +++ b/src/NuGet.Core/NuGet.CommandLine.XPlat/Strings.Designer.cs @@ -681,6 +681,14 @@ internal static string Error_InvalidVersion { return ResourceManager.GetString("Error_InvalidVersion", resourceCulture); } } + + /// Looks up a localized string similar to Invalid verbosity level '{0}'. Allowed values are: quiet, normal, detailed.. + /// + internal static string Error_InvalidVerbosity { + get { + return ResourceManager.GetString("Error_InvalidVerbosity", resourceCulture); + } + } /// /// Looks up a localized string similar to Invalid version range '{0}'. @@ -1760,6 +1768,141 @@ internal static string PackageUpdateCommand_VulnerableOptionDescription { } } + /// + /// Looks up a localized string similar to Allows downloading from HTTP (non-HTTPS) package sources.. + /// + internal static string pkgDownload_AllowInsecureConnectionsDescritption { + get { + return ResourceManager.GetString("pkgDownload_AllowInsecureConnectionsDescritption", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Skipping download. Package '{0}' version {1} already exists at '{2}'.. + /// + internal static string PkgDownload_AlreadyInstalled { + get { + return ResourceManager.GetString("PkgDownload_AlreadyInstalled", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Path to a NuGet.config file to use for settings and sources.. + /// + internal static string pkgDownload_configFileDesciption { + get { + return ResourceManager.GetString("pkgDownload_configFileDesciption", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Downloads a NuGet package to a local folder without requiring a project file.. + /// + internal static string pkgDownload_descritpion { + get { + return ResourceManager.GetString("pkgDownload_descritpion", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Download only the .nupkg file without extracting it.. + /// + internal static string pkgDownload_downloadOnlyDeciption { + get { + return ResourceManager.GetString("pkgDownload_downloadOnlyDeciption", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Package '{0}' ({1}) failed to download.. + /// + internal static string PkgDownload_Failed { + get { + return ResourceManager.GetString("PkgDownload_Failed", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Allows interactive authentication if required.. + /// + internal static string pkgDownload_interactiveDecription { + get { + return ResourceManager.GetString("pkgDownload_interactiveDecription", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Directory where the package will be placed. Defaults to the current working directory.. + /// + internal static string pkgDownload_OutputDirectoryDescription { + get { + return ResourceManager.GetString("pkgDownload_OutputDirectoryDescription", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Package identifier (e.g. 'Newtonsoft.Json').. + /// + internal static string pkgDownload_packageIdDescription { + get { + return ResourceManager.GetString("pkgDownload_packageIdDescription", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Allows downloading prerelease versions.. + /// + internal static string pkgDownload_prereleaseDescription { + get { + return ResourceManager.GetString("pkgDownload_prereleaseDescription", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Specifies one or more NuGet package sources to use.. + /// + internal static string pkgDownload_sourcesDescription { + get { + return ResourceManager.GetString("pkgDownload_sourcesDescription", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Downloading package {0}, version {1}.. + /// + internal static string PkgDownload_Starting { + get { + return ResourceManager.GetString("PkgDownload_Starting", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Package '{0}' ({1}) successfully downloaded to '{2}'.. + /// + internal static string PkgDownload_Succeeded { + get { + return ResourceManager.GetString("PkgDownload_Succeeded", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Set the verbosity level. Allowed values: quiet | normal | detailed.. + /// + internal static string pkgDownload_verbosityDescription { + get { + return ResourceManager.GetString("pkgDownload_verbosityDescription", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Specific package version to download. + /// + internal static string pkgDownload_versionDescription { + get { + return ResourceManager.GetString("pkgDownload_versionDescription", resourceCulture); + } + } + /// /// Looks up a localized string similar to Searches one or more package sources for packages that match a search term. If no sources are specified, all sources defined in the NuGet.Config are used.. /// diff --git a/src/NuGet.Core/NuGet.CommandLine.XPlat/Strings.resx b/src/NuGet.Core/NuGet.CommandLine.XPlat/Strings.resx index 9065e0a4da7..5cc28cc4c66 100644 --- a/src/NuGet.Core/NuGet.CommandLine.XPlat/Strings.resx +++ b/src/NuGet.Core/NuGet.CommandLine.XPlat/Strings.resx @@ -1108,4 +1108,65 @@ Do not translate "PackageVersion" Invalid version value '{0}'. 0 - package version + + Downloads a NuGet package to a local folder without requiring a project file. + + + Package identifier (e.g. 'Newtonsoft.Json'). + + + Allows downloading from HTTP (non-HTTPS) package sources. + + + Path to a NuGet.config file to use for settings and sources. + + + Download only the .nupkg file without extracting it. + + + Allows interactive authentication if required. + + + Directory where the package will be placed. Defaults to the current working directory. + + + Allows downloading prerelease versions. + + + Specifies one or more NuGet package sources to use. + + + Set the verbosity level. Allowed values: quiet | normal | detailed. + Please do not translate `quiet`, `normal`, `detailed` + + + Specific package version to download + + + Downloading package {0}, version {1}. + 0- package id +1- package version + + + Skipping download. Package '{0}' version {1} already exists at '{2}'. + 0 - package id +1 - version +2 - directory path where the package was downloaded + + + Package '{0}' ({1}) successfully downloaded to '{2}'. + 0 - package id +1 - package version +2 - package download location + + + Package '{0}' ({1}) failed to download. + 0 - package id +1 - package version + + + Invalid verbosity level '{0}'. Allowed values are: quiet, normal, detailed. + 0 - verbosity level +do not translate: quiet, normal, detailed. + \ No newline at end of file diff --git a/src/NuGet.Core/NuGet.CommandLine.XPlat/xlf/Strings.cs.xlf b/src/NuGet.Core/NuGet.CommandLine.XPlat/xlf/Strings.cs.xlf index 4ab265ee684..051c022b416 100644 --- a/src/NuGet.Core/NuGet.CommandLine.XPlat/xlf/Strings.cs.xlf +++ b/src/NuGet.Core/NuGet.CommandLine.XPlat/xlf/Strings.cs.xlf @@ -358,6 +358,12 @@ NuGet vyžaduje zdroje HTTPS. Pokud chcete používat zdroje HTTP, musíte v sou Invalid version value '{0}'. 0 - package version + + Invalid verbosity level '{0}'. Allowed values are: quiet, normal, detailed. + Invalid verbosity level '{0}'. Allowed values are: quiet, normal, detailed. + 0 - verbosity level +do not translate: quiet, normal, detailed. + Invalid version range '{0}' Neplatný rozsah verzí {0} @@ -984,6 +990,32 @@ Další informace najdete tady: https://docs.nuget.org/docs/reference/command-li Aktualizují se balíčky s bezpečnostními upozorněními v {0} {0} - the project/solution being checked + + Skipping download. Package '{0}' version {1} already exists at '{2}'. + Skipping download. Package '{0}' version {1} already exists at '{2}'. + 0 - package id +1 - version +2 - directory path where the package was downloaded + + + Package '{0}' ({1}) failed to download. + Package '{0}' ({1}) failed to download. + 0 - package id +1 - package version + + + Downloading package {0}, version {1}. + Downloading package {0}, version {1}. + 0- package id +1- package version + + + Package '{0}' ({1}) successfully downloaded to '{2}'. + Package '{0}' ({1}) successfully downloaded to '{2}'. + 0 - package id +1 - package version +2 - package download location + There are no stable versions available, {0} is the best available. Consider adding the --prerelease option Nejsou k dispozici žádné stabilní verze, nejlepší dostupná verze je {0}. Zvažte možnost přidat --prerelease. @@ -1451,6 +1483,61 @@ Hledání porovnává řetězce bez rozlišování malých a velkých písmen po Soubor prostředků {0} pro projekt {1} neobsahuje cíl pro zadanou vstupní architekturu {2}. {0} - Assets file path, {1} - Project name, {2} - Framework + + Allows downloading from HTTP (non-HTTPS) package sources. + Allows downloading from HTTP (non-HTTPS) package sources. + + + + Directory where the package will be placed. Defaults to the current working directory. + Directory where the package will be placed. Defaults to the current working directory. + + + + Path to a NuGet.config file to use for settings and sources. + Path to a NuGet.config file to use for settings and sources. + + + + Downloads a NuGet package to a local folder without requiring a project file. + Downloads a NuGet package to a local folder without requiring a project file. + + + + Download only the .nupkg file without extracting it. + Download only the .nupkg file without extracting it. + + + + Allows interactive authentication if required. + Allows interactive authentication if required. + + + + Package identifier (e.g. 'Newtonsoft.Json'). + Package identifier (e.g. 'Newtonsoft.Json'). + + + + Allows downloading prerelease versions. + Allows downloading prerelease versions. + + + + Specifies one or more NuGet package sources to use. + Specifies one or more NuGet package sources to use. + + + + Set the verbosity level. Allowed values: quiet | normal | detailed. + Set the verbosity level. Allowed values: quiet | normal | detailed. + Please do not translate `quiet`, `normal`, `detailed` + + + Specific package version to download + Specific package version to download + + Searches one or more package sources for packages that match a search term. If no sources are specified, all sources defined in the NuGet.Config are used. Vyhledá v jednom nebo více zdrojích balíčků balíčky, které odpovídají hledanému výrazu. Pokud nejsou zadány žádné zdroje, budou použity všechny zdroje definované v NuGet.Config. diff --git a/src/NuGet.Core/NuGet.CommandLine.XPlat/xlf/Strings.de.xlf b/src/NuGet.Core/NuGet.CommandLine.XPlat/xlf/Strings.de.xlf index 694d7433b88..d2aca5850bc 100644 --- a/src/NuGet.Core/NuGet.CommandLine.XPlat/xlf/Strings.de.xlf +++ b/src/NuGet.Core/NuGet.CommandLine.XPlat/xlf/Strings.de.xlf @@ -358,6 +358,12 @@ NuGet erfordert HTTPS-Quellen. Um HTTP-Quellen zu verwenden, müssen Sie „allo Invalid version value '{0}'. 0 - package version + + Invalid verbosity level '{0}'. Allowed values are: quiet, normal, detailed. + Invalid verbosity level '{0}'. Allowed values are: quiet, normal, detailed. + 0 - verbosity level +do not translate: quiet, normal, detailed. + Invalid version range '{0}' Ungültiger Versionsbereich „{0}“ @@ -984,6 +990,32 @@ Weitere Informationen finden Sie unter: https://docs.nuget.org/docs/reference/co Pakete mit Sicherheitshinweisen in {0} werden aktualisiert. {0} - the project/solution being checked + + Skipping download. Package '{0}' version {1} already exists at '{2}'. + Skipping download. Package '{0}' version {1} already exists at '{2}'. + 0 - package id +1 - version +2 - directory path where the package was downloaded + + + Package '{0}' ({1}) failed to download. + Package '{0}' ({1}) failed to download. + 0 - package id +1 - package version + + + Downloading package {0}, version {1}. + Downloading package {0}, version {1}. + 0- package id +1- package version + + + Package '{0}' ({1}) successfully downloaded to '{2}'. + Package '{0}' ({1}) successfully downloaded to '{2}'. + 0 - package id +1 - package version +2 - package download location + There are no stable versions available, {0} is the best available. Consider adding the --prerelease option Es sind keine stabilen Versionen verfügbar. Beste verfügbare Version: {0}. Erwägen Sie, die Option "--prerelease" hinzuzufügen. @@ -1451,6 +1483,61 @@ Bei der Suche handelt es sich um einen ungültigen Zeichenfolgenvergleich mit de Die Ressourcendaten "{0}" für das Projekt "{1}" enthält kein Ziel für das angegebene Eingabeframework "{2}". {0} - Assets file path, {1} - Project name, {2} - Framework + + Allows downloading from HTTP (non-HTTPS) package sources. + Allows downloading from HTTP (non-HTTPS) package sources. + + + + Directory where the package will be placed. Defaults to the current working directory. + Directory where the package will be placed. Defaults to the current working directory. + + + + Path to a NuGet.config file to use for settings and sources. + Path to a NuGet.config file to use for settings and sources. + + + + Downloads a NuGet package to a local folder without requiring a project file. + Downloads a NuGet package to a local folder without requiring a project file. + + + + Download only the .nupkg file without extracting it. + Download only the .nupkg file without extracting it. + + + + Allows interactive authentication if required. + Allows interactive authentication if required. + + + + Package identifier (e.g. 'Newtonsoft.Json'). + Package identifier (e.g. 'Newtonsoft.Json'). + + + + Allows downloading prerelease versions. + Allows downloading prerelease versions. + + + + Specifies one or more NuGet package sources to use. + Specifies one or more NuGet package sources to use. + + + + Set the verbosity level. Allowed values: quiet | normal | detailed. + Set the verbosity level. Allowed values: quiet | normal | detailed. + Please do not translate `quiet`, `normal`, `detailed` + + + Specific package version to download + Specific package version to download + + Searches one or more package sources for packages that match a search term. If no sources are specified, all sources defined in the NuGet.Config are used. Durchsucht mindestens eine Paketquelle nach Paketen, die mit einem Suchbegriff übereinstimmen. Wenn keine Quellen angegeben sind, werden alle in der NuGet.Config definierten Quellen verwendet. diff --git a/src/NuGet.Core/NuGet.CommandLine.XPlat/xlf/Strings.es.xlf b/src/NuGet.Core/NuGet.CommandLine.XPlat/xlf/Strings.es.xlf index df23ef140b8..7db2e77f484 100644 --- a/src/NuGet.Core/NuGet.CommandLine.XPlat/xlf/Strings.es.xlf +++ b/src/NuGet.Core/NuGet.CommandLine.XPlat/xlf/Strings.es.xlf @@ -358,6 +358,12 @@ NuGet requiere orígenes HTTPS. Para usar orígenes HTTP, es necesario establece Invalid version value '{0}'. 0 - package version + + Invalid verbosity level '{0}'. Allowed values are: quiet, normal, detailed. + Invalid verbosity level '{0}'. Allowed values are: quiet, normal, detailed. + 0 - verbosity level +do not translate: quiet, normal, detailed. + Invalid version range '{0}' Intervalo de versiones no válido "{0}" @@ -984,6 +990,32 @@ Para obtener más información, visite https://docs.nuget.org/docs/reference/com Actualizando paquetes con avisos de seguridad en {0} {0} - the project/solution being checked + + Skipping download. Package '{0}' version {1} already exists at '{2}'. + Skipping download. Package '{0}' version {1} already exists at '{2}'. + 0 - package id +1 - version +2 - directory path where the package was downloaded + + + Package '{0}' ({1}) failed to download. + Package '{0}' ({1}) failed to download. + 0 - package id +1 - package version + + + Downloading package {0}, version {1}. + Downloading package {0}, version {1}. + 0- package id +1- package version + + + Package '{0}' ({1}) successfully downloaded to '{2}'. + Package '{0}' ({1}) successfully downloaded to '{2}'. + 0 - package id +1 - package version +2 - package download location + There are no stable versions available, {0} is the best available. Consider adding the --prerelease option No hay ninguna versión estable que se pueda seleccionar; {0} es la mejor opción disponible. Puede agregar la opción --prerelease. @@ -1451,6 +1483,61 @@ La búsqueda es una comparación de cadenas que no diferencia mayúsculas de min El archivo de recursos "{0}" para el proyecto "{1}" no contiene un destino para el marco de entrada "{2}" especificado. {0} - Assets file path, {1} - Project name, {2} - Framework + + Allows downloading from HTTP (non-HTTPS) package sources. + Allows downloading from HTTP (non-HTTPS) package sources. + + + + Directory where the package will be placed. Defaults to the current working directory. + Directory where the package will be placed. Defaults to the current working directory. + + + + Path to a NuGet.config file to use for settings and sources. + Path to a NuGet.config file to use for settings and sources. + + + + Downloads a NuGet package to a local folder without requiring a project file. + Downloads a NuGet package to a local folder without requiring a project file. + + + + Download only the .nupkg file without extracting it. + Download only the .nupkg file without extracting it. + + + + Allows interactive authentication if required. + Allows interactive authentication if required. + + + + Package identifier (e.g. 'Newtonsoft.Json'). + Package identifier (e.g. 'Newtonsoft.Json'). + + + + Allows downloading prerelease versions. + Allows downloading prerelease versions. + + + + Specifies one or more NuGet package sources to use. + Specifies one or more NuGet package sources to use. + + + + Set the verbosity level. Allowed values: quiet | normal | detailed. + Set the verbosity level. Allowed values: quiet | normal | detailed. + Please do not translate `quiet`, `normal`, `detailed` + + + Specific package version to download + Specific package version to download + + Searches one or more package sources for packages that match a search term. If no sources are specified, all sources defined in the NuGet.Config are used. Busca en uno o varios orígenes de paquetes los paquetes que coincidan con un término de búsqueda. Si no se especifica ningún origen, se usarán todos los orígenes definidos en NuGet.Config. diff --git a/src/NuGet.Core/NuGet.CommandLine.XPlat/xlf/Strings.fr.xlf b/src/NuGet.Core/NuGet.CommandLine.XPlat/xlf/Strings.fr.xlf index 82f8fb778b1..f7c552cb6c0 100644 --- a/src/NuGet.Core/NuGet.CommandLine.XPlat/xlf/Strings.fr.xlf +++ b/src/NuGet.Core/NuGet.CommandLine.XPlat/xlf/Strings.fr.xlf @@ -358,6 +358,12 @@ NuGet nécessite des sources HTTPS. Pour utiliser des sources HTTP, vous devez d Invalid version value '{0}'. 0 - package version + + Invalid verbosity level '{0}'. Allowed values are: quiet, normal, detailed. + Invalid verbosity level '{0}'. Allowed values are: quiet, normal, detailed. + 0 - verbosity level +do not translate: quiet, normal, detailed. + Invalid version range '{0}' Étendue de version non valide « {0} » @@ -984,6 +990,32 @@ Pour plus d'informations, visitez https://docs.nuget.org/docs/reference/command- Mise à jour des packages ayant des avis de sécurité dans {0} {0} - the project/solution being checked + + Skipping download. Package '{0}' version {1} already exists at '{2}'. + Skipping download. Package '{0}' version {1} already exists at '{2}'. + 0 - package id +1 - version +2 - directory path where the package was downloaded + + + Package '{0}' ({1}) failed to download. + Package '{0}' ({1}) failed to download. + 0 - package id +1 - package version + + + Downloading package {0}, version {1}. + Downloading package {0}, version {1}. + 0- package id +1- package version + + + Package '{0}' ({1}) successfully downloaded to '{2}'. + Package '{0}' ({1}) successfully downloaded to '{2}'. + 0 - package id +1 - package version +2 - package download location + There are no stable versions available, {0} is the best available. Consider adding the --prerelease option Aucune version stable n'est disponible, {0} est la meilleure version disponible. Ajoutez l'option --prerelease @@ -1451,6 +1483,61 @@ La recherche est une comparaison de chaînes qui ne respecte pas la casse à l Le fichier d’actifs « {0} » pour le « {1} » de projet ne contient pas de cible pour le framework d’entrée spécifié « {2} ». {0} - Assets file path, {1} - Project name, {2} - Framework + + Allows downloading from HTTP (non-HTTPS) package sources. + Allows downloading from HTTP (non-HTTPS) package sources. + + + + Directory where the package will be placed. Defaults to the current working directory. + Directory where the package will be placed. Defaults to the current working directory. + + + + Path to a NuGet.config file to use for settings and sources. + Path to a NuGet.config file to use for settings and sources. + + + + Downloads a NuGet package to a local folder without requiring a project file. + Downloads a NuGet package to a local folder without requiring a project file. + + + + Download only the .nupkg file without extracting it. + Download only the .nupkg file without extracting it. + + + + Allows interactive authentication if required. + Allows interactive authentication if required. + + + + Package identifier (e.g. 'Newtonsoft.Json'). + Package identifier (e.g. 'Newtonsoft.Json'). + + + + Allows downloading prerelease versions. + Allows downloading prerelease versions. + + + + Specifies one or more NuGet package sources to use. + Specifies one or more NuGet package sources to use. + + + + Set the verbosity level. Allowed values: quiet | normal | detailed. + Set the verbosity level. Allowed values: quiet | normal | detailed. + Please do not translate `quiet`, `normal`, `detailed` + + + Specific package version to download + Specific package version to download + + Searches one or more package sources for packages that match a search term. If no sources are specified, all sources defined in the NuGet.Config are used. Recherche dans une ou plusieurs sources de packages les packages qui correspondent à un terme de recherche. Si aucune source n’est spécifiée, toutes les sources définies dans NuGet.Config sont utilisées. diff --git a/src/NuGet.Core/NuGet.CommandLine.XPlat/xlf/Strings.it.xlf b/src/NuGet.Core/NuGet.CommandLine.XPlat/xlf/Strings.it.xlf index e4eafe258ea..2a74ff04f00 100644 --- a/src/NuGet.Core/NuGet.CommandLine.XPlat/xlf/Strings.it.xlf +++ b/src/NuGet.Core/NuGet.CommandLine.XPlat/xlf/Strings.it.xlf @@ -358,6 +358,12 @@ NuGet richiede origini HTTPS. Per utilizzare origini HTTP, è necessario imposta Invalid version value '{0}'. 0 - package version + + Invalid verbosity level '{0}'. Allowed values are: quiet, normal, detailed. + Invalid verbosity level '{0}'. Allowed values are: quiet, normal, detailed. + 0 - verbosity level +do not translate: quiet, normal, detailed. + Invalid version range '{0}' Intervallo versioni non valido '{0}' @@ -984,6 +990,32 @@ Per altre informazioni, vedere https://docs.nuget.org/docs/reference/command-lin È in corso l'aggiornamento dei pacchetti con avvisi di sicurezza in {0} {0} - the project/solution being checked + + Skipping download. Package '{0}' version {1} already exists at '{2}'. + Skipping download. Package '{0}' version {1} already exists at '{2}'. + 0 - package id +1 - version +2 - directory path where the package was downloaded + + + Package '{0}' ({1}) failed to download. + Package '{0}' ({1}) failed to download. + 0 - package id +1 - package version + + + Downloading package {0}, version {1}. + Downloading package {0}, version {1}. + 0- package id +1- package version + + + Package '{0}' ({1}) successfully downloaded to '{2}'. + Package '{0}' ({1}) successfully downloaded to '{2}'. + 0 - package id +1 - package version +2 - package download location + There are no stable versions available, {0} is the best available. Consider adding the --prerelease option Non sono disponibili versioni stabili e {0} è la migliore versione disponibile. Provare ad aggiungere l'opzione --prerelease @@ -1451,6 +1483,61 @@ La ricerca viene eseguita in un confronto di stringhe senza distinzione tra maiu Il file di asset {0} per il progetto '{1}' non contiene una destinazione per il framework di input specificato '{2}'. {0} - Assets file path, {1} - Project name, {2} - Framework + + Allows downloading from HTTP (non-HTTPS) package sources. + Allows downloading from HTTP (non-HTTPS) package sources. + + + + Directory where the package will be placed. Defaults to the current working directory. + Directory where the package will be placed. Defaults to the current working directory. + + + + Path to a NuGet.config file to use for settings and sources. + Path to a NuGet.config file to use for settings and sources. + + + + Downloads a NuGet package to a local folder without requiring a project file. + Downloads a NuGet package to a local folder without requiring a project file. + + + + Download only the .nupkg file without extracting it. + Download only the .nupkg file without extracting it. + + + + Allows interactive authentication if required. + Allows interactive authentication if required. + + + + Package identifier (e.g. 'Newtonsoft.Json'). + Package identifier (e.g. 'Newtonsoft.Json'). + + + + Allows downloading prerelease versions. + Allows downloading prerelease versions. + + + + Specifies one or more NuGet package sources to use. + Specifies one or more NuGet package sources to use. + + + + Set the verbosity level. Allowed values: quiet | normal | detailed. + Set the verbosity level. Allowed values: quiet | normal | detailed. + Please do not translate `quiet`, `normal`, `detailed` + + + Specific package version to download + Specific package version to download + + Searches one or more package sources for packages that match a search term. If no sources are specified, all sources defined in the NuGet.Config are used. Cerca in una o più origini pacchetti per pacchetti che corrispondono a un termine di ricerca. Se non vengono specificate origini, vengono usate tutte le origini definite in NuGet.Config. diff --git a/src/NuGet.Core/NuGet.CommandLine.XPlat/xlf/Strings.ja.xlf b/src/NuGet.Core/NuGet.CommandLine.XPlat/xlf/Strings.ja.xlf index b59fae6677c..63b6d8f1289 100644 --- a/src/NuGet.Core/NuGet.CommandLine.XPlat/xlf/Strings.ja.xlf +++ b/src/NuGet.Core/NuGet.CommandLine.XPlat/xlf/Strings.ja.xlf @@ -358,6 +358,12 @@ NuGet には HTTPS ソースが必要です。HTTP ソースを使用するに Invalid version value '{0}'. 0 - package version + + Invalid verbosity level '{0}'. Allowed values are: quiet, normal, detailed. + Invalid verbosity level '{0}'. Allowed values are: quiet, normal, detailed. + 0 - verbosity level +do not translate: quiet, normal, detailed. + Invalid version range '{0}' バージョン範囲 '{0}' が無効です @@ -984,6 +990,32 @@ For more information, visit https://docs.nuget.org/docs/reference/command-line-r {0} 内のセキュリティ アドバイザリがあるパッケージを更新しています {0} - the project/solution being checked + + Skipping download. Package '{0}' version {1} already exists at '{2}'. + Skipping download. Package '{0}' version {1} already exists at '{2}'. + 0 - package id +1 - version +2 - directory path where the package was downloaded + + + Package '{0}' ({1}) failed to download. + Package '{0}' ({1}) failed to download. + 0 - package id +1 - package version + + + Downloading package {0}, version {1}. + Downloading package {0}, version {1}. + 0- package id +1- package version + + + Package '{0}' ({1}) successfully downloaded to '{2}'. + Package '{0}' ({1}) successfully downloaded to '{2}'. + 0 - package id +1 - package version +2 - package download location + There are no stable versions available, {0} is the best available. Consider adding the --prerelease option 利用可能な安定バージョンがありません。利用可能な中では {0} が最善です。--prerelease オプションを追加することをご検討ください @@ -1451,6 +1483,61 @@ The search is a case-insensitive string comparison using the supplied value, whi プロジェクト '{1}' のアセット ファイル '{0}' に、指定された入力フレームワーク '{2}' のターゲットが含まれていません。 {0} - Assets file path, {1} - Project name, {2} - Framework + + Allows downloading from HTTP (non-HTTPS) package sources. + Allows downloading from HTTP (non-HTTPS) package sources. + + + + Directory where the package will be placed. Defaults to the current working directory. + Directory where the package will be placed. Defaults to the current working directory. + + + + Path to a NuGet.config file to use for settings and sources. + Path to a NuGet.config file to use for settings and sources. + + + + Downloads a NuGet package to a local folder without requiring a project file. + Downloads a NuGet package to a local folder without requiring a project file. + + + + Download only the .nupkg file without extracting it. + Download only the .nupkg file without extracting it. + + + + Allows interactive authentication if required. + Allows interactive authentication if required. + + + + Package identifier (e.g. 'Newtonsoft.Json'). + Package identifier (e.g. 'Newtonsoft.Json'). + + + + Allows downloading prerelease versions. + Allows downloading prerelease versions. + + + + Specifies one or more NuGet package sources to use. + Specifies one or more NuGet package sources to use. + + + + Set the verbosity level. Allowed values: quiet | normal | detailed. + Set the verbosity level. Allowed values: quiet | normal | detailed. + Please do not translate `quiet`, `normal`, `detailed` + + + Specific package version to download + Specific package version to download + + Searches one or more package sources for packages that match a search term. If no sources are specified, all sources defined in the NuGet.Config are used. 検索語句に一致するパッケージを 1 つ以上のパッケージ ソースで検索します。ソースが指定されていない場合、NuGet.Config で定義されているすべてのソースが使用されます。 diff --git a/src/NuGet.Core/NuGet.CommandLine.XPlat/xlf/Strings.ko.xlf b/src/NuGet.Core/NuGet.CommandLine.XPlat/xlf/Strings.ko.xlf index 49021d131a1..721c4ac7173 100644 --- a/src/NuGet.Core/NuGet.CommandLine.XPlat/xlf/Strings.ko.xlf +++ b/src/NuGet.Core/NuGet.CommandLine.XPlat/xlf/Strings.ko.xlf @@ -358,6 +358,12 @@ NuGet에는 HTTPS 원본이 필요합니다. HTTP 원본을 사용하려면 NuGe Invalid version value '{0}'. 0 - package version + + Invalid verbosity level '{0}'. Allowed values are: quiet, normal, detailed. + Invalid verbosity level '{0}'. Allowed values are: quiet, normal, detailed. + 0 - verbosity level +do not translate: quiet, normal, detailed. + Invalid version range '{0}' 잘못된 버전 범위 '{0}' @@ -984,6 +990,32 @@ For more information, visit https://docs.nuget.org/docs/reference/command-line-r {0}에서 보안 권고가 있는 패키지를 업데이트하는 중 {0} - the project/solution being checked + + Skipping download. Package '{0}' version {1} already exists at '{2}'. + Skipping download. Package '{0}' version {1} already exists at '{2}'. + 0 - package id +1 - version +2 - directory path where the package was downloaded + + + Package '{0}' ({1}) failed to download. + Package '{0}' ({1}) failed to download. + 0 - package id +1 - package version + + + Downloading package {0}, version {1}. + Downloading package {0}, version {1}. + 0- package id +1- package version + + + Package '{0}' ({1}) successfully downloaded to '{2}'. + Package '{0}' ({1}) successfully downloaded to '{2}'. + 0 - package id +1 - package version +2 - package download location + There are no stable versions available, {0} is the best available. Consider adding the --prerelease option 사용할 수 있는 안정적인 버전이 없습니다. 사용할 수 있는 최상의 버전은 {0}입니다. --prerelease 옵션을 추가하는 것이 좋습니다. @@ -1452,6 +1484,61 @@ The search is a case-insensitive string comparison using the supplied value, whi 프로젝트 '{1}'의 자산 파일 '{0}'에 지정된 입력 프레임워크 '{2}' 대상이 없습니다. {0} - Assets file path, {1} - Project name, {2} - Framework + + Allows downloading from HTTP (non-HTTPS) package sources. + Allows downloading from HTTP (non-HTTPS) package sources. + + + + Directory where the package will be placed. Defaults to the current working directory. + Directory where the package will be placed. Defaults to the current working directory. + + + + Path to a NuGet.config file to use for settings and sources. + Path to a NuGet.config file to use for settings and sources. + + + + Downloads a NuGet package to a local folder without requiring a project file. + Downloads a NuGet package to a local folder without requiring a project file. + + + + Download only the .nupkg file without extracting it. + Download only the .nupkg file without extracting it. + + + + Allows interactive authentication if required. + Allows interactive authentication if required. + + + + Package identifier (e.g. 'Newtonsoft.Json'). + Package identifier (e.g. 'Newtonsoft.Json'). + + + + Allows downloading prerelease versions. + Allows downloading prerelease versions. + + + + Specifies one or more NuGet package sources to use. + Specifies one or more NuGet package sources to use. + + + + Set the verbosity level. Allowed values: quiet | normal | detailed. + Set the verbosity level. Allowed values: quiet | normal | detailed. + Please do not translate `quiet`, `normal`, `detailed` + + + Specific package version to download + Specific package version to download + + Searches one or more package sources for packages that match a search term. If no sources are specified, all sources defined in the NuGet.Config are used. 하나 이상의 패키지 원본에서 검색어와 일치하는 패키지를 검색합니다. 원본을 지정하지 않으면 NuGet.Config에 정의된 모든 원본이 사용됩니다. diff --git a/src/NuGet.Core/NuGet.CommandLine.XPlat/xlf/Strings.pl.xlf b/src/NuGet.Core/NuGet.CommandLine.XPlat/xlf/Strings.pl.xlf index 3973ba7aced..6556e2a79ab 100644 --- a/src/NuGet.Core/NuGet.CommandLine.XPlat/xlf/Strings.pl.xlf +++ b/src/NuGet.Core/NuGet.CommandLine.XPlat/xlf/Strings.pl.xlf @@ -358,6 +358,12 @@ Menedżer NuGet wymaga źródeł HTTPS. Aby użyć źródeł HTTP, musisz wyraź Invalid version value '{0}'. 0 - package version + + Invalid verbosity level '{0}'. Allowed values are: quiet, normal, detailed. + Invalid verbosity level '{0}'. Allowed values are: quiet, normal, detailed. + 0 - verbosity level +do not translate: quiet, normal, detailed. + Invalid version range '{0}' Nieprawidłowy zakres wersji „{0}” @@ -984,6 +990,32 @@ Aby uzyskać więcej informacji, odwiedź stronę https://docs.nuget.org/docs/re Aktualizowanie pakietów z ostrzeżeniami dotyczącymi zabezpieczeń w {0} {0} - the project/solution being checked + + Skipping download. Package '{0}' version {1} already exists at '{2}'. + Skipping download. Package '{0}' version {1} already exists at '{2}'. + 0 - package id +1 - version +2 - directory path where the package was downloaded + + + Package '{0}' ({1}) failed to download. + Package '{0}' ({1}) failed to download. + 0 - package id +1 - package version + + + Downloading package {0}, version {1}. + Downloading package {0}, version {1}. + 0- package id +1- package version + + + Package '{0}' ({1}) successfully downloaded to '{2}'. + Package '{0}' ({1}) successfully downloaded to '{2}'. + 0 - package id +1 - package version +2 - package download location + There are no stable versions available, {0} is the best available. Consider adding the --prerelease option Brak dostępnych stabilnych wersji. Najlepsza dostępna opcja to {0}. Rozważ dodanie opcji --prerelease @@ -1451,6 +1483,61 @@ Wyszukiwanie polega na porównywaniu łańcuchów bez rozróżniania wielkości Plik zasobów „{0}” dla projektu „{1}” nie zawiera celu dla określonej struktury wejściowej „{2}”. {0} - Assets file path, {1} - Project name, {2} - Framework + + Allows downloading from HTTP (non-HTTPS) package sources. + Allows downloading from HTTP (non-HTTPS) package sources. + + + + Directory where the package will be placed. Defaults to the current working directory. + Directory where the package will be placed. Defaults to the current working directory. + + + + Path to a NuGet.config file to use for settings and sources. + Path to a NuGet.config file to use for settings and sources. + + + + Downloads a NuGet package to a local folder without requiring a project file. + Downloads a NuGet package to a local folder without requiring a project file. + + + + Download only the .nupkg file without extracting it. + Download only the .nupkg file without extracting it. + + + + Allows interactive authentication if required. + Allows interactive authentication if required. + + + + Package identifier (e.g. 'Newtonsoft.Json'). + Package identifier (e.g. 'Newtonsoft.Json'). + + + + Allows downloading prerelease versions. + Allows downloading prerelease versions. + + + + Specifies one or more NuGet package sources to use. + Specifies one or more NuGet package sources to use. + + + + Set the verbosity level. Allowed values: quiet | normal | detailed. + Set the verbosity level. Allowed values: quiet | normal | detailed. + Please do not translate `quiet`, `normal`, `detailed` + + + Specific package version to download + Specific package version to download + + Searches one or more package sources for packages that match a search term. If no sources are specified, all sources defined in the NuGet.Config are used. Wyszukuje co najmniej jedno źródło pakietów, które są zgodne z wyszukiwanym terminem. Jeśli nie określono żadnego źródła, zostaną użyte wszystkie źródła zdefiniowane w NuGet.Config. diff --git a/src/NuGet.Core/NuGet.CommandLine.XPlat/xlf/Strings.pt-BR.xlf b/src/NuGet.Core/NuGet.CommandLine.XPlat/xlf/Strings.pt-BR.xlf index d39fd35c62e..310ceb71d6a 100644 --- a/src/NuGet.Core/NuGet.CommandLine.XPlat/xlf/Strings.pt-BR.xlf +++ b/src/NuGet.Core/NuGet.CommandLine.XPlat/xlf/Strings.pt-BR.xlf @@ -358,6 +358,12 @@ O NuGet requer fontes HTTPS. Para usar fontes HTTP, você deve definir explicita Invalid version value '{0}'. 0 - package version + + Invalid verbosity level '{0}'. Allowed values are: quiet, normal, detailed. + Invalid verbosity level '{0}'. Allowed values are: quiet, normal, detailed. + 0 - verbosity level +do not translate: quiet, normal, detailed. + Invalid version range '{0}' Intervalo de versão "{0}" inválido @@ -984,6 +990,32 @@ Para obter mais informações, acesse https://docs.nuget.org/docs/reference/comm Atualizando pacotes com avisos de segurança em {0} {0} - the project/solution being checked + + Skipping download. Package '{0}' version {1} already exists at '{2}'. + Skipping download. Package '{0}' version {1} already exists at '{2}'. + 0 - package id +1 - version +2 - directory path where the package was downloaded + + + Package '{0}' ({1}) failed to download. + Package '{0}' ({1}) failed to download. + 0 - package id +1 - package version + + + Downloading package {0}, version {1}. + Downloading package {0}, version {1}. + 0- package id +1- package version + + + Package '{0}' ({1}) successfully downloaded to '{2}'. + Package '{0}' ({1}) successfully downloaded to '{2}'. + 0 - package id +1 - package version +2 - package download location + There are no stable versions available, {0} is the best available. Consider adding the --prerelease option Não há versões estáveis disponíveis. {0} é a melhor disponível. Considere a adição da opção --prerelease @@ -1451,6 +1483,61 @@ A pesquisa é uma comparação de cadeia de caracteres que não faz distinção O arquivo de ativos "{0}" para o projeto "{1}" não contém um destino para a estrutura de entrada especificada "{2}". {0} - Assets file path, {1} - Project name, {2} - Framework + + Allows downloading from HTTP (non-HTTPS) package sources. + Allows downloading from HTTP (non-HTTPS) package sources. + + + + Directory where the package will be placed. Defaults to the current working directory. + Directory where the package will be placed. Defaults to the current working directory. + + + + Path to a NuGet.config file to use for settings and sources. + Path to a NuGet.config file to use for settings and sources. + + + + Downloads a NuGet package to a local folder without requiring a project file. + Downloads a NuGet package to a local folder without requiring a project file. + + + + Download only the .nupkg file without extracting it. + Download only the .nupkg file without extracting it. + + + + Allows interactive authentication if required. + Allows interactive authentication if required. + + + + Package identifier (e.g. 'Newtonsoft.Json'). + Package identifier (e.g. 'Newtonsoft.Json'). + + + + Allows downloading prerelease versions. + Allows downloading prerelease versions. + + + + Specifies one or more NuGet package sources to use. + Specifies one or more NuGet package sources to use. + + + + Set the verbosity level. Allowed values: quiet | normal | detailed. + Set the verbosity level. Allowed values: quiet | normal | detailed. + Please do not translate `quiet`, `normal`, `detailed` + + + Specific package version to download + Specific package version to download + + Searches one or more package sources for packages that match a search term. If no sources are specified, all sources defined in the NuGet.Config are used. Pesquisa uma ou mais fontes de pacote em busca de pacotes que correspondam a um termo de pesquisa. Se nenhuma fonte for especificada, todas as fontes definidas no NuGet.Config serão usadas. diff --git a/src/NuGet.Core/NuGet.CommandLine.XPlat/xlf/Strings.ru.xlf b/src/NuGet.Core/NuGet.CommandLine.XPlat/xlf/Strings.ru.xlf index 0c2ceb27912..4330c968b98 100644 --- a/src/NuGet.Core/NuGet.CommandLine.XPlat/xlf/Strings.ru.xlf +++ b/src/NuGet.Core/NuGet.CommandLine.XPlat/xlf/Strings.ru.xlf @@ -358,6 +358,12 @@ NuGet requires HTTPS sources. To use HTTP sources, you must explicitly set 'allo Invalid version value '{0}'. 0 - package version + + Invalid verbosity level '{0}'. Allowed values are: quiet, normal, detailed. + Invalid verbosity level '{0}'. Allowed values are: quiet, normal, detailed. + 0 - verbosity level +do not translate: quiet, normal, detailed. + Invalid version range '{0}' Недопустимый диапазон версий "{0}" @@ -984,6 +990,32 @@ For more information, visit https://docs.nuget.org/docs/reference/command-line-r Обновление пакетов с предупреждениями по безопасности в {0} {0} - the project/solution being checked + + Skipping download. Package '{0}' version {1} already exists at '{2}'. + Skipping download. Package '{0}' version {1} already exists at '{2}'. + 0 - package id +1 - version +2 - directory path where the package was downloaded + + + Package '{0}' ({1}) failed to download. + Package '{0}' ({1}) failed to download. + 0 - package id +1 - package version + + + Downloading package {0}, version {1}. + Downloading package {0}, version {1}. + 0- package id +1- package version + + + Package '{0}' ({1}) successfully downloaded to '{2}'. + Package '{0}' ({1}) successfully downloaded to '{2}'. + 0 - package id +1 - package version +2 - package download location + There are no stable versions available, {0} is the best available. Consider adding the --prerelease option Нет доступных стабильных версий. Наилучшей из доступных является {0}. Попробуйте добавить параметр --prerelease. @@ -1451,6 +1483,61 @@ The search is a case-insensitive string comparison using the supplied value, whi Файл ресурсов "{0}" для проекта "{1}" не содержит целевой объект для указанной входной платформы "{2}". {0} - Assets file path, {1} - Project name, {2} - Framework + + Allows downloading from HTTP (non-HTTPS) package sources. + Allows downloading from HTTP (non-HTTPS) package sources. + + + + Directory where the package will be placed. Defaults to the current working directory. + Directory where the package will be placed. Defaults to the current working directory. + + + + Path to a NuGet.config file to use for settings and sources. + Path to a NuGet.config file to use for settings and sources. + + + + Downloads a NuGet package to a local folder without requiring a project file. + Downloads a NuGet package to a local folder without requiring a project file. + + + + Download only the .nupkg file without extracting it. + Download only the .nupkg file without extracting it. + + + + Allows interactive authentication if required. + Allows interactive authentication if required. + + + + Package identifier (e.g. 'Newtonsoft.Json'). + Package identifier (e.g. 'Newtonsoft.Json'). + + + + Allows downloading prerelease versions. + Allows downloading prerelease versions. + + + + Specifies one or more NuGet package sources to use. + Specifies one or more NuGet package sources to use. + + + + Set the verbosity level. Allowed values: quiet | normal | detailed. + Set the verbosity level. Allowed values: quiet | normal | detailed. + Please do not translate `quiet`, `normal`, `detailed` + + + Specific package version to download + Specific package version to download + + Searches one or more package sources for packages that match a search term. If no sources are specified, all sources defined in the NuGet.Config are used. Ищет в одном или нескольких источниках пакетов пакеты, соответствующие поисковому запросу. Если источники не указаны, используются все источники, определенные в NuGet.Config. diff --git a/src/NuGet.Core/NuGet.CommandLine.XPlat/xlf/Strings.tr.xlf b/src/NuGet.Core/NuGet.CommandLine.XPlat/xlf/Strings.tr.xlf index 3fc1b6b3dc4..6a650b47e4d 100644 --- a/src/NuGet.Core/NuGet.CommandLine.XPlat/xlf/Strings.tr.xlf +++ b/src/NuGet.Core/NuGet.CommandLine.XPlat/xlf/Strings.tr.xlf @@ -359,6 +359,12 @@ NuGet için HTTPS kaynakları gereklidir. HTTP kaynaklarını kullanmak için Nu Invalid version value '{0}'. 0 - package version + + Invalid verbosity level '{0}'. Allowed values are: quiet, normal, detailed. + Invalid verbosity level '{0}'. Allowed values are: quiet, normal, detailed. + 0 - verbosity level +do not translate: quiet, normal, detailed. + Invalid version range '{0}' Geçersiz sürüm aralığı '{0}' @@ -985,6 +991,32 @@ Daha fazla bilgi için bkz. https://docs.nuget.org/docs/reference/command-line-r {0} içerisinde güvenlik uyarıları içeren paketler güncelleştiriliyor {0} - the project/solution being checked + + Skipping download. Package '{0}' version {1} already exists at '{2}'. + Skipping download. Package '{0}' version {1} already exists at '{2}'. + 0 - package id +1 - version +2 - directory path where the package was downloaded + + + Package '{0}' ({1}) failed to download. + Package '{0}' ({1}) failed to download. + 0 - package id +1 - package version + + + Downloading package {0}, version {1}. + Downloading package {0}, version {1}. + 0- package id +1- package version + + + Package '{0}' ({1}) successfully downloaded to '{2}'. + Package '{0}' ({1}) successfully downloaded to '{2}'. + 0 - package id +1 - package version +2 - package download location + There are no stable versions available, {0} is the best available. Consider adding the --prerelease option Kullanılabilir kararlı bir sürüm olmadığından {0} en iyi seçenektir. --prerelease seçeneğini eklemeyi göz önünde bulundurun @@ -1452,6 +1484,61 @@ Arama, sağlanan değeri kullanan büyük/küçük harfe duyarsız bir dize kar '{1}' projesinin '{0}' varlık dosyası, belirtilen '{2}' giriş çerçevesi için bir hedef içermiyor. {0} - Assets file path, {1} - Project name, {2} - Framework + + Allows downloading from HTTP (non-HTTPS) package sources. + Allows downloading from HTTP (non-HTTPS) package sources. + + + + Directory where the package will be placed. Defaults to the current working directory. + Directory where the package will be placed. Defaults to the current working directory. + + + + Path to a NuGet.config file to use for settings and sources. + Path to a NuGet.config file to use for settings and sources. + + + + Downloads a NuGet package to a local folder without requiring a project file. + Downloads a NuGet package to a local folder without requiring a project file. + + + + Download only the .nupkg file without extracting it. + Download only the .nupkg file without extracting it. + + + + Allows interactive authentication if required. + Allows interactive authentication if required. + + + + Package identifier (e.g. 'Newtonsoft.Json'). + Package identifier (e.g. 'Newtonsoft.Json'). + + + + Allows downloading prerelease versions. + Allows downloading prerelease versions. + + + + Specifies one or more NuGet package sources to use. + Specifies one or more NuGet package sources to use. + + + + Set the verbosity level. Allowed values: quiet | normal | detailed. + Set the verbosity level. Allowed values: quiet | normal | detailed. + Please do not translate `quiet`, `normal`, `detailed` + + + Specific package version to download + Specific package version to download + + Searches one or more package sources for packages that match a search term. If no sources are specified, all sources defined in the NuGet.Config are used. Bir arama terimiyle eşleşen paketler için bir veya daha fazla paket kaynağı arar. Kaynak belirtilmezse NuGet.Config’de tanımlanan tüm kaynaklar kullanılır. diff --git a/src/NuGet.Core/NuGet.CommandLine.XPlat/xlf/Strings.zh-Hans.xlf b/src/NuGet.Core/NuGet.CommandLine.XPlat/xlf/Strings.zh-Hans.xlf index d72727b90e9..e1d40709ab9 100644 --- a/src/NuGet.Core/NuGet.CommandLine.XPlat/xlf/Strings.zh-Hans.xlf +++ b/src/NuGet.Core/NuGet.CommandLine.XPlat/xlf/Strings.zh-Hans.xlf @@ -358,6 +358,12 @@ NuGet 需要 HTTPS 源。要使用 HTTP 源,必须在 NuGet.Config 文件中 Invalid version value '{0}'. 0 - package version + + Invalid verbosity level '{0}'. Allowed values are: quiet, normal, detailed. + Invalid verbosity level '{0}'. Allowed values are: quiet, normal, detailed. + 0 - verbosity level +do not translate: quiet, normal, detailed. + Invalid version range '{0}' 版本范围“{0}”无效 @@ -984,6 +990,32 @@ For more information, visit https://docs.nuget.org/docs/reference/command-line-r 正在更新 {0} 中带有安全公告的包 {0} - the project/solution being checked + + Skipping download. Package '{0}' version {1} already exists at '{2}'. + Skipping download. Package '{0}' version {1} already exists at '{2}'. + 0 - package id +1 - version +2 - directory path where the package was downloaded + + + Package '{0}' ({1}) failed to download. + Package '{0}' ({1}) failed to download. + 0 - package id +1 - package version + + + Downloading package {0}, version {1}. + Downloading package {0}, version {1}. + 0- package id +1- package version + + + Package '{0}' ({1}) successfully downloaded to '{2}'. + Package '{0}' ({1}) successfully downloaded to '{2}'. + 0 - package id +1 - package version +2 - package download location + There are no stable versions available, {0} is the best available. Consider adding the --prerelease option 没有可用的稳定版本,{0} 是可用的最佳版本。请考虑添加 --prerelease 选项 @@ -1451,6 +1483,61 @@ The search is a case-insensitive string comparison using the supplied value, whi 项目“{1}”的资产文件“{0}”不包含指定输入框架“{2}”的目标。 {0} - Assets file path, {1} - Project name, {2} - Framework + + Allows downloading from HTTP (non-HTTPS) package sources. + Allows downloading from HTTP (non-HTTPS) package sources. + + + + Directory where the package will be placed. Defaults to the current working directory. + Directory where the package will be placed. Defaults to the current working directory. + + + + Path to a NuGet.config file to use for settings and sources. + Path to a NuGet.config file to use for settings and sources. + + + + Downloads a NuGet package to a local folder without requiring a project file. + Downloads a NuGet package to a local folder without requiring a project file. + + + + Download only the .nupkg file without extracting it. + Download only the .nupkg file without extracting it. + + + + Allows interactive authentication if required. + Allows interactive authentication if required. + + + + Package identifier (e.g. 'Newtonsoft.Json'). + Package identifier (e.g. 'Newtonsoft.Json'). + + + + Allows downloading prerelease versions. + Allows downloading prerelease versions. + + + + Specifies one or more NuGet package sources to use. + Specifies one or more NuGet package sources to use. + + + + Set the verbosity level. Allowed values: quiet | normal | detailed. + Set the verbosity level. Allowed values: quiet | normal | detailed. + Please do not translate `quiet`, `normal`, `detailed` + + + Specific package version to download + Specific package version to download + + Searches one or more package sources for packages that match a search term. If no sources are specified, all sources defined in the NuGet.Config are used. 在一个或多个包源中搜索与搜索词匹配的包。如果未指定任何源,则会使用 NuGet.Config 中定义的所有源。 diff --git a/src/NuGet.Core/NuGet.CommandLine.XPlat/xlf/Strings.zh-Hant.xlf b/src/NuGet.Core/NuGet.CommandLine.XPlat/xlf/Strings.zh-Hant.xlf index 1e8ca73ed77..3899176e6ce 100644 --- a/src/NuGet.Core/NuGet.CommandLine.XPlat/xlf/Strings.zh-Hant.xlf +++ b/src/NuGet.Core/NuGet.CommandLine.XPlat/xlf/Strings.zh-Hant.xlf @@ -358,6 +358,12 @@ NuGet 需要 HTTPS 來源。您必須在 NuGet.Config 檔案中將 'allowInsecur Invalid version value '{0}'. 0 - package version + + Invalid verbosity level '{0}'. Allowed values are: quiet, normal, detailed. + Invalid verbosity level '{0}'. Allowed values are: quiet, normal, detailed. + 0 - verbosity level +do not translate: quiet, normal, detailed. + Invalid version range '{0}' 不正確版本範圍 '{0}' @@ -984,6 +990,32 @@ For more information, visit https://docs.nuget.org/docs/reference/command-line-r 正在更新 {0} 中含有安全性建議的套件 {0} - the project/solution being checked + + Skipping download. Package '{0}' version {1} already exists at '{2}'. + Skipping download. Package '{0}' version {1} already exists at '{2}'. + 0 - package id +1 - version +2 - directory path where the package was downloaded + + + Package '{0}' ({1}) failed to download. + Package '{0}' ({1}) failed to download. + 0 - package id +1 - package version + + + Downloading package {0}, version {1}. + Downloading package {0}, version {1}. + 0- package id +1- package version + + + Package '{0}' ({1}) successfully downloaded to '{2}'. + Package '{0}' ({1}) successfully downloaded to '{2}'. + 0 - package id +1 - package version +2 - package download location + There are no stable versions available, {0} is the best available. Consider adding the --prerelease option 沒有可用的穩定版本,{0} 是最適用的版本。請考慮新增 --prerelease 選項 @@ -1451,6 +1483,61 @@ The search is a case-insensitive string comparison using the supplied value, whi 專案 '{1}' 的資產檔案 '{0}' 未包含指定輸入架構 '{2}' 的目標。 {0} - Assets file path, {1} - Project name, {2} - Framework + + Allows downloading from HTTP (non-HTTPS) package sources. + Allows downloading from HTTP (non-HTTPS) package sources. + + + + Directory where the package will be placed. Defaults to the current working directory. + Directory where the package will be placed. Defaults to the current working directory. + + + + Path to a NuGet.config file to use for settings and sources. + Path to a NuGet.config file to use for settings and sources. + + + + Downloads a NuGet package to a local folder without requiring a project file. + Downloads a NuGet package to a local folder without requiring a project file. + + + + Download only the .nupkg file without extracting it. + Download only the .nupkg file without extracting it. + + + + Allows interactive authentication if required. + Allows interactive authentication if required. + + + + Package identifier (e.g. 'Newtonsoft.Json'). + Package identifier (e.g. 'Newtonsoft.Json'). + + + + Allows downloading prerelease versions. + Allows downloading prerelease versions. + + + + Specifies one or more NuGet package sources to use. + Specifies one or more NuGet package sources to use. + + + + Set the verbosity level. Allowed values: quiet | normal | detailed. + Set the verbosity level. Allowed values: quiet | normal | detailed. + Please do not translate `quiet`, `normal`, `detailed` + + + Specific package version to download + Specific package version to download + + Searches one or more package sources for packages that match a search term. If no sources are specified, all sources defined in the NuGet.Config are used. 搜尋一或多個套件來源以尋找符合搜尋字詞的套件。如果未指定來源,則會使用 NuGet.Config 中定義的所有來源。 diff --git a/test/NuGet.Core.Tests/NuGet.CommandLine.Xplat.Tests/PackageDownloadRunnerTests.cs b/test/NuGet.Core.Tests/NuGet.CommandLine.Xplat.Tests/PackageDownloadRunnerTests.cs new file mode 100644 index 00000000000..11f28f60d9e --- /dev/null +++ b/test/NuGet.Core.Tests/NuGet.CommandLine.Xplat.Tests/PackageDownloadRunnerTests.cs @@ -0,0 +1,294 @@ +// 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. + +namespace NuGet.CommandLine.Xplat.Tests; + +using System.IO; +using System.Linq; +using System.Threading.Tasks; +using FluentAssertions; +using Moq; +using NuGet.CommandLine.XPlat; +using NuGet.CommandLine.XPlat.Commands.PackageDownload; +using NuGet.Common; +using NuGet.Test.Utility; +using Xunit; + +public class PackageDownloadRunnerTests +{ + [Fact] + public async Task RunAsync_ExplicitVersionFromLocalFolderSource_SucceedsAsync() + { + using var context = new SimpleTestPathContext(); + + // Arrange + var sourceDir = Path.Combine(context.WorkingDirectory, "src"); + var outputDir = Path.Combine(context.WorkingDirectory, "packages"); + Directory.CreateDirectory(sourceDir); + Directory.CreateDirectory(outputDir); + + var id = "Contoso.Lib"; + var version = "1.2.3"; + await SimpleTestPackageUtility.CreateFullPackageAsync(sourceDir, id, version); + + var logger = new Mock(MockBehavior.Loose); + + var args = new PackageDownloadArgs( + packageId: id, + sources: new[] { sourceDir }.ToList(), + outputDirectory: outputDir, + logger: logger.Object) + { + Version = version, + ConfigFile = context.NuGetConfig, + AllowInsecureConnections = true, + Verbosity = Verbosity.Detailed + }; + + + // Act + var result = await PackageDownloadRunner.RunAsync(args, System.Threading.CancellationToken.None); + + // Assert + result.Should().Be(ExitCodes.Success); + + var installDir = Path.Combine(outputDir, id.ToLowerInvariant(), version); + Directory.Exists(installDir).Should().BeTrue(); + Directory.EnumerateFiles(installDir, "*.nupkg").Any().Should().BeTrue(); + File.Exists(Path.Combine(installDir, $"{id.ToLowerInvariant()}.{version}.nupkg")).Should().BeTrue(); + } + + [Fact] + public async Task RunAsync_NoVersionWhenPrereleaseNotIncluded_PicksLatestStable() + { + using var context = new SimpleTestPathContext(); + + // Arrange + var sourceDir = Path.Combine(context.WorkingDirectory, "src"); + var outputDir = Path.Combine(context.WorkingDirectory, "packages"); + Directory.CreateDirectory(sourceDir); + Directory.CreateDirectory(outputDir); + + var id = "Contoso.Core"; + await SimpleTestPackageUtility.CreateFullPackageAsync(sourceDir, id, "1.0.0"); + await SimpleTestPackageUtility.CreateFullPackageAsync(sourceDir, id, "1.1.0"); + await SimpleTestPackageUtility.CreateFullPackageAsync(sourceDir, id, "2.0.0-beta"); // prerelease + + var logger = new Mock(MockBehavior.Loose); + + var args = new PackageDownloadArgs( + packageId: id, + sources: new[] { sourceDir }.ToList(), + outputDirectory: outputDir, + logger: logger.Object); + + // Act + var result = await PackageDownloadRunner.RunAsync(args, System.Threading.CancellationToken.None); + + // Assert + result.Should().Be(ExitCodes.Success); + + var chosen = Path.Combine(outputDir, id.ToLowerInvariant(), "1.1.0"); + var notChosen = Path.Combine(outputDir, id.ToLowerInvariant(), "2.0.0-beta"); + Directory.Exists(chosen).Should().BeTrue("latest stable (1.1.0) should be chosen"); + Directory.Exists(notChosen).Should().BeFalse("prerelease (2.0.0-beta) should not be chosen"); + File.Exists(Path.Combine(chosen, $"{id.ToLowerInvariant()}.1.1.0.nupkg")).Should().BeTrue(); + } + + [Fact] + public async Task RunAsync_NoVersionWithPrereleaseTrue_PicksHighestIncludingPrerelease() + { + using var context = new SimpleTestPathContext(); + + // Arrange + var sourceDir = Path.Combine(context.WorkingDirectory, "src"); + var outputDir = Path.Combine(context.WorkingDirectory, "packages"); + Directory.CreateDirectory(sourceDir); + Directory.CreateDirectory(outputDir); + + var id = "Contoso.Preview"; + await SimpleTestPackageUtility.CreateFullPackageAsync(sourceDir, id, "1.3.0"); + await SimpleTestPackageUtility.CreateFullPackageAsync(sourceDir, id, "2.0.0-beta.2"); + + var logger = new Mock(MockBehavior.Loose); + + var args = new PackageDownloadArgs( + packageId: id, + sources: new[] { sourceDir }.ToList(), + outputDirectory: outputDir, + logger: logger.Object) + { + IncludePrerelease = true + }; + + // Act + var result = await PackageDownloadRunner.RunAsync(args, System.Threading.CancellationToken.None); + + // Assert + result.Should().Be(ExitCodes.Success); + + var chosen = Path.Combine(outputDir, id.ToLowerInvariant(), "2.0.0-beta.2"); + Directory.Exists(chosen).Should().BeTrue("IncludePrerelease=true should allow picking 2.0.0-beta.2"); + File.Exists(Path.Combine(chosen, $"{id.ToLowerInvariant()}.2.0.0-beta.2.nupkg")).Should().BeTrue(); + } + + [Fact] + public async Task RunAsync_NoVersion_PicksHighestAcrossMultipleSources() + { + using var context = new SimpleTestPathContext(); + + // Arrange + var srcA = Path.Combine(context.WorkingDirectory, "srcA"); + var srcB = Path.Combine(context.WorkingDirectory, "srcB"); + var outputDir = Path.Combine(context.WorkingDirectory, "packages"); + Directory.CreateDirectory(srcA); + Directory.CreateDirectory(srcB); + Directory.CreateDirectory(outputDir); + + var id = "Contoso.Toolkit"; + await SimpleTestPackageUtility.CreateFullPackageAsync(srcA, id, "1.1.0"); + await SimpleTestPackageUtility.CreateFullPackageAsync(srcB, id, "1.2.0"); + + var logger = new Mock(MockBehavior.Loose); + + var args = new PackageDownloadArgs( + packageId: id, + sources: [srcA, srcB], + outputDirectory: outputDir, + logger: logger.Object); + + // Act + var result = await PackageDownloadRunner.RunAsync(args, System.Threading.CancellationToken.None); + + // Assert + result.Should().Be(ExitCodes.Success); + + var chosen = Path.Combine(outputDir, id.ToLowerInvariant(), "1.2.0"); + Directory.Exists(chosen).Should().BeTrue("should choose the highest version found across all sources"); + File.Exists(Path.Combine(chosen, $"{id.ToLowerInvariant()}.1.2.0.nupkg")).Should().BeTrue(); + } + + [Fact] + public async Task RunAsync_ExplicitVersionAlreadyInstalled_ShortCircuitsAndSucceeds() + { + using var context = new SimpleTestPathContext(); + + // Arrange + var sourceDir = Path.Combine(context.WorkingDirectory, "src"); + var outputDir = Path.Combine(context.WorkingDirectory, "packages"); + Directory.CreateDirectory(sourceDir); + Directory.CreateDirectory(outputDir); + + var id = "Contoso.Utils"; + var v = "3.4.5"; + await SimpleTestPackageUtility.CreateFullPackageAsync(sourceDir, id, v); + + // First run: install it + var logger1 = new Mock(MockBehavior.Loose); + var args1 = new PackageDownloadArgs( + packageId: id, + sources: [sourceDir], + outputDirectory: outputDir, + logger: logger1.Object) + { + Version = v + }; + + var first = await PackageDownloadRunner.RunAsync(args1, System.Threading.CancellationToken.None); + first.Should().Be(ExitCodes.Success); + + // Second run: should short-circuit + var logger2 = new Mock(MockBehavior.Loose); + var args2 = new PackageDownloadArgs( + packageId: id, + sources: [sourceDir], + outputDirectory: outputDir, + logger: logger2.Object) + { + Version = v + }; + + // Act + var second = await PackageDownloadRunner.RunAsync(args2, System.Threading.CancellationToken.None); + + // Assert + second.Should().Be(ExitCodes.Success); + + var installDir = Path.Combine(outputDir, id.ToLowerInvariant(), v); + Directory.Exists(installDir).Should().BeTrue(); + File.Exists(Path.Combine(installDir, $"{id.ToLowerInvariant()}.{v}.nupkg")).Should().BeTrue(); + logger2.Verify(l => l.LogMinimal(It.Is(s => + s.Contains("Skipping", System.StringComparison.OrdinalIgnoreCase))), Times.AtLeastOnce); + } + + [Fact] + public async Task RunAsync_WhenAllowInsecureConnectionsFalse_RejectsHttpSource() + { + using var context = new SimpleTestPathContext(); + + var outputDir = Path.Combine(context.WorkingDirectory, "packages"); + Directory.CreateDirectory(outputDir); + + var httpSource = "http://contoso/v3/index.json"; + var logger = new Mock(MockBehavior.Loose); + + var args = new PackageDownloadArgs( + packageId: "Contoso.Lib", + sources: [httpSource], + outputDirectory: outputDir, + logger: logger.Object) + { + ConfigFile = context.NuGetConfig, + AllowInsecureConnections = false, + Verbosity = Verbosity.Detailed + }; + + // Act + var result = await PackageDownloadRunner.RunAsync(args, System.Threading.CancellationToken.None); + + // Assert + result.Should().Be(ExitCodes.Error); + logger.Verify(l => l.LogError(It.Is(s => + s.Contains(httpSource, System.StringComparison.OrdinalIgnoreCase))), Times.AtLeastOnce); + } + + [Fact] + public async Task RunAsync_PackageDoesNotExist_ReturnsError() + { + using var context = new SimpleTestPathContext(); + + // Arrange + var srcA = Path.Combine(context.WorkingDirectory, "emptyA"); + var srcB = Path.Combine(context.WorkingDirectory, "emptyB"); + var outputDir = Path.Combine(context.WorkingDirectory, "packages"); + Directory.CreateDirectory(srcA); + Directory.CreateDirectory(srcB); + Directory.CreateDirectory(outputDir); + + var id = "Missing.Package"; + var v = "9.9.9"; + + var logger = new Mock(MockBehavior.Loose); + + var args = new PackageDownloadArgs( + packageId: id, + sources: [srcA, srcB], + outputDirectory: outputDir, + logger: logger.Object) + { + Version = v, + ConfigFile = context.NuGetConfig, + AllowInsecureConnections = true, + Verbosity = Verbosity.Detailed + }; + + // Act + var result = await PackageDownloadRunner.RunAsync(args, System.Threading.CancellationToken.None); + + // Assert + result.Should().Be(ExitCodes.Error); + logger.Verify(l => l.LogError(It.IsAny()), Times.AtLeastOnce); + + File.Exists(Path.Combine(outputDir, $"{id.ToLowerInvariant()}.{v}.nupkg")).Should().BeFalse("Package does not exist in sources"); + } +} From b4d4d4a43039d568eac2fd4c3dd4543bc3691c3b Mon Sep 17 00:00:00 2001 From: Nigusu Yenework Date: Sun, 28 Sep 2025 12:46:36 -0700 Subject: [PATCH 02/15] More tests --- .../Package/Download/PackageDownloadArgs.cs | 23 + .../Download}/PackageDownloadCommand.cs | 80 +-- .../Package/Download/PackageDownloadRunner.cs | 316 +++++++++ .../PackageDownload/PackageDownloadArgs.cs | 66 -- .../PackageDownload/PackageDownloadLogger.cs | 135 ---- .../PackageDownload/PackageDownloadRunner.cs | 293 --------- .../NuGet.CommandLine.XPlat.csproj | 1 - .../NuGet.CommandLine.XPlat/NuGetCommands.cs | 2 + .../NuGet.CommandLine.XPlat/Program.cs | 3 +- .../Strings.Designer.cs | 9 + .../NuGet.CommandLine.XPlat/Strings.resx | 6 + .../xlf/Strings.cs.xlf | 7 + .../xlf/Strings.de.xlf | 7 + .../xlf/Strings.es.xlf | 7 + .../xlf/Strings.fr.xlf | 7 + .../xlf/Strings.it.xlf | 7 + .../xlf/Strings.ja.xlf | 7 + .../xlf/Strings.ko.xlf | 7 + .../xlf/Strings.pl.xlf | 7 + .../xlf/Strings.pt-BR.xlf | 7 + .../xlf/Strings.ru.xlf | 7 + .../xlf/Strings.tr.xlf | 7 + .../xlf/Strings.zh-Hans.xlf | 7 + .../xlf/Strings.zh-Hant.xlf | 7 + .../Download/PackageDownloadRunnerTests.cs | 600 ++++++++++++++++++ .../PackageDownloadRunnerTests.cs | 294 --------- 26 files changed, 1078 insertions(+), 841 deletions(-) create mode 100644 src/NuGet.Core/NuGet.CommandLine.XPlat/Commands/Package/Download/PackageDownloadArgs.cs rename src/NuGet.Core/NuGet.CommandLine.XPlat/Commands/{PackageDownload => Package/Download}/PackageDownloadCommand.cs (52%) create mode 100644 src/NuGet.Core/NuGet.CommandLine.XPlat/Commands/Package/Download/PackageDownloadRunner.cs delete mode 100644 src/NuGet.Core/NuGet.CommandLine.XPlat/Commands/PackageDownload/PackageDownloadArgs.cs delete mode 100644 src/NuGet.Core/NuGet.CommandLine.XPlat/Commands/PackageDownload/PackageDownloadLogger.cs delete mode 100644 src/NuGet.Core/NuGet.CommandLine.XPlat/Commands/PackageDownload/PackageDownloadRunner.cs create mode 100644 test/NuGet.Core.Tests/NuGet.CommandLine.Xplat.Tests/Commands/Package/Download/PackageDownloadRunnerTests.cs delete mode 100644 test/NuGet.Core.Tests/NuGet.CommandLine.Xplat.Tests/PackageDownloadRunnerTests.cs diff --git a/src/NuGet.Core/NuGet.CommandLine.XPlat/Commands/Package/Download/PackageDownloadArgs.cs b/src/NuGet.Core/NuGet.CommandLine.XPlat/Commands/Package/Download/PackageDownloadArgs.cs new file mode 100644 index 00000000000..9f0e52ed364 --- /dev/null +++ b/src/NuGet.Core/NuGet.CommandLine.XPlat/Commands/Package/Download/PackageDownloadArgs.cs @@ -0,0 +1,23 @@ +// 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. + +using System.Collections.Generic; +using NuGet.Common; + +namespace NuGet.CommandLine.XPlat.Commands.Package.PackageDownload; + +internal class PackageDownloadArgs +{ + public IReadOnlyList Packages { get; set; } + public IList Sources { get; set; } + public string OutputDirectory { get; set; } + public string ConfigFile { get; set; } + + public bool IncludePrerelease { get; set; } + public bool DownloadOnly { get; set; } + public bool AllowInsecureConnections { get; set; } + public bool Interactive { get; set; } + public LogLevel LogLevel { get; set; } +} + +internal enum Verbosity { Quiet, Normal, Detailed } diff --git a/src/NuGet.Core/NuGet.CommandLine.XPlat/Commands/PackageDownload/PackageDownloadCommand.cs b/src/NuGet.Core/NuGet.CommandLine.XPlat/Commands/Package/Download/PackageDownloadCommand.cs similarity index 52% rename from src/NuGet.Core/NuGet.CommandLine.XPlat/Commands/PackageDownload/PackageDownloadCommand.cs rename to src/NuGet.Core/NuGet.CommandLine.XPlat/Commands/Package/Download/PackageDownloadCommand.cs index 0969cf218a1..900769d12e2 100644 --- a/src/NuGet.Core/NuGet.CommandLine.XPlat/Commands/PackageDownload/PackageDownloadCommand.cs +++ b/src/NuGet.Core/NuGet.CommandLine.XPlat/Commands/Package/Download/PackageDownloadCommand.cs @@ -5,14 +5,18 @@ using System.Collections.Generic; using System.CommandLine; using System.CommandLine.Help; -using NuGet.CommandLine.XPlat.Commands; -using NuGet.CommandLine.XPlat.Commands.PackageDownload; +using System.Threading; +using System.Threading.Tasks; -namespace NuGet.CommandLine.XPlat +namespace NuGet.CommandLine.XPlat.Commands.Package.PackageDownload { internal class PackageDownloadCommand { - public static void Register(Command rootCommand, Func getLogger) + internal static void Register(Command packageCommand, Option interactiveOption) + { + Register(packageCommand, interactiveOption, PackageDownloadRunner.RunAsync); + } + public static void Register(Command packageCommand, Option interactiveOption, Func> action) { var downloadCommand = new DocumentedCommand( "download", @@ -20,10 +24,11 @@ public static void Register(Command rootCommand, Func getLogge "https://aka.ms/dotnet/package/download"); // Arguments - var packageId = new Argument("PackageId") + var packagesArguments = new Argument>("packages") { - Description = Strings.pkgDownload_packageIdDescription, - Arity = ArgumentArity.ExactlyOne, + Description = Strings.PackageUpdate_PackageArgumentDescription, + Arity = ArgumentArity.ZeroOrMore, + CustomParser = Package.Parse }; // Options @@ -50,12 +55,6 @@ public static void Register(Command rootCommand, Func getLogge Arity = ArgumentArity.Zero }; - var interactive = new Option("--interactive") - { - Description = Strings.pkgDownload_interactiveDecription, - Arity = ArgumentArity.Zero - }; - var outputDirectory = new Option("--output-directory") { Description = Strings.pkgDownload_OutputDirectoryDescription, @@ -74,59 +73,40 @@ public static void Register(Command rootCommand, Func getLogge Arity = ArgumentArity.OneOrMore }; - var verbosity = new Option("--verbosity") - { - Description = Strings.pkgDownload_verbosityDescription, - Arity = ArgumentArity.ExactlyOne - }; + var verbosity = CommonOptions.GetVerbosityOption(); - var version = new Option("--version") - { - Description = Strings.pkgDownload_versionDescription, - Arity = ArgumentArity.ExactlyOne - }; - - downloadCommand.Arguments.Add(packageId); + downloadCommand.Arguments.Add(packagesArguments); downloadCommand.Options.Add(allowInsecureConnections); downloadCommand.Options.Add(configFile); downloadCommand.Options.Add(downloadOnly); downloadCommand.Options.Add(help); - downloadCommand.Options.Add(interactive); + downloadCommand.Options.Add(interactiveOption); downloadCommand.Options.Add(outputDirectory); downloadCommand.Options.Add(prerelease); downloadCommand.Options.Add(sources); downloadCommand.Options.Add(verbosity); - downloadCommand.Options.Add(version); downloadCommand.SetAction(async (parserResult, cancellationToken) => { - ILoggerWithColor logger = getLogger(); - - try - { - var args = new PackageDownloadArgs(parserResult.GetValue(packageId), parserResult.GetValue(sources), parserResult.GetValue(outputDirectory), logger) - { - Version = parserResult.GetValue(version), - IncludePrerelease = parserResult.GetValue(prerelease), - DownloadOnly = parserResult.GetValue(downloadOnly), - AllowInsecureConnections = parserResult.GetValue(allowInsecureConnections), - Interactive = parserResult.GetValue(interactive), - ConfigFile = parserResult.GetValue(configFile) - }; - - args.SetVerbosity(parserResult.GetValue(verbosity)); - - return await PackageDownloadRunner.RunAsync(args, cancellationToken); - } - catch (ArgumentException ex) + IReadOnlyList packages = parserResult.GetValue(packagesArguments) ?? []; + var args = new PackageDownloadArgs() { - logger.LogError(ex.Message); - return ExitCodes.InvalidArguments; - } + Packages = packages, + Sources = parserResult.GetValue(sources), + OutputDirectory = parserResult.GetValue(outputDirectory), + IncludePrerelease = parserResult.GetValue(prerelease), + DownloadOnly = parserResult.GetValue(downloadOnly), + AllowInsecureConnections = parserResult.GetValue(allowInsecureConnections), + Interactive = parserResult.GetValue(interactiveOption), + ConfigFile = parserResult.GetValue(configFile), + LogLevel = (parserResult.GetValue(verbosity) ?? VerbosityEnum.normal).ToLogLevel() + }; + + return await action(args, cancellationToken); }); - rootCommand.Subcommands.Add(downloadCommand); + packageCommand.Subcommands.Add(downloadCommand); } } } diff --git a/src/NuGet.Core/NuGet.CommandLine.XPlat/Commands/Package/Download/PackageDownloadRunner.cs b/src/NuGet.Core/NuGet.CommandLine.XPlat/Commands/Package/Download/PackageDownloadRunner.cs new file mode 100644 index 00000000000..e77215eced2 --- /dev/null +++ b/src/NuGet.Core/NuGet.CommandLine.XPlat/Commands/Package/Download/PackageDownloadRunner.cs @@ -0,0 +1,316 @@ +// 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. + +using System; +using System.Collections.Generic; +using System.Globalization; +using System.IO; +using System.Linq; +using System.Threading; +using System.Threading.Tasks; +using NuGet.Commands; +using NuGet.Configuration; +using NuGet.Credentials; +using NuGet.Packaging; +using NuGet.Packaging.Core; +using NuGet.Packaging.PackageExtraction; +using NuGet.Packaging.Signing; +using NuGet.Protocol; +using NuGet.Protocol.Core.Types; +using NuGet.Repositories; +using NuGet.Versioning; + +namespace NuGet.CommandLine.XPlat.Commands.Package.PackageDownload +{ + internal static class PackageDownloadRunner + { + internal const int ExitCodeError = 1; + internal const int ExitCodeSuccess = 0; + + public static async Task RunAsync(PackageDownloadArgs args, CancellationToken token) + { + ILoggerWithColor logger = new CommandOutputLogger(args.LogLevel) + { + HidePrefixForInfoAndMinimal = true + }; + + // Custom logger for package downloads. + // It wraps the provided logger and uses verbosity levels to determine which messages to log. + XPlatUtility.ConfigureProtocol(); + DefaultCredentialServiceUtility.SetupDefaultCredentialService(logger, !args.Interactive); + ISettings settings = Settings.LoadDefaultSettings( + Directory.GetCurrentDirectory(), + args.ConfigFile, + new XPlatMachineWideSetting()); + PackageSourceProvider sourceProvider = new PackageSourceProvider(settings); + var packageSources = GetPackageSources(args.Sources, sourceProvider); + + return await RunAsync(args, logger, packageSources, settings, token); + } + + public static async Task RunAsync(PackageDownloadArgs args, ILoggerWithColor logger, IEnumerable packageSources, ISettings settings, CancellationToken token) + { + // Check for insecure sources + if (!args.AllowInsecureConnections) + { + var insecureSources = packageSources.Where(source => source.SourceUri.AbsoluteUri.StartsWith("http://", StringComparison.OrdinalIgnoreCase)); + + if (insecureSources.Any()) + { + if (insecureSources.Count() == 1) + { + logger.LogError(string.Format( + CultureInfo.CurrentCulture, + Strings.Error_HttpServerUsage, + "download", + insecureSources.First())); + } + else + { + logger.LogError(string.Format( + CultureInfo.CurrentCulture, + Strings.Error_HttpServerUsage_MultipleSources, + "download", + string.Join(", ", insecureSources))); + } + + return ExitCodeError; + } + } + + var cache = new SourceCacheContext() + { + NoCache = true, + DirectDownload = true + }; + + bool downloadedAllSuccessfully = true; + + foreach (var package in args.Packages) + { + logger.LogMinimal(string.Format( + CultureInfo.CurrentCulture, + Strings.PkgDownload_Starting, + package.Id, + string.IsNullOrEmpty(package.VersionRange?.ToNormalizedString()) ? "latest version" : package.VersionRange.ToNormalizedString())); + +#pragma warning disable CA1031 // Do not catch general exception types + try + { + (NuGetVersion version, SourceRepository downloadRepository) = await ResolvePackageDownloadVersion(package, packageSources, cache, logger, args.IncludePrerelease, token); + + if (version == null) + { + // Unable to find a valid version + downloadedAllSuccessfully &= false; + continue; + } + + bool download = await InstallPackageAsync( + package.Id, + version, + downloadRepository, + cache, + settings, + args.OutputDirectory, + logger, + token); + + if (download) + { + logger.LogMinimal(string.Format( + CultureInfo.CurrentCulture, + Strings.PkgDownload_Succeeded, + package.Id, + version, + args.OutputDirectory)); + } + else + { + logger.LogError(string.Format( + CultureInfo.CurrentCulture, + Strings.PkgDownload_Failed, + package.Id, + version)); + + downloadedAllSuccessfully &= false; + } + } + catch (Exception ex) + { + logger.LogError(ex.ToString()); + downloadedAllSuccessfully &= false; + } +#pragma warning restore CA1031 // Do not catch general exception types + } + + return downloadedAllSuccessfully ? ExitCodeSuccess : ExitCodeError; + } + + internal static async Task<(NuGetVersion, SourceRepository)> ResolvePackageDownloadVersion( + Package package, + IEnumerable packageSources, + SourceCacheContext cache, + ILoggerWithColor logger, + bool includePrerelease, + CancellationToken token) + { + // Determine if an exact version is requested. + // If the original string contains a comma, it's a range. + bool isExactVersionRequested = + package.VersionRange != null && + !string.IsNullOrEmpty(package.VersionRange.OriginalString) && + !package.VersionRange.OriginalString.Contains(","); + NuGetVersion requestedExactVersion = package.VersionRange?.MinVersion; + + NuGetVersion versionToDownload = null; + SourceRepository downloadSourceRepository = null; + + bool pickLatest = false; + + if (package.VersionRange == null) + { + // If the version is not defined, pick the latest + pickLatest = true; + } + + foreach (var source in packageSources) + { + var repo = Repository.Factory.GetCoreV3(source); + var finder = await repo.GetResourceAsync(token); + var versions = await finder.GetAllVersionsAsync(package.Id, cache, logger, token); + + if (isExactVersionRequested) + { + // If an exact version is requested, check if the version exists at this source + if (versions != null && versions.Contains(requestedExactVersion)) + { + versionToDownload = requestedExactVersion; + downloadSourceRepository = repo; + return (versionToDownload, downloadSourceRepository); + } + else + { + // continue to the next source + continue; + } + } + + // If a version range is specified, find the best match at this source + var candidates = versions? + .Where(v => (package.VersionRange == null || package.VersionRange.Satisfies(v)) && (includePrerelease || !v.IsPrerelease)) + .OrderByDescending(v => v); + + var candidate = pickLatest ? candidates?.FirstOrDefault() : candidates?.LastOrDefault(); + + if (candidate != null && (versionToDownload == null || pickLatest ? candidate > versionToDownload : candidate < versionToDownload)) + { + versionToDownload = candidate; + downloadSourceRepository = repo; + } + } + + if (versionToDownload == null) + { + logger.LogError("Unable to find a valid package version"); + } + + return (versionToDownload, downloadSourceRepository); + } + + private static async Task InstallPackageAsync( + string id, + NuGetVersion version, + SourceRepository repo, + SourceCacheContext cache, + ISettings settings, + string outputDirectory, + Common.ILogger logger, + CancellationToken token) + { + var extractionContext = new PackageExtractionContext( + PackageSaveMode.Defaultv3, + PackageExtractionBehavior.XmlDocFileSaveMode, + ClientPolicyContext.GetClientPolicy(settings, logger), + logger); + + var resolver = new VersionFolderPathResolver(outputDirectory); + var userPackageFolder = new NuGetv3LocalRepository(outputDirectory); + + // no-op if already installed + if (userPackageFolder.Exists(id, version)) + { + logger.LogMinimal(string.Format( + CultureInfo.CurrentCulture, + Strings.PkgDownload_AlreadyInstalled, + id, + version.ToNormalizedString(), + outputDirectory)); + + return true; + } + + try + { + var finder = await repo.GetResourceAsync(token); + bool installed = await PackageExtractor.InstallFromSourceAsync( + repo.PackageSource.Source, + new PackageIdentity(id, version), + async (destination) => + { + using var nupkg = new MemoryStream(); + bool ok = await finder.CopyNupkgToStreamAsync( + id, version, nupkg, cache, logger, token); + + if (!ok) throw new InvalidOperationException("Package not found."); + + nupkg.Position = 0; + await nupkg.CopyToAsync(destination, 81920); + }, + resolver, + extractionContext, + token); + + if (installed) + { + return true; + } + } + catch (InvalidOperationException) + { + // Unable to download the package + logger.LogError(string.Format( + CultureInfo.CurrentCulture, + Strings.PkgDownload_UnableToDownload, + id, + version.ToNormalizedString(), + repo.PackageSource.Source)); + return false; + } + + return false; + } + + private static IEnumerable GetPackageSources(IList sources, IPackageSourceProvider sourceProvider) + { + IEnumerable configuredSources = sourceProvider.LoadPackageSources() + .Where(s => s.IsEnabled); + + IEnumerable packageSources; + + if (sources != null && sources.Count > 0) + { + // Use sources specified on command line + packageSources = sources + .Select(s => PackageSourceProviderExtensions.ResolveSource(configuredSources, s)); + } + else + { + // Use all configured sources + packageSources = configuredSources; + } + + return packageSources; + } + } +} diff --git a/src/NuGet.Core/NuGet.CommandLine.XPlat/Commands/PackageDownload/PackageDownloadArgs.cs b/src/NuGet.Core/NuGet.CommandLine.XPlat/Commands/PackageDownload/PackageDownloadArgs.cs deleted file mode 100644 index f005286b5cc..00000000000 --- a/src/NuGet.Core/NuGet.CommandLine.XPlat/Commands/PackageDownload/PackageDownloadArgs.cs +++ /dev/null @@ -1,66 +0,0 @@ -// 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. - -using System; -using System.Collections.Generic; -using System.Globalization; -using NuGet.Common; - -namespace NuGet.CommandLine.XPlat.Commands.PackageDownload; - -internal class PackageDownloadArgs -{ - public PackageDownloadArgs(string packageId, IList sources, string outputDirectory, ILogger logger) - { - PackageId = packageId; - Sources = sources; - OutputDirectory = outputDirectory; - Logger = logger; - } - - public string PackageId { get; set; } - public string Version { get; set; } - public IList Sources { get; set; } - public string OutputDirectory { get; set; } - public string ConfigFile { get; set; } - - public bool IncludePrerelease { get; set; } - public bool DownloadOnly { get; set; } - public bool AllowInsecureConnections { get; set; } - public bool Interactive { get; set; } - - private Verbosity _verbosity = Verbosity.Normal; - public Verbosity Verbosity - { - get => _verbosity; - set => _verbosity = value; - } - - public ILogger Logger { get; set; } - - public void SetVerbosity(string level) - { - if (string.IsNullOrWhiteSpace(level)) - { - _verbosity = Verbosity.Normal; - return; - } - - switch (level.Trim().ToLowerInvariant()) - { - case "quiet": - _verbosity = Verbosity.Quiet; - break; - case "normal": - _verbosity = Verbosity.Normal; - break; - case "detailed": - _verbosity = Verbosity.Detailed; - break; - default: - throw new ArgumentException(string.Format(CultureInfo.CurrentCulture, Strings.Error_InvalidVerbosity, level)); - } - } -} - -internal enum Verbosity { Quiet, Normal, Detailed } diff --git a/src/NuGet.Core/NuGet.CommandLine.XPlat/Commands/PackageDownload/PackageDownloadLogger.cs b/src/NuGet.Core/NuGet.CommandLine.XPlat/Commands/PackageDownload/PackageDownloadLogger.cs deleted file mode 100644 index d832b1c0132..00000000000 --- a/src/NuGet.Core/NuGet.CommandLine.XPlat/Commands/PackageDownload/PackageDownloadLogger.cs +++ /dev/null @@ -1,135 +0,0 @@ -// 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. - -using System; -using System.Threading.Tasks; -using NuGet.Common; - -namespace NuGet.CommandLine.XPlat.Commands.PackageDownload -{ - internal sealed class PackageDownloadLogger : ILogger - { - private readonly ILogger _primary; - private readonly Verbosity _verbosity; - - public PackageDownloadLogger( - ILogger primary, - Verbosity verbosity) - { - _primary = primary ?? throw new ArgumentNullException(nameof(primary)); - _verbosity = verbosity; - } - - public void LogDebug(string data) - { - if (_verbosity == Verbosity.Detailed) - { - _primary.LogDebug(data); - } - } - public void LogVerbose(string data) - { - if (_verbosity == Verbosity.Detailed) - { - _primary.LogVerbose(data); - } - } - public void LogInformation(string data) - { - if (_verbosity == Verbosity.Detailed) - { - _primary.LogInformation(data); - } - } - public void LogMinimal(string data) - { - if (_verbosity != Verbosity.Quiet) - { - _primary.LogMinimal(data); - } - } - public void LogWarning(string data) - { - if (_verbosity != Verbosity.Quiet) - { - _primary.LogWarning(data); - } - } - public void LogError(string data) - { - _primary.LogError(data); - } - - public void LogInformationSummary(string data) - { - if (_verbosity == Verbosity.Detailed) - { - _primary.LogInformationSummary(data); - } - } - - public void Log(LogLevel level, string data) - { - if (_verbosity == Verbosity.Quiet) - { - if (level == LogLevel.Error) - { - _primary.Log(level, data); - } - } - else if (_verbosity == Verbosity.Normal) - { - if (level == LogLevel.Error - || level == LogLevel.Warning - || level == LogLevel.Minimal - || level == LogLevel.Information) - { - _primary.Log(level, data); - } - } - else - { - _primary.Log(level, data); - } - } - public Task LogAsync(LogLevel level, string data) - { - if (_verbosity == Verbosity.Quiet) - { - if (level == LogLevel.Error) - { - return _primary.LogAsync(level, data); - } - - return Task.CompletedTask; - } - else if (_verbosity == Verbosity.Normal) - { - if (level == LogLevel.Error - || level == LogLevel.Warning - || level == LogLevel.Minimal - || level == LogLevel.Information) - { - return _primary.LogAsync(level, data); - } - - return Task.CompletedTask; - } - else - { - return _primary.LogAsync(level, data); - } - } - - public void Log(ILogMessage message) - { - Log(message.Level, message.Message); - } - - public Task LogAsync(ILogMessage message) - { - return LogAsync(message.Level, message.Message); - } - } -} - diff --git a/src/NuGet.Core/NuGet.CommandLine.XPlat/Commands/PackageDownload/PackageDownloadRunner.cs b/src/NuGet.Core/NuGet.CommandLine.XPlat/Commands/PackageDownload/PackageDownloadRunner.cs deleted file mode 100644 index f120503f16b..00000000000 --- a/src/NuGet.Core/NuGet.CommandLine.XPlat/Commands/PackageDownload/PackageDownloadRunner.cs +++ /dev/null @@ -1,293 +0,0 @@ -// 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. - -using System; -using System.Collections.Generic; -using System.Globalization; -using System.IO; -using System.Linq; -using System.Threading; -using System.Threading.Tasks; -using NuGet.Commands; -using NuGet.Configuration; -using NuGet.Credentials; -using NuGet.Packaging; -using NuGet.Packaging.Core; -using NuGet.Packaging.PackageExtraction; -using NuGet.Packaging.Signing; -using NuGet.Protocol; -using NuGet.Protocol.Core.Types; -using NuGet.Repositories; -using NuGet.Versioning; - -namespace NuGet.CommandLine.XPlat.Commands.PackageDownload -{ - internal static class PackageDownloadRunner - { - public static async Task RunAsync(PackageDownloadArgs args, CancellationToken token) - { - // Custom logger for package downloads. - // It wraps the provided logger and uses verbosity levels to determine which messages to log. - var logger = new PackageDownloadLogger(args.Logger, args.Verbosity); - - DefaultCredentialServiceUtility.SetupDefaultCredentialService(logger, !args.Interactive); - ISettings settings = Settings.LoadDefaultSettings( - Directory.GetCurrentDirectory(), - args.ConfigFile, - new XPlatMachineWideSetting()); - PackageSourceProvider sourceProvider = new PackageSourceProvider(settings); - var packageSources = GetPackageSources(args.Sources, sourceProvider); - - // Check for insecure sources - if (!args.AllowInsecureConnections) - { - var insecureSources = packageSources.Where(source => source.SourceUri.AbsoluteUri.StartsWith("http://", StringComparison.OrdinalIgnoreCase)); - - if (insecureSources.Any()) - { - if (insecureSources.Count() == 1) - { - logger.LogError(string.Format( - CultureInfo.CurrentCulture, - Strings.Error_HttpServerUsage, - "download", - insecureSources.First())); - } - else - { - logger.LogError(string.Format( - CultureInfo.CurrentCulture, - Strings.Error_HttpServerUsage_MultipleSources, - "download", - string.Join(", ", insecureSources))); - } - - return ExitCodes.Error; - } - } - - var cache = new SourceCacheContext() - { - NoCache = true, - DirectDownload = true - }; - -#pragma warning disable CA1031 // Do not catch general exception types - try - { - logger.LogMinimal(string.Format( - CultureInfo.CurrentCulture, - Strings.PkgDownload_Starting, - args.PackageId, - string.IsNullOrEmpty(args.Version) ? "latest version" : args.Version)); - - bool download = await InstallPackageAsync( - packageSources, - cache, - args, - settings, - logger, - token); - - if (download) - { - logger.LogMinimal(string.Format( - CultureInfo.CurrentCulture, - Strings.PkgDownload_Succeeded, - args.PackageId, - string.IsNullOrEmpty(args.Version) ? "latest version" : args.Version, - args.OutputDirectory)); - - return ExitCodes.Success; - } - else - { - logger.LogError(string.Format( - CultureInfo.CurrentCulture, - Strings.PkgDownload_Failed, - args.PackageId, - string.IsNullOrEmpty(args.Version) ? "latest version" : args.Version)); - - return ExitCodes.Error; - } - } - catch (Exception ex) - { - logger.LogError(ex.ToString()); - return ExitCodes.Error; - } -#pragma warning restore CA1031 // Do not catch general exception types - } - - private static async Task InstallPackageAsync( - IEnumerable sources, - SourceCacheContext cache, - PackageDownloadArgs args, - ISettings settings, - Common.ILogger logger, - CancellationToken token) - { - bool versionDefined = !string.IsNullOrEmpty(args.Version); - NuGetVersion versionToDownload = null; - SourceRepository downloadSourceRepository = null; - - if (versionDefined) - { - versionToDownload = NuGetVersion.Parse(args.Version); - } - - var extractionContext = new PackageExtractionContext( - PackageSaveMode.Defaultv3, - PackageExtractionBehavior.XmlDocFileSaveMode, - ClientPolicyContext.GetClientPolicy(settings, logger), - logger); - - var resolver = new VersionFolderPathResolver(args.OutputDirectory); - var userPackageFolder = new NuGetv3LocalRepository(args.OutputDirectory); - - // no-op if already installed - if (versionDefined && userPackageFolder.Exists(args.PackageId, versionToDownload)) - { - logger.LogMinimal(string.Format( - CultureInfo.CurrentCulture, - Strings.PkgDownload_AlreadyInstalled, - args.PackageId, - versionToDownload.ToNormalizedString(), - args.OutputDirectory)); - - return true; - } - - // Try each source until we can download and install the requested version - foreach (var source in sources) - { - var repo = Repository.Factory.GetCoreV3(source); - var finder = await repo.GetResourceAsync(token); - - if (versionDefined) - { - // download the defined version at the first source that has it - PackageIdentity identity = new PackageIdentity(args.PackageId, versionToDownload); - - try - { - bool installed = await PackageExtractor.InstallFromSourceAsync( - source.Source, - identity, - async (destination) => - { - using var nupkg = new MemoryStream(); - bool ok = await finder.CopyNupkgToStreamAsync( - identity.Id, identity.Version, nupkg, cache, logger, token); - - if (!ok) throw new InvalidOperationException("Package not found."); - - nupkg.Position = 0; - await nupkg.CopyToAsync(destination, 81920); - }, - resolver, - extractionContext, - token); - - if (installed) - { - return true; - } - - continue; - } - catch (InvalidOperationException) - { - // Package not found at this source, try the next one - continue; - } - } - else - { - // If the version is not defined - // update the version to download value to be the latest - var versions = await finder.GetAllVersionsAsync(args.PackageId, cache, logger, token); - var candidate = versions? - .Where(v => args.IncludePrerelease || !v.IsPrerelease) - .OrderByDescending(v => v) - .FirstOrDefault(); - - if (candidate != null && (versionToDownload == null || candidate > versionToDownload)) - { - versionToDownload = candidate; - downloadSourceRepository = repo; - } - } - } - - // If the version was not defined, and we found a version across one of the sources, install it now - if (!versionDefined && versionToDownload != null && downloadSourceRepository != null) - { - // no-op if already installed - if (userPackageFolder.Exists(args.PackageId, versionToDownload)) - { - logger.LogInformation(string.Format( - CultureInfo.CurrentCulture, - Strings.PkgDownload_AlreadyInstalled, - args.PackageId, - versionToDownload.ToNormalizedString(), - args.OutputDirectory)); - - return true; - } - - // download the latest version found across all sources - PackageIdentity identity = new PackageIdentity(args.PackageId, versionToDownload); - var findPackageByIdResource = await downloadSourceRepository.GetResourceAsync(token); - - try - { - return await PackageExtractor.InstallFromSourceAsync( - downloadSourceRepository.PackageSource.Source, - identity, - async (destination) => - { - using var nupkg = new MemoryStream(); - bool ok = await findPackageByIdResource.CopyNupkgToStreamAsync( - identity.Id, identity.Version, nupkg, cache, logger, token); - if (!ok) throw new InvalidOperationException("Package not found."); - nupkg.Position = 0; - await nupkg.CopyToAsync(destination, 81920); - }, - resolver, - extractionContext, - token); - } - catch (InvalidOperationException) - { - // install failed - return false; - } - } - - return false; - } - - private static IEnumerable GetPackageSources(IList sources, IPackageSourceProvider sourceProvider) - { - IEnumerable configuredSources = sourceProvider.LoadPackageSources() - .Where(s => s.IsEnabled); - - IEnumerable packageSources; - - if (sources != null && sources.Count > 0) - { - // Use sources specified on command line - packageSources = sources - .Select(s => PackageSourceProviderExtensions.ResolveSource(configuredSources, s)); - } - else - { - // Use all configured sources - packageSources = configuredSources; - } - - return packageSources; - } - } -} diff --git a/src/NuGet.Core/NuGet.CommandLine.XPlat/NuGet.CommandLine.XPlat.csproj b/src/NuGet.Core/NuGet.CommandLine.XPlat/NuGet.CommandLine.XPlat.csproj index 7be310985ad..850e30c7035 100644 --- a/src/NuGet.Core/NuGet.CommandLine.XPlat/NuGet.CommandLine.XPlat.csproj +++ b/src/NuGet.Core/NuGet.CommandLine.XPlat/NuGet.CommandLine.XPlat.csproj @@ -12,7 +12,6 @@ - diff --git a/src/NuGet.Core/NuGet.CommandLine.XPlat/NuGetCommands.cs b/src/NuGet.Core/NuGet.CommandLine.XPlat/NuGetCommands.cs index f9a6a037c49..62c00c79bd9 100644 --- a/src/NuGet.Core/NuGet.CommandLine.XPlat/NuGetCommands.cs +++ b/src/NuGet.Core/NuGet.CommandLine.XPlat/NuGetCommands.cs @@ -4,6 +4,7 @@ using System; using System.CommandLine; using System.Linq; +using NuGet.CommandLine.XPlat.Commands.Package.PackageDownload; using NuGet.CommandLine.XPlat.Commands.Package.Update; namespace NuGet.CommandLine.XPlat; @@ -30,6 +31,7 @@ public static void Add(RootCommand rootCommand, Option interactiveOption) } PackageUpdateCommand.Register(packageCommand, interactiveOption); + PackageDownloadCommand.Register(packageCommand, interactiveOption); } // To delete once the SDK starts using the other overload. Joys of public APIs. diff --git a/src/NuGet.Core/NuGet.CommandLine.XPlat/Program.cs b/src/NuGet.Core/NuGet.CommandLine.XPlat/Program.cs index 0269fc44435..f625524ad53 100644 --- a/src/NuGet.Core/NuGet.CommandLine.XPlat/Program.cs +++ b/src/NuGet.Core/NuGet.CommandLine.XPlat/Program.cs @@ -97,7 +97,6 @@ public static int MainInternal(string[] args, CommandOutputLogger log, IEnvironm { var packageCommand = new Command("package"); rootCommand.Subcommands.Add(packageCommand); - PackageDownloadCommand.Register(packageCommand, getHidePrefixLogger); PackageSearchCommand.Register(packageCommand, getHidePrefixLogger); PackageUpdateCommand.Register(packageCommand, interactiveOption); @@ -247,7 +246,7 @@ private static bool IsSystemCommandLineParsedCommand(string[] args) if (args.Length >= 2 && arg0 == "package") { string arg1 = args[1]; - if (arg1 == "search" || arg1 == "update" || arg1 == "download") + if (arg1 == "search" || arg1 == "update") { return true; } diff --git a/src/NuGet.Core/NuGet.CommandLine.XPlat/Strings.Designer.cs b/src/NuGet.Core/NuGet.CommandLine.XPlat/Strings.Designer.cs index e551b4cb7b1..b57132df8bc 100644 --- a/src/NuGet.Core/NuGet.CommandLine.XPlat/Strings.Designer.cs +++ b/src/NuGet.Core/NuGet.CommandLine.XPlat/Strings.Designer.cs @@ -1885,6 +1885,15 @@ internal static string PkgDownload_Succeeded { } } + /// + /// Looks up a localized string similar to Package download failed for `{0} {1}` from source `{2}`.. + /// + internal static string PkgDownload_UnableToDownload { + get { + return ResourceManager.GetString("PkgDownload_UnableToDownload", resourceCulture); + } + } + /// /// Looks up a localized string similar to Set the verbosity level. Allowed values: quiet | normal | detailed.. /// diff --git a/src/NuGet.Core/NuGet.CommandLine.XPlat/Strings.resx b/src/NuGet.Core/NuGet.CommandLine.XPlat/Strings.resx index 5cc28cc4c66..7c35466b93d 100644 --- a/src/NuGet.Core/NuGet.CommandLine.XPlat/Strings.resx +++ b/src/NuGet.Core/NuGet.CommandLine.XPlat/Strings.resx @@ -1169,4 +1169,10 @@ Do not translate "PackageVersion" 0 - verbosity level do not translate: quiet, normal, detailed. + + Package download failed for `{0} {1}` from source `{2}`. + 0 package id +1 package version +2 package source + \ No newline at end of file diff --git a/src/NuGet.Core/NuGet.CommandLine.XPlat/xlf/Strings.cs.xlf b/src/NuGet.Core/NuGet.CommandLine.XPlat/xlf/Strings.cs.xlf index 051c022b416..5c317e7a031 100644 --- a/src/NuGet.Core/NuGet.CommandLine.XPlat/xlf/Strings.cs.xlf +++ b/src/NuGet.Core/NuGet.CommandLine.XPlat/xlf/Strings.cs.xlf @@ -1015,6 +1015,13 @@ Další informace najdete tady: https://docs.nuget.org/docs/reference/command-li 0 - package id 1 - package version 2 - package download location + + + Package download failed for `{0} {1}` from source `{2}`. + Package download failed for `{0} {1}` from source `{2}`. + 0 package id +1 package version +2 package source There are no stable versions available, {0} is the best available. Consider adding the --prerelease option diff --git a/src/NuGet.Core/NuGet.CommandLine.XPlat/xlf/Strings.de.xlf b/src/NuGet.Core/NuGet.CommandLine.XPlat/xlf/Strings.de.xlf index d2aca5850bc..54b6c05bcf2 100644 --- a/src/NuGet.Core/NuGet.CommandLine.XPlat/xlf/Strings.de.xlf +++ b/src/NuGet.Core/NuGet.CommandLine.XPlat/xlf/Strings.de.xlf @@ -1015,6 +1015,13 @@ Weitere Informationen finden Sie unter: https://docs.nuget.org/docs/reference/co 0 - package id 1 - package version 2 - package download location + + + Package download failed for `{0} {1}` from source `{2}`. + Package download failed for `{0} {1}` from source `{2}`. + 0 package id +1 package version +2 package source There are no stable versions available, {0} is the best available. Consider adding the --prerelease option diff --git a/src/NuGet.Core/NuGet.CommandLine.XPlat/xlf/Strings.es.xlf b/src/NuGet.Core/NuGet.CommandLine.XPlat/xlf/Strings.es.xlf index 7db2e77f484..a019c16d433 100644 --- a/src/NuGet.Core/NuGet.CommandLine.XPlat/xlf/Strings.es.xlf +++ b/src/NuGet.Core/NuGet.CommandLine.XPlat/xlf/Strings.es.xlf @@ -1015,6 +1015,13 @@ Para obtener más información, visite https://docs.nuget.org/docs/reference/com 0 - package id 1 - package version 2 - package download location + + + Package download failed for `{0} {1}` from source `{2}`. + Package download failed for `{0} {1}` from source `{2}`. + 0 package id +1 package version +2 package source There are no stable versions available, {0} is the best available. Consider adding the --prerelease option diff --git a/src/NuGet.Core/NuGet.CommandLine.XPlat/xlf/Strings.fr.xlf b/src/NuGet.Core/NuGet.CommandLine.XPlat/xlf/Strings.fr.xlf index f7c552cb6c0..178e6eef084 100644 --- a/src/NuGet.Core/NuGet.CommandLine.XPlat/xlf/Strings.fr.xlf +++ b/src/NuGet.Core/NuGet.CommandLine.XPlat/xlf/Strings.fr.xlf @@ -1015,6 +1015,13 @@ Pour plus d'informations, visitez https://docs.nuget.org/docs/reference/command- 0 - package id 1 - package version 2 - package download location + + + Package download failed for `{0} {1}` from source `{2}`. + Package download failed for `{0} {1}` from source `{2}`. + 0 package id +1 package version +2 package source There are no stable versions available, {0} is the best available. Consider adding the --prerelease option diff --git a/src/NuGet.Core/NuGet.CommandLine.XPlat/xlf/Strings.it.xlf b/src/NuGet.Core/NuGet.CommandLine.XPlat/xlf/Strings.it.xlf index 2a74ff04f00..c02e5b11ee1 100644 --- a/src/NuGet.Core/NuGet.CommandLine.XPlat/xlf/Strings.it.xlf +++ b/src/NuGet.Core/NuGet.CommandLine.XPlat/xlf/Strings.it.xlf @@ -1015,6 +1015,13 @@ Per altre informazioni, vedere https://docs.nuget.org/docs/reference/command-lin 0 - package id 1 - package version 2 - package download location + + + Package download failed for `{0} {1}` from source `{2}`. + Package download failed for `{0} {1}` from source `{2}`. + 0 package id +1 package version +2 package source There are no stable versions available, {0} is the best available. Consider adding the --prerelease option diff --git a/src/NuGet.Core/NuGet.CommandLine.XPlat/xlf/Strings.ja.xlf b/src/NuGet.Core/NuGet.CommandLine.XPlat/xlf/Strings.ja.xlf index 63b6d8f1289..5b16081bf79 100644 --- a/src/NuGet.Core/NuGet.CommandLine.XPlat/xlf/Strings.ja.xlf +++ b/src/NuGet.Core/NuGet.CommandLine.XPlat/xlf/Strings.ja.xlf @@ -1015,6 +1015,13 @@ For more information, visit https://docs.nuget.org/docs/reference/command-line-r 0 - package id 1 - package version 2 - package download location + + + Package download failed for `{0} {1}` from source `{2}`. + Package download failed for `{0} {1}` from source `{2}`. + 0 package id +1 package version +2 package source There are no stable versions available, {0} is the best available. Consider adding the --prerelease option diff --git a/src/NuGet.Core/NuGet.CommandLine.XPlat/xlf/Strings.ko.xlf b/src/NuGet.Core/NuGet.CommandLine.XPlat/xlf/Strings.ko.xlf index 721c4ac7173..3e4b9b12566 100644 --- a/src/NuGet.Core/NuGet.CommandLine.XPlat/xlf/Strings.ko.xlf +++ b/src/NuGet.Core/NuGet.CommandLine.XPlat/xlf/Strings.ko.xlf @@ -1015,6 +1015,13 @@ For more information, visit https://docs.nuget.org/docs/reference/command-line-r 0 - package id 1 - package version 2 - package download location + + + Package download failed for `{0} {1}` from source `{2}`. + Package download failed for `{0} {1}` from source `{2}`. + 0 package id +1 package version +2 package source There are no stable versions available, {0} is the best available. Consider adding the --prerelease option diff --git a/src/NuGet.Core/NuGet.CommandLine.XPlat/xlf/Strings.pl.xlf b/src/NuGet.Core/NuGet.CommandLine.XPlat/xlf/Strings.pl.xlf index 6556e2a79ab..6a179a2be08 100644 --- a/src/NuGet.Core/NuGet.CommandLine.XPlat/xlf/Strings.pl.xlf +++ b/src/NuGet.Core/NuGet.CommandLine.XPlat/xlf/Strings.pl.xlf @@ -1015,6 +1015,13 @@ Aby uzyskać więcej informacji, odwiedź stronę https://docs.nuget.org/docs/re 0 - package id 1 - package version 2 - package download location + + + Package download failed for `{0} {1}` from source `{2}`. + Package download failed for `{0} {1}` from source `{2}`. + 0 package id +1 package version +2 package source There are no stable versions available, {0} is the best available. Consider adding the --prerelease option diff --git a/src/NuGet.Core/NuGet.CommandLine.XPlat/xlf/Strings.pt-BR.xlf b/src/NuGet.Core/NuGet.CommandLine.XPlat/xlf/Strings.pt-BR.xlf index 310ceb71d6a..182ae13e675 100644 --- a/src/NuGet.Core/NuGet.CommandLine.XPlat/xlf/Strings.pt-BR.xlf +++ b/src/NuGet.Core/NuGet.CommandLine.XPlat/xlf/Strings.pt-BR.xlf @@ -1015,6 +1015,13 @@ Para obter mais informações, acesse https://docs.nuget.org/docs/reference/comm 0 - package id 1 - package version 2 - package download location + + + Package download failed for `{0} {1}` from source `{2}`. + Package download failed for `{0} {1}` from source `{2}`. + 0 package id +1 package version +2 package source There are no stable versions available, {0} is the best available. Consider adding the --prerelease option diff --git a/src/NuGet.Core/NuGet.CommandLine.XPlat/xlf/Strings.ru.xlf b/src/NuGet.Core/NuGet.CommandLine.XPlat/xlf/Strings.ru.xlf index 4330c968b98..8f9667ebc0e 100644 --- a/src/NuGet.Core/NuGet.CommandLine.XPlat/xlf/Strings.ru.xlf +++ b/src/NuGet.Core/NuGet.CommandLine.XPlat/xlf/Strings.ru.xlf @@ -1015,6 +1015,13 @@ For more information, visit https://docs.nuget.org/docs/reference/command-line-r 0 - package id 1 - package version 2 - package download location + + + Package download failed for `{0} {1}` from source `{2}`. + Package download failed for `{0} {1}` from source `{2}`. + 0 package id +1 package version +2 package source There are no stable versions available, {0} is the best available. Consider adding the --prerelease option diff --git a/src/NuGet.Core/NuGet.CommandLine.XPlat/xlf/Strings.tr.xlf b/src/NuGet.Core/NuGet.CommandLine.XPlat/xlf/Strings.tr.xlf index 6a650b47e4d..45dab8e0979 100644 --- a/src/NuGet.Core/NuGet.CommandLine.XPlat/xlf/Strings.tr.xlf +++ b/src/NuGet.Core/NuGet.CommandLine.XPlat/xlf/Strings.tr.xlf @@ -1016,6 +1016,13 @@ Daha fazla bilgi için bkz. https://docs.nuget.org/docs/reference/command-line-r 0 - package id 1 - package version 2 - package download location + + + Package download failed for `{0} {1}` from source `{2}`. + Package download failed for `{0} {1}` from source `{2}`. + 0 package id +1 package version +2 package source There are no stable versions available, {0} is the best available. Consider adding the --prerelease option diff --git a/src/NuGet.Core/NuGet.CommandLine.XPlat/xlf/Strings.zh-Hans.xlf b/src/NuGet.Core/NuGet.CommandLine.XPlat/xlf/Strings.zh-Hans.xlf index e1d40709ab9..87fd9aa758a 100644 --- a/src/NuGet.Core/NuGet.CommandLine.XPlat/xlf/Strings.zh-Hans.xlf +++ b/src/NuGet.Core/NuGet.CommandLine.XPlat/xlf/Strings.zh-Hans.xlf @@ -1015,6 +1015,13 @@ For more information, visit https://docs.nuget.org/docs/reference/command-line-r 0 - package id 1 - package version 2 - package download location + + + Package download failed for `{0} {1}` from source `{2}`. + Package download failed for `{0} {1}` from source `{2}`. + 0 package id +1 package version +2 package source There are no stable versions available, {0} is the best available. Consider adding the --prerelease option diff --git a/src/NuGet.Core/NuGet.CommandLine.XPlat/xlf/Strings.zh-Hant.xlf b/src/NuGet.Core/NuGet.CommandLine.XPlat/xlf/Strings.zh-Hant.xlf index 3899176e6ce..4da72c30f92 100644 --- a/src/NuGet.Core/NuGet.CommandLine.XPlat/xlf/Strings.zh-Hant.xlf +++ b/src/NuGet.Core/NuGet.CommandLine.XPlat/xlf/Strings.zh-Hant.xlf @@ -1015,6 +1015,13 @@ For more information, visit https://docs.nuget.org/docs/reference/command-line-r 0 - package id 1 - package version 2 - package download location + + + Package download failed for `{0} {1}` from source `{2}`. + Package download failed for `{0} {1}` from source `{2}`. + 0 package id +1 package version +2 package source There are no stable versions available, {0} is the best available. Consider adding the --prerelease option diff --git a/test/NuGet.Core.Tests/NuGet.CommandLine.Xplat.Tests/Commands/Package/Download/PackageDownloadRunnerTests.cs b/test/NuGet.Core.Tests/NuGet.CommandLine.Xplat.Tests/Commands/Package/Download/PackageDownloadRunnerTests.cs new file mode 100644 index 00000000000..cea54fde584 --- /dev/null +++ b/test/NuGet.Core.Tests/NuGet.CommandLine.Xplat.Tests/Commands/Package/Download/PackageDownloadRunnerTests.cs @@ -0,0 +1,600 @@ +// 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. + +namespace NuGet.CommandLine.Xplat.Tests; + +using System; +using System.IO; +using System.Linq; +using System.Threading; +using System.Threading.Tasks; +using FluentAssertions; +using Moq; +using NuGet.CommandLine.XPlat; +using NuGet.CommandLine.XPlat.Commands.Package; +using NuGet.CommandLine.XPlat.Commands.Package.PackageDownload; +using NuGet.Common; +using NuGet.Configuration; +using NuGet.Protocol.Core.Types; +using NuGet.Test.Utility; +using NuGet.Versioning; +using Xunit; + +public class PackageDownloadRunnerTests +{ + [Fact] + public async Task RunAsync_ExplicitVersionFromLocalFolderSource_SucceedsAsync() + { + // Arrange + using var context = new SimpleTestPathContext(); + var sourceDir = Path.Combine(context.WorkingDirectory, "src"); + var outputDir = Path.Combine(context.WorkingDirectory, "packages"); + Directory.CreateDirectory(sourceDir); + Directory.CreateDirectory(outputDir); + + var id = "Contoso.Lib"; + var version = "1.2.3"; + await SimpleTestPackageUtility.CreateFullPackageAsync(sourceDir, id, version); + + var logger = new Mock(MockBehavior.Loose); + var settings = new Mock(MockBehavior.Loose); + + var args = new PackageDownloadArgs() + { + Packages = [new Package { Id = id, VersionRange = VersionRange.Parse(version) }], + OutputDirectory = outputDir, + }; + + + // Act + var result = await PackageDownloadRunner.RunAsync( + args, + logger.Object, + [new(sourceDir)], + settings.Object, + CancellationToken.None); + + // Assert + result.Should().Be(PackageDownloadRunner.ExitCodeSuccess); + var installDir = Path.Combine(outputDir, id.ToLowerInvariant(), version); + Directory.Exists(installDir).Should().BeTrue(); + Directory.EnumerateFiles(installDir, "*.nupkg").Any().Should().BeTrue(); + File.Exists(Path.Combine(installDir, $"{id.ToLowerInvariant()}.{version}.nupkg")).Should().BeTrue(); + } + + [Fact] + public async Task RunAsync_NoVersionWhenPrereleaseNotIncluded_PicksLatestStable() + { + // Arrange + using var context = new SimpleTestPathContext(); + var sourceDir = Path.Combine(context.WorkingDirectory, "src"); + var outputDir = Path.Combine(context.WorkingDirectory, "packages"); + Directory.CreateDirectory(sourceDir); + Directory.CreateDirectory(outputDir); + + var id = "Contoso.Core"; + await SimpleTestPackageUtility.CreateFullPackageAsync(sourceDir, id, "1.0.0"); + await SimpleTestPackageUtility.CreateFullPackageAsync(sourceDir, id, "1.1.0"); + await SimpleTestPackageUtility.CreateFullPackageAsync(sourceDir, id, "2.0.0-beta"); + + var logger = new Mock(MockBehavior.Loose); + var settings = new Mock(MockBehavior.Loose); + + var args = new PackageDownloadArgs() + { + Packages = [new Package { Id = id, VersionRange = null }], + OutputDirectory = outputDir, + }; + + // Act + var result = await PackageDownloadRunner.RunAsync( + args, + logger.Object, + [new PackageSource(sourceDir)], + settings.Object, + CancellationToken.None); + + // Assert + result.Should().Be(PackageDownloadRunner.ExitCodeSuccess); + + var chosen = Path.Combine(outputDir, id.ToLowerInvariant(), "1.1.0"); + var notChosen = Path.Combine(outputDir, id.ToLowerInvariant(), "2.0.0-beta"); + Directory.Exists(chosen).Should().BeTrue("latest stable (1.1.0) should be chosen"); + Directory.Exists(notChosen).Should().BeFalse("prerelease (2.0.0-beta) should not be chosen"); + File.Exists(Path.Combine(chosen, $"{id.ToLowerInvariant()}.1.1.0.nupkg")).Should().BeTrue(); + } + + [Fact] + public async Task RunAsync_NoVersionWithPrereleaseTrue_PicksHighestIncludingPrerelease() + { + // Arrange + using var context = new SimpleTestPathContext(); + var sourceDir = Path.Combine(context.WorkingDirectory, "src"); + var outputDir = Path.Combine(context.WorkingDirectory, "packages"); + Directory.CreateDirectory(sourceDir); + Directory.CreateDirectory(outputDir); + + var id = "Contoso.Preview"; + await SimpleTestPackageUtility.CreateFullPackageAsync(sourceDir, id, "1.3.0"); + await SimpleTestPackageUtility.CreateFullPackageAsync(sourceDir, id, "2.0.0-beta.2"); + + var logger = new Mock(MockBehavior.Loose); + var settings = new Mock(MockBehavior.Loose); + + var args = new PackageDownloadArgs() + { + Packages = [new Package { Id = id, VersionRange = null }], + OutputDirectory = outputDir, + IncludePrerelease = true + }; + + // Act + var result = await PackageDownloadRunner.RunAsync( + args, + logger.Object, + [new PackageSource(sourceDir)], + settings.Object, + CancellationToken.None); + + // Assert + result.Should().Be(PackageDownloadRunner.ExitCodeSuccess); + + var chosen = Path.Combine(outputDir, id.ToLowerInvariant(), "2.0.0-beta.2"); + Directory.Exists(chosen).Should().BeTrue("IncludePrerelease should allow picking 2.0.0-beta.2"); + File.Exists(Path.Combine(chosen, $"{id.ToLowerInvariant()}.2.0.0-beta.2.nupkg")).Should().BeTrue(); + } + + [Fact] + public async Task RunAsync_NoVersion_PicksHighestAcrossMultipleSources() + { + // Arrange + using var context = new SimpleTestPathContext(); + var srcA = Path.Combine(context.WorkingDirectory, "srcA"); + var srcB = Path.Combine(context.WorkingDirectory, "srcB"); + var outputDir = Path.Combine(context.WorkingDirectory, "packages"); + Directory.CreateDirectory(srcA); + Directory.CreateDirectory(srcB); + Directory.CreateDirectory(outputDir); + + var id = "Contoso.Toolkit"; + await SimpleTestPackageUtility.CreateFullPackageAsync(srcA, id, "1.1.0"); + await SimpleTestPackageUtility.CreateFullPackageAsync(srcB, id, "1.2.0"); + + var logger = new Mock(MockBehavior.Loose); + var settings = new Mock(MockBehavior.Loose); + + var args = new PackageDownloadArgs() + { + Packages = [new Package { Id = id, VersionRange = null }], + OutputDirectory = outputDir, + }; + + // Act + var result = await PackageDownloadRunner.RunAsync( + args, + logger.Object, + [new PackageSource(srcA), new PackageSource(srcB)], + settings.Object, + CancellationToken.None); + + // Assert + result.Should().Be(PackageDownloadRunner.ExitCodeSuccess); + + var chosen = Path.Combine(outputDir, id.ToLowerInvariant(), "1.2.0"); + Directory.Exists(chosen).Should().BeTrue("should choose the highest version found across all sources"); + File.Exists(Path.Combine(chosen, $"{id.ToLowerInvariant()}.1.2.0.nupkg")).Should().BeTrue(); + } + + [Fact] + public async Task RunAsync_ExplicitVersionAlreadyInstalled_ShortCircuitsAndSucceeds() + { + // Arrange + using var context = new SimpleTestPathContext(); + var sourceDir = Path.Combine(context.WorkingDirectory, "src"); + var outputDir = Path.Combine(context.WorkingDirectory, "packages"); + Directory.CreateDirectory(sourceDir); + Directory.CreateDirectory(outputDir); + + var id = "Contoso.Utils"; + var v = "3.4.5"; + await SimpleTestPackageUtility.CreateFullPackageAsync(sourceDir, id, v); + + var logger = new Mock(MockBehavior.Loose); + var settings = new Mock(MockBehavior.Loose); + + // First run: install explicit version + var args1 = new PackageDownloadArgs() + { + Packages = [new Package { Id = id, VersionRange = VersionRange.Parse(v) }], + LogLevel = LogLevel.Verbose, + OutputDirectory = outputDir, + }; + + var first = await PackageDownloadRunner.RunAsync( + args1, + logger.Object, + [new PackageSource(sourceDir)], + settings.Object, + CancellationToken.None); + first.Should().Be(ExitCodes.Success); + + // Second run: should short-circuit because already installed + var args2 = new PackageDownloadArgs() + { + Packages = [new Package { Id = id, VersionRange = VersionRange.Parse(v) }], + OutputDirectory = outputDir, + }; + + // Act + var second = await PackageDownloadRunner.RunAsync( + args2, + logger.Object, + [new PackageSource(sourceDir)], + settings.Object, + CancellationToken.None); + + // Assert + second.Should().Be(PackageDownloadRunner.ExitCodeSuccess); + var installDir = Path.Combine(outputDir, id.ToLowerInvariant(), v); + Directory.Exists(installDir).Should().BeTrue(); + File.Exists(Path.Combine(installDir, $"{id.ToLowerInvariant()}.{v}.nupkg")).Should().BeTrue(); + logger.Verify(l => l.LogMinimal(It.Is(s => s.Contains("Skipping", StringComparison.OrdinalIgnoreCase))), Times.AtLeastOnce); + } + + [Fact] + public async Task RunAsync_WhenAllowInsecureConnectionsFalse_RejectsHttpSource() + { + // Arrange + using var context = new SimpleTestPathContext(); + + var outputDir = Path.Combine(context.WorkingDirectory, "packages"); + Directory.CreateDirectory(outputDir); + + var httpSource = "http://contoso/v3/index.json"; + var logger = new Mock(MockBehavior.Loose); + var settings = new Mock(MockBehavior.Loose); + + var args = new PackageDownloadArgs() + { + Packages = [new Package { Id = "Contoso.Lib", VersionRange = null }], + OutputDirectory = outputDir, + }; + + // Act + var result = await PackageDownloadRunner.RunAsync( + args, + logger.Object, + [new PackageSource(httpSource)], + settings.Object, + CancellationToken.None); + + // Assert + result.Should().Be(PackageDownloadRunner.ExitCodeError); + logger.Verify(l => l.LogError(It.Is(s => s.Contains(httpSource, StringComparison.OrdinalIgnoreCase))), Times.AtLeastOnce); + } + + [Fact] + public async Task RunAsync_PackageDoesNotExist_ReturnsError() + { + // Arrange + using var context = new SimpleTestPathContext(); + var srcA = Path.Combine(context.WorkingDirectory, "emptyA"); + var srcB = Path.Combine(context.WorkingDirectory, "emptyB"); + var outputDir = Path.Combine(context.WorkingDirectory, "packages"); + Directory.CreateDirectory(srcA); + Directory.CreateDirectory(srcB); + Directory.CreateDirectory(outputDir); + + var id = "Missing.Package"; + var v = "9.9.9"; + + var logger = new Mock(MockBehavior.Loose); + var settings = new Mock(MockBehavior.Loose); + + var args = new PackageDownloadArgs() + { + Packages = [new Package { Id = id, VersionRange = VersionRange.Parse(v) }], + OutputDirectory = outputDir, + }; + + // Act + var result = await PackageDownloadRunner.RunAsync( + args, + logger.Object, + [new PackageSource(srcA), new PackageSource(srcB)], + settings.Object, + CancellationToken.None); + + // Assert + result.Should().Be(PackageDownloadRunner.ExitCodeError); + logger.Verify(l => l.LogError(It.IsAny()), Times.AtLeastOnce); + + File.Exists(Path.Combine(outputDir, $"{id.ToLowerInvariant()}.{v}.nupkg")) + .Should().BeFalse("Package does not exist in sources"); + } + + [Fact] + public async Task ResolvePackageDownloadVersion_ExactVersionAtFirstSource_ReturnsEarly() + { + // Arrange + using var context = new SimpleTestPathContext(); + var v123 = new NuGetVersion("1.2.3"); + var package = new Package { Id = "Contoso", VersionRange = VersionRange.Parse(v123.OriginalVersion) }; + var sourceDir = Path.Combine(context.WorkingDirectory, "src"); + Directory.CreateDirectory(sourceDir); + await SimpleTestPackageUtility.CreateFullPackageAsync(sourceDir, package.Id, package.VersionRange.OriginalString); + var sources = new[] { new PackageSource(sourceDir) }; + + var logger = new Mock(MockBehavior.Loose); + + // Act + (NuGetVersion resolved, SourceRepository resolvedRepo) = await PackageDownloadRunner.ResolvePackageDownloadVersion( + package, + sources, + new SourceCacheContext(), + logger.Object, + includePrerelease: false, + CancellationToken.None); + + // Assert + Assert.Equal(v123, resolved); + Assert.Equal(sourceDir, resolvedRepo.PackageSource.Source); + } + + [Fact] + public async Task ResolvePackageDownloadVersion_ExactVersionMissingInFirstPresentInSecond_ReturnsSecondSource() + { + // Arrange + using var context = new SimpleTestPathContext(); + var v123 = new NuGetVersion("1.2.3"); + var v100 = new NuGetVersion("1.0.0"); + var package = new Package { Id = "Contoso", VersionRange = VersionRange.Parse(v123.OriginalVersion) }; + + var sourceA = Path.Combine(context.WorkingDirectory, "srcA"); + var sourceB = Path.Combine(context.WorkingDirectory, "srcB"); + Directory.CreateDirectory(sourceA); + Directory.CreateDirectory(sourceB); + + await SimpleTestPackageUtility.CreateFullPackageAsync(sourceA, package.Id, v100.OriginalVersion); + await SimpleTestPackageUtility.CreateFullPackageAsync(sourceB, package.Id, v123.OriginalVersion); + + var sources = new[] { new PackageSource(sourceA), new PackageSource(sourceB) }; + var logger = new Mock(MockBehavior.Loose); + + // Act + (NuGetVersion resolved, SourceRepository resolvedRepo) = await PackageDownloadRunner.ResolvePackageDownloadVersion( + package, sources, new SourceCacheContext(), logger.Object, includePrerelease: false, CancellationToken.None); + + // Assert + Assert.Equal(v123, resolved); + Assert.Equal(sourceB, resolvedRepo.PackageSource.Source); + } + + [Fact] + public async Task ResolvePackageDownloadVersion_NoVersionSpecifiedExcludePrerelease_PicksHighestStableAcrossSources() + { + // Arrange + using var context = new SimpleTestPathContext(); + var package = new Package { Id = "Contoso", VersionRange = null }; + var sourceA = Path.Combine(context.WorkingDirectory, "srcA"); + var sourceB = Path.Combine(context.WorkingDirectory, "srcB"); + Directory.CreateDirectory(sourceA); + Directory.CreateDirectory(sourceB); + + await SimpleTestPackageUtility.CreateFullPackageAsync(sourceA, package.Id, "1.0.0"); + await SimpleTestPackageUtility.CreateFullPackageAsync(sourceA, package.Id, "1.5.0-alpha"); // prerelease; should be ignored + await SimpleTestPackageUtility.CreateFullPackageAsync(sourceB, package.Id, "2.0.0"); // highest stable + + var sources = new[] { new PackageSource(sourceA), new PackageSource(sourceB) }; + var logger = new Mock(MockBehavior.Loose); + + // Act + (NuGetVersion resolved, SourceRepository resolvedRepo) = await PackageDownloadRunner.ResolvePackageDownloadVersion( + package, sources, new SourceCacheContext(), logger.Object, includePrerelease: false, CancellationToken.None); + + // Assert + Assert.Equal(new NuGetVersion("2.0.0"), resolved); + Assert.Equal(sourceB, resolvedRepo.PackageSource.Source); + } + + [Fact] + public async Task ResolvePackageDownloadVersion_NoVersionSpecifiedIncludePrerelease_PicksHighestIncludingPrerelease() + { + // Arrange + using var context = new SimpleTestPathContext(); + var package = new Package { Id = "Contoso", VersionRange = null }; // pickLatest + var sourceA = Path.Combine(context.WorkingDirectory, "srcA"); + var sourceB = Path.Combine(context.WorkingDirectory, "srcB"); + Directory.CreateDirectory(sourceA); + Directory.CreateDirectory(sourceB); + + await SimpleTestPackageUtility.CreateFullPackageAsync(sourceA, package.Id, "2.0.0"); // stable + await SimpleTestPackageUtility.CreateFullPackageAsync(sourceB, package.Id, "2.1.0-rc.1"); // higher (prerelease) + + var sources = new[] { new PackageSource(sourceA), new PackageSource(sourceB) }; + var logger = new Mock(MockBehavior.Loose); + + // Act + (NuGetVersion resolved, SourceRepository resolvedRepo) = await PackageDownloadRunner.ResolvePackageDownloadVersion( + package, sources, new SourceCacheContext(), logger.Object, includePrerelease: true, CancellationToken.None); + + // Assert + Assert.Equal(new NuGetVersion("2.1.0-rc.1"), resolved); + Assert.Equal(sourceB, resolvedRepo.PackageSource.Source); + } + + [Fact] + public async Task ResolvePackageDownloadVersion_MinInclusiveVersionRange_PicksLowestSatisfyingAcrossSources() + { + // Arrange + using var context = new SimpleTestPathContext(); + var min = new NuGetVersion("1.0.0"); + var max = new NuGetVersion("2.0.0"); + var range = VersionRange.Parse($"[{min.OriginalVersion}, {max.OriginalVersion})"); + + var package = new Package { Id = "Contoso", VersionRange = range }; + + var sourceA = Path.Combine(context.WorkingDirectory, "srcA"); + var sourceB = Path.Combine(context.WorkingDirectory, "srcB"); + Directory.CreateDirectory(sourceA); + Directory.CreateDirectory(sourceB); + + await SimpleTestPackageUtility.CreateFullPackageAsync(sourceA, package.Id, "1.0.0"); + await SimpleTestPackageUtility.CreateFullPackageAsync(sourceA, package.Id, "1.5.0"); + await SimpleTestPackageUtility.CreateFullPackageAsync(sourceB, package.Id, "1.8.0"); + + var sources = new[] { new PackageSource(sourceA), new PackageSource(sourceB) }; + var logger = new Mock(MockBehavior.Loose); + + // Act + (NuGetVersion resolved, SourceRepository resolvedRepo) = await PackageDownloadRunner.ResolvePackageDownloadVersion( + package, sources, new SourceCacheContext(), logger.Object, includePrerelease: false, CancellationToken.None); + + // Assert + resolved.Should().Be(min); + resolvedRepo.PackageSource.Source.Should().Be(sourceA); + } + + [Fact] + public async Task ResolvePackageDownloadVersion_MinAndMaxInclusiveRange_UsesMinVersionIfPresent() + { + // Arrange + using var context = new SimpleTestPathContext(); + var rangeText = "[1.0.0,2.0.0]"; + var package = new Package { Id = "Contoso", VersionRange = VersionRange.Parse(rangeText) }; + + var sourceA = Path.Combine(context.WorkingDirectory, "srcA"); + Directory.CreateDirectory(sourceA); + + await SimpleTestPackageUtility.CreateFullPackageAsync(sourceA, package.Id, "1.0.0"); // MinVersion present + await SimpleTestPackageUtility.CreateFullPackageAsync(sourceA, package.Id, "2.0.0"); + + var sources = new[] { new PackageSource(sourceA) }; + var logger = new Mock(MockBehavior.Loose); + + // Act + (NuGetVersion resolved, SourceRepository resolvedRepo) = await PackageDownloadRunner.ResolvePackageDownloadVersion( + package, sources, new SourceCacheContext(), logger.Object, includePrerelease: false, CancellationToken.None); + + // Assert (pins current behavior) + resolved.Should().Be(new NuGetVersion("1.0.0")); + resolvedRepo.PackageSource.Source.Should().Be(sourceA); + } + + [Fact] + public async Task ResolvePackageDownloadVersion_NoMatchesAnywhere_ReturnsNull() + { + using var context = new SimpleTestPathContext(); + + // Arrange + var package = new Package { Id = "Contoso", VersionRange = null }; // pickLatest + var sourceA = Path.Combine(context.WorkingDirectory, "emptyA"); + var sourceB = Path.Combine(context.WorkingDirectory, "emptyB"); + Directory.CreateDirectory(sourceA); + Directory.CreateDirectory(sourceB); + var sources = new[] { new PackageSource(sourceA), new PackageSource(sourceB) }; + + var logger = new Mock(MockBehavior.Loose); + logger.Setup(l => l.LogError(It.IsAny())); + + // Act + (NuGetVersion resolved, SourceRepository resolvedRepo) = await PackageDownloadRunner.ResolvePackageDownloadVersion( + package, sources, new SourceCacheContext(), logger.Object, includePrerelease: false, CancellationToken.None); + + // Assert + Assert.Null(resolved); + Assert.Null(resolvedRepo); + } + + [Fact] + public async Task ResolvePackageDownloadVersion_ExactVersionNotFoundAnywhere_ReturnsNull() + { + // Arrange + using var context = new SimpleTestPathContext(); + var v123 = new NuGetVersion("1.2.3"); + var package = new Package { Id = "Contoso", VersionRange = VersionRange.Parse(v123.OriginalVersion) }; + var sourceDir = Path.Combine(context.WorkingDirectory, "src"); + Directory.CreateDirectory(sourceDir); + + await SimpleTestPackageUtility.CreateFullPackageAsync(sourceDir, package.Id, "1.0.0"); + + var sources = new[] { new PackageSource(sourceDir) }; + var logger = new Mock(MockBehavior.Loose); + logger.Setup(l => l.LogError(It.IsAny())); + + // Act + (NuGetVersion resolved, SourceRepository resolvedRepo) = await PackageDownloadRunner.ResolvePackageDownloadVersion( + package, + sources, + new SourceCacheContext(), + logger.Object, + includePrerelease: false, + CancellationToken.None); + + // Assert + Assert.Null(resolved); + Assert.Null(resolvedRepo); + } + + [Fact] + public async Task ResolvePackageDownloadVersion_OnlyPrereleaseExistsAndExcludePrerelease_ReturnsNull() + { + using var context = new SimpleTestPathContext(); + + // Arrange + var package = new Package { Id = "Contoso", VersionRange = null }; // pickLatest = true + var sourceDir = Path.Combine(context.WorkingDirectory, "src"); + Directory.CreateDirectory(sourceDir); + + await SimpleTestPackageUtility.CreateFullPackageAsync(sourceDir, package.Id, "3.0.0-beta.1"); // prerelease only + + var sources = new[] { new PackageSource(sourceDir) }; + var logger = new Mock(MockBehavior.Loose); + logger.Setup(l => l.LogError(It.IsAny())); + + // Act + (NuGetVersion resolved, SourceRepository resolvedRepo) = await PackageDownloadRunner.ResolvePackageDownloadVersion( + package, + sources, + new SourceCacheContext(), + logger.Object, + includePrerelease: false, // do not allow prerelease + CancellationToken.None); + + // Assert + Assert.Null(resolved); + Assert.Null(resolvedRepo); + } + + [Fact] + public async Task ResolvePackageDownloadVersion_VersionRangeMinNotAvailable_PicksNextLowestSatisfying() + { + // Arrange + using var context = new SimpleTestPathContext(); + var min = new NuGetVersion("1.0.0"); + var max = new NuGetVersion("3.0.0"); + + var package = new Package { Id = "Contoso", VersionRange = VersionRange.Parse($"[{min.OriginalVersion}, {max.OriginalVersion}]") }; + + var sourceDir = Path.Combine(context.WorkingDirectory, "src"); + Directory.CreateDirectory(sourceDir); + + await SimpleTestPackageUtility.CreateFullPackageAsync(sourceDir, package.Id, "1.5.0"); + await SimpleTestPackageUtility.CreateFullPackageAsync(sourceDir, package.Id, "2.0.0"); + + var sources = new[] { new PackageSource(sourceDir) }; + var logger = new Mock(MockBehavior.Loose); + + // Act + (NuGetVersion resolved, SourceRepository resolvedRepo) = await PackageDownloadRunner.ResolvePackageDownloadVersion( + package, + sources, + new SourceCacheContext(), + logger.Object, + includePrerelease: false, + CancellationToken.None); + + Assert.Equal(new NuGetVersion("1.5.0"), resolved); + Assert.Equal(sourceDir, resolvedRepo.PackageSource.Source); + } +} diff --git a/test/NuGet.Core.Tests/NuGet.CommandLine.Xplat.Tests/PackageDownloadRunnerTests.cs b/test/NuGet.Core.Tests/NuGet.CommandLine.Xplat.Tests/PackageDownloadRunnerTests.cs deleted file mode 100644 index 11f28f60d9e..00000000000 --- a/test/NuGet.Core.Tests/NuGet.CommandLine.Xplat.Tests/PackageDownloadRunnerTests.cs +++ /dev/null @@ -1,294 +0,0 @@ -// 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. - -namespace NuGet.CommandLine.Xplat.Tests; - -using System.IO; -using System.Linq; -using System.Threading.Tasks; -using FluentAssertions; -using Moq; -using NuGet.CommandLine.XPlat; -using NuGet.CommandLine.XPlat.Commands.PackageDownload; -using NuGet.Common; -using NuGet.Test.Utility; -using Xunit; - -public class PackageDownloadRunnerTests -{ - [Fact] - public async Task RunAsync_ExplicitVersionFromLocalFolderSource_SucceedsAsync() - { - using var context = new SimpleTestPathContext(); - - // Arrange - var sourceDir = Path.Combine(context.WorkingDirectory, "src"); - var outputDir = Path.Combine(context.WorkingDirectory, "packages"); - Directory.CreateDirectory(sourceDir); - Directory.CreateDirectory(outputDir); - - var id = "Contoso.Lib"; - var version = "1.2.3"; - await SimpleTestPackageUtility.CreateFullPackageAsync(sourceDir, id, version); - - var logger = new Mock(MockBehavior.Loose); - - var args = new PackageDownloadArgs( - packageId: id, - sources: new[] { sourceDir }.ToList(), - outputDirectory: outputDir, - logger: logger.Object) - { - Version = version, - ConfigFile = context.NuGetConfig, - AllowInsecureConnections = true, - Verbosity = Verbosity.Detailed - }; - - - // Act - var result = await PackageDownloadRunner.RunAsync(args, System.Threading.CancellationToken.None); - - // Assert - result.Should().Be(ExitCodes.Success); - - var installDir = Path.Combine(outputDir, id.ToLowerInvariant(), version); - Directory.Exists(installDir).Should().BeTrue(); - Directory.EnumerateFiles(installDir, "*.nupkg").Any().Should().BeTrue(); - File.Exists(Path.Combine(installDir, $"{id.ToLowerInvariant()}.{version}.nupkg")).Should().BeTrue(); - } - - [Fact] - public async Task RunAsync_NoVersionWhenPrereleaseNotIncluded_PicksLatestStable() - { - using var context = new SimpleTestPathContext(); - - // Arrange - var sourceDir = Path.Combine(context.WorkingDirectory, "src"); - var outputDir = Path.Combine(context.WorkingDirectory, "packages"); - Directory.CreateDirectory(sourceDir); - Directory.CreateDirectory(outputDir); - - var id = "Contoso.Core"; - await SimpleTestPackageUtility.CreateFullPackageAsync(sourceDir, id, "1.0.0"); - await SimpleTestPackageUtility.CreateFullPackageAsync(sourceDir, id, "1.1.0"); - await SimpleTestPackageUtility.CreateFullPackageAsync(sourceDir, id, "2.0.0-beta"); // prerelease - - var logger = new Mock(MockBehavior.Loose); - - var args = new PackageDownloadArgs( - packageId: id, - sources: new[] { sourceDir }.ToList(), - outputDirectory: outputDir, - logger: logger.Object); - - // Act - var result = await PackageDownloadRunner.RunAsync(args, System.Threading.CancellationToken.None); - - // Assert - result.Should().Be(ExitCodes.Success); - - var chosen = Path.Combine(outputDir, id.ToLowerInvariant(), "1.1.0"); - var notChosen = Path.Combine(outputDir, id.ToLowerInvariant(), "2.0.0-beta"); - Directory.Exists(chosen).Should().BeTrue("latest stable (1.1.0) should be chosen"); - Directory.Exists(notChosen).Should().BeFalse("prerelease (2.0.0-beta) should not be chosen"); - File.Exists(Path.Combine(chosen, $"{id.ToLowerInvariant()}.1.1.0.nupkg")).Should().BeTrue(); - } - - [Fact] - public async Task RunAsync_NoVersionWithPrereleaseTrue_PicksHighestIncludingPrerelease() - { - using var context = new SimpleTestPathContext(); - - // Arrange - var sourceDir = Path.Combine(context.WorkingDirectory, "src"); - var outputDir = Path.Combine(context.WorkingDirectory, "packages"); - Directory.CreateDirectory(sourceDir); - Directory.CreateDirectory(outputDir); - - var id = "Contoso.Preview"; - await SimpleTestPackageUtility.CreateFullPackageAsync(sourceDir, id, "1.3.0"); - await SimpleTestPackageUtility.CreateFullPackageAsync(sourceDir, id, "2.0.0-beta.2"); - - var logger = new Mock(MockBehavior.Loose); - - var args = new PackageDownloadArgs( - packageId: id, - sources: new[] { sourceDir }.ToList(), - outputDirectory: outputDir, - logger: logger.Object) - { - IncludePrerelease = true - }; - - // Act - var result = await PackageDownloadRunner.RunAsync(args, System.Threading.CancellationToken.None); - - // Assert - result.Should().Be(ExitCodes.Success); - - var chosen = Path.Combine(outputDir, id.ToLowerInvariant(), "2.0.0-beta.2"); - Directory.Exists(chosen).Should().BeTrue("IncludePrerelease=true should allow picking 2.0.0-beta.2"); - File.Exists(Path.Combine(chosen, $"{id.ToLowerInvariant()}.2.0.0-beta.2.nupkg")).Should().BeTrue(); - } - - [Fact] - public async Task RunAsync_NoVersion_PicksHighestAcrossMultipleSources() - { - using var context = new SimpleTestPathContext(); - - // Arrange - var srcA = Path.Combine(context.WorkingDirectory, "srcA"); - var srcB = Path.Combine(context.WorkingDirectory, "srcB"); - var outputDir = Path.Combine(context.WorkingDirectory, "packages"); - Directory.CreateDirectory(srcA); - Directory.CreateDirectory(srcB); - Directory.CreateDirectory(outputDir); - - var id = "Contoso.Toolkit"; - await SimpleTestPackageUtility.CreateFullPackageAsync(srcA, id, "1.1.0"); - await SimpleTestPackageUtility.CreateFullPackageAsync(srcB, id, "1.2.0"); - - var logger = new Mock(MockBehavior.Loose); - - var args = new PackageDownloadArgs( - packageId: id, - sources: [srcA, srcB], - outputDirectory: outputDir, - logger: logger.Object); - - // Act - var result = await PackageDownloadRunner.RunAsync(args, System.Threading.CancellationToken.None); - - // Assert - result.Should().Be(ExitCodes.Success); - - var chosen = Path.Combine(outputDir, id.ToLowerInvariant(), "1.2.0"); - Directory.Exists(chosen).Should().BeTrue("should choose the highest version found across all sources"); - File.Exists(Path.Combine(chosen, $"{id.ToLowerInvariant()}.1.2.0.nupkg")).Should().BeTrue(); - } - - [Fact] - public async Task RunAsync_ExplicitVersionAlreadyInstalled_ShortCircuitsAndSucceeds() - { - using var context = new SimpleTestPathContext(); - - // Arrange - var sourceDir = Path.Combine(context.WorkingDirectory, "src"); - var outputDir = Path.Combine(context.WorkingDirectory, "packages"); - Directory.CreateDirectory(sourceDir); - Directory.CreateDirectory(outputDir); - - var id = "Contoso.Utils"; - var v = "3.4.5"; - await SimpleTestPackageUtility.CreateFullPackageAsync(sourceDir, id, v); - - // First run: install it - var logger1 = new Mock(MockBehavior.Loose); - var args1 = new PackageDownloadArgs( - packageId: id, - sources: [sourceDir], - outputDirectory: outputDir, - logger: logger1.Object) - { - Version = v - }; - - var first = await PackageDownloadRunner.RunAsync(args1, System.Threading.CancellationToken.None); - first.Should().Be(ExitCodes.Success); - - // Second run: should short-circuit - var logger2 = new Mock(MockBehavior.Loose); - var args2 = new PackageDownloadArgs( - packageId: id, - sources: [sourceDir], - outputDirectory: outputDir, - logger: logger2.Object) - { - Version = v - }; - - // Act - var second = await PackageDownloadRunner.RunAsync(args2, System.Threading.CancellationToken.None); - - // Assert - second.Should().Be(ExitCodes.Success); - - var installDir = Path.Combine(outputDir, id.ToLowerInvariant(), v); - Directory.Exists(installDir).Should().BeTrue(); - File.Exists(Path.Combine(installDir, $"{id.ToLowerInvariant()}.{v}.nupkg")).Should().BeTrue(); - logger2.Verify(l => l.LogMinimal(It.Is(s => - s.Contains("Skipping", System.StringComparison.OrdinalIgnoreCase))), Times.AtLeastOnce); - } - - [Fact] - public async Task RunAsync_WhenAllowInsecureConnectionsFalse_RejectsHttpSource() - { - using var context = new SimpleTestPathContext(); - - var outputDir = Path.Combine(context.WorkingDirectory, "packages"); - Directory.CreateDirectory(outputDir); - - var httpSource = "http://contoso/v3/index.json"; - var logger = new Mock(MockBehavior.Loose); - - var args = new PackageDownloadArgs( - packageId: "Contoso.Lib", - sources: [httpSource], - outputDirectory: outputDir, - logger: logger.Object) - { - ConfigFile = context.NuGetConfig, - AllowInsecureConnections = false, - Verbosity = Verbosity.Detailed - }; - - // Act - var result = await PackageDownloadRunner.RunAsync(args, System.Threading.CancellationToken.None); - - // Assert - result.Should().Be(ExitCodes.Error); - logger.Verify(l => l.LogError(It.Is(s => - s.Contains(httpSource, System.StringComparison.OrdinalIgnoreCase))), Times.AtLeastOnce); - } - - [Fact] - public async Task RunAsync_PackageDoesNotExist_ReturnsError() - { - using var context = new SimpleTestPathContext(); - - // Arrange - var srcA = Path.Combine(context.WorkingDirectory, "emptyA"); - var srcB = Path.Combine(context.WorkingDirectory, "emptyB"); - var outputDir = Path.Combine(context.WorkingDirectory, "packages"); - Directory.CreateDirectory(srcA); - Directory.CreateDirectory(srcB); - Directory.CreateDirectory(outputDir); - - var id = "Missing.Package"; - var v = "9.9.9"; - - var logger = new Mock(MockBehavior.Loose); - - var args = new PackageDownloadArgs( - packageId: id, - sources: [srcA, srcB], - outputDirectory: outputDir, - logger: logger.Object) - { - Version = v, - ConfigFile = context.NuGetConfig, - AllowInsecureConnections = true, - Verbosity = Verbosity.Detailed - }; - - // Act - var result = await PackageDownloadRunner.RunAsync(args, System.Threading.CancellationToken.None); - - // Assert - result.Should().Be(ExitCodes.Error); - logger.Verify(l => l.LogError(It.IsAny()), Times.AtLeastOnce); - - File.Exists(Path.Combine(outputDir, $"{id.ToLowerInvariant()}.{v}.nupkg")).Should().BeFalse("Package does not exist in sources"); - } -} From 8266c93fe66775b543dc930660fa024ca4599e43 Mon Sep 17 00:00:00 2001 From: Nigusu Yenework Date: Sun, 28 Sep 2025 13:32:33 -0700 Subject: [PATCH 03/15] Add parsing tests --- .../Download/PackageDownloadCommandTests.cs | 309 ++++++++++++++++++ 1 file changed, 309 insertions(+) create mode 100644 test/NuGet.Core.Tests/NuGet.CommandLine.Xplat.Tests/Commands/Package/Download/PackageDownloadCommandTests.cs diff --git a/test/NuGet.Core.Tests/NuGet.CommandLine.Xplat.Tests/Commands/Package/Download/PackageDownloadCommandTests.cs b/test/NuGet.Core.Tests/NuGet.CommandLine.Xplat.Tests/Commands/Package/Download/PackageDownloadCommandTests.cs new file mode 100644 index 00000000000..ccc5b5ac7cb --- /dev/null +++ b/test/NuGet.Core.Tests/NuGet.CommandLine.Xplat.Tests/Commands/Package/Download/PackageDownloadCommandTests.cs @@ -0,0 +1,309 @@ +// 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. + +using System; +using System.CommandLine; +using System.IO; +using System.Threading; +using System.Threading.Tasks; +using FluentAssertions; +using NuGet.CommandLine.XPlat.Commands.Package.PackageDownload; +using NuGet.Common; +using NuGet.Test.Utility; +using Xunit; + +namespace NuGet.CommandLine.Xplat.Tests.Commands.Package.Download +{ + public class PackageDownloadCommandTests + { + [Fact] + public async Task NoArguments_HasDefaultOptions() + { + // Arrange + string args = "package download"; + + // Act + var result = await RunAsync(args); + + // Assert + result.Should().NotBeNull(); + result.Packages.Should().BeEmpty(); + result.Sources.Should().BeEmpty(); + result.OutputDirectory.Should().BeNull(); + result.ConfigFile.Should().BeNull(); + result.IncludePrerelease.Should().BeFalse(); + result.DownloadOnly.Should().BeFalse(); + result.AllowInsecureConnections.Should().BeFalse(); + result.Interactive.Should().BeFalse(); + result.LogLevel.Should().Be(LogLevel.Information); + } + + [Fact] + public async Task WithSinglePackage_ShouldParsePackageId() + { + // Arrange + string args = "package download Contoso.Utils"; + + // Act + var result = await RunAsync(args); + + // Assert + result.Should().NotBeNull(); + result.Packages.Should().HaveCount(1); + result.Packages[0].Id.Should().Be("Contoso.Utils"); + result.Packages[0].VersionRange.Should().BeNull(); + } + + [Fact] + public async Task WithMultiplePackages_ShouldParseAllPackageIds() + { + // Arrange + string args = "package download Contoso.Utils Contoso.Framework"; + + // Act + var result = await RunAsync(args); + + // Assert + result.Should().NotBeNull(); + result.Packages.Should().HaveCount(2); + result.Packages[0].Id.Should().Be("Contoso.Utils"); + result.Packages[0].VersionRange.Should().BeNull(); + result.Packages[1].Id.Should().Be("Contoso.Framework"); + result.Packages[1].VersionRange.Should().BeNull(); + } + + [Fact] + public async Task WithPackageAndVersion_ShouldParsePackageWithVersionRange() + { + // Arrange + string args = "package download Contoso.Utils@2.1.0"; + + // Act + var result = await RunAsync(args); + + // Assert + result.Should().NotBeNull(); + result.Packages.Should().HaveCount(1); + result.Packages[0].Id.Should().Be("Contoso.Utils"); + result.Packages[0].VersionRange.Should().NotBeNull(); + result.Packages[0].VersionRange!.ToString().Should().Be("[2.1.0, )"); + } + + [Fact] + public async Task WithPackageAndVersionRange_ShouldParsePackageWithVersionRange() + { + // Arrange + string args = "package download Contoso.Utils@[2.0.0,3.0.0)"; + + // Act + var result = await RunAsync(args); + + // Assert + result.Should().NotBeNull(); + result.Packages.Should().HaveCount(1); + result.Packages[0].Id.Should().Be("Contoso.Utils"); + result.Packages[0].VersionRange.Should().NotBeNull(); + result.Packages[0].VersionRange!.ToString().Should().Be("[2.0.0, 3.0.0)"); + } + + [Fact] + public async Task WithMixedPackages_ShouldParseMixedPackagesCorrectly() + { + // Arrange + string args = "package download Contoso.Utils@2.1.0 Contoso.Framework"; + + // Act + var result = await RunAsync(args); + + // Assert + result.Should().NotBeNull(); + result.Packages.Should().HaveCount(2); + result.Packages[0].Id.Should().Be("Contoso.Utils"); + result.Packages[0].VersionRange.Should().NotBeNull(); + result.Packages[1].Id.Should().Be("Contoso.Framework"); + result.Packages[1].VersionRange.Should().BeNull(); + } + + [Fact] + public async Task WithOutputAndConfig_ShouldBindPaths() + { + // Arrange + using var pathContext = new SimpleTestPathContext(); + string outDir = Path.Combine(pathContext.WorkingDirectory, "out"); + string cfg = Path.Combine(pathContext.WorkingDirectory, "nuget.config"); + + string args = $"package download --output-directory \"{outDir}\" --configfile \"{cfg}\""; + + // Act + var result = await RunAsync(args); + + // Assert + result.Should().NotBeNull(); + result.OutputDirectory.Should().Be(outDir); + result.ConfigFile.Should().Be(cfg); + } + + [Theory] + [InlineData(true)] + [InlineData(false)] + public async Task WithInteractiveOption_ShouldSetCorrectInteractiveValue(bool value) + { + // Arrange + string args = $"package download --interactive:{value}"; + + // Act + var result = await RunAsync(args); + + // Assert + result.Should().NotBeNull(); + result.Interactive.Should().Be(value); + } + + [Fact] + public async Task WithBooleanFlags_ShouldSetAllFlagsTrue() + { + // Arrange + string args = "package download --prerelease --download-only --allow-insecure-connections --interactive"; + + // Act + var result = await RunAsync(args); + + // Assert + result.IncludePrerelease.Should().BeTrue(); + result.DownloadOnly.Should().BeTrue(); + result.AllowInsecureConnections.Should().BeTrue(); + result.Interactive.Should().BeTrue(); + } + + [Theory] + [InlineData("--verbosity quiet", LogLevel.Warning)] + [InlineData("--verbosity q", LogLevel.Warning)] + [InlineData("--verbosity minimal", LogLevel.Minimal)] + [InlineData("--verbosity m", LogLevel.Minimal)] + [InlineData("--verbosity normal", LogLevel.Information)] + [InlineData("--verbosity n", LogLevel.Information)] + [InlineData("--verbosity detailed", LogLevel.Verbose)] + [InlineData("--verbosity d", LogLevel.Verbose)] + [InlineData("--verbosity diagnostic", LogLevel.Debug)] + [InlineData("--verbosity diag", LogLevel.Debug)] + [InlineData("-v quiet", LogLevel.Warning)] + [InlineData("-v minimal", LogLevel.Minimal)] + [InlineData("-v normal", LogLevel.Information)] + [InlineData("-v detailed", LogLevel.Verbose)] + [InlineData("-v diagnostic", LogLevel.Debug)] + public async Task WithVerbosityOption_ShouldSetCorrectLogLevel(string verbosityArgs, LogLevel expectedLogLevel) + { + // Arrange + string args = $"package download {verbosityArgs}"; + + // Act + var result = await RunAsync(args); + + // Assert + result.Should().NotBeNull(); + result.LogLevel.Should().Be(expectedLogLevel); + } + + [Fact] + public void WithInvalidVersionRange_ShouldHaveParseErrors() + { + // Arrange + string args = "package download Contoso.Utils@invalid-version"; + + // Act + var result = Parse(args); + + // Assert + result.Errors.Count.Should().BeGreaterThan(0); + } + + [Fact] + public void WithEmptyVersionAfterAt_ShouldHaveParseErrors() + { + // Arrange + string args = "package download Contoso.Utils@"; + + // Act + var result = Parse(args); + + // Assert + result.Errors.Count.Should().BeGreaterThan(0); + } + + [Fact] + public async Task WithAllOptions_ShouldParseAllOptionsCorrectly() + { + // Arrange + using var pathContext = new SimpleTestPathContext(); + string outDir = Path.Combine(pathContext.WorkingDirectory, "out"); + string cfg = Path.Combine(pathContext.WorkingDirectory, "nuget.config"); + + string args = $"package download Contoso.Utils@2.1.0 --output-directory \"{outDir}\" --configfile \"{cfg}\" --prerelease --download-only --allow-insecure-connections --source s1 --source s2 --verbosity detailed --interactive"; + + // Act + var result = await RunAsync(args); + + // Assert + result.Should().NotBeNull(); + result.Packages.Should().HaveCount(1); + result.Packages[0].Id.Should().Be("Contoso.Utils"); + result.Packages[0].VersionRange.Should().NotBeNull(); + result.OutputDirectory.Should().Be(outDir); + result.ConfigFile.Should().Be(cfg); + result.IncludePrerelease.Should().BeTrue(); + result.DownloadOnly.Should().BeTrue(); + result.AllowInsecureConnections.Should().BeTrue(); + result.Sources.Should().ContainInOrder("s1", "s2"); + result.LogLevel.Should().Be(LogLevel.Verbose); + result.Interactive.Should().BeTrue(); + } + + private ParseResult Parse(string commandLine, Func> action = null) + { + RootCommand rootCommand = new RootCommand(); + + var packageCommand = new Command("package"); + rootCommand.Subcommands.Add(packageCommand); + + // Simulate SDK-provided interactive option + var interactiveOption = new Option("--interactive"); + + if (action == null) + { + action = (_, _) => throw new NotImplementedException("No action provided for command execution."); + } + + PackageDownloadCommand.Register(packageCommand, interactiveOption, action); + + var parser = rootCommand.Parse(commandLine); + return parser; + } + + private async Task RunAsync(string commandLine) + { + PackageDownloadArgs commandArgs = null; + + var parseResult = Parse(commandLine, (args, cancellationToken) => + { + commandArgs = args; + return Task.FromResult(0); + }); + + using var output = new StringWriter(); + var commandLineConfiguration = new InvocationConfiguration + { + Output = output, + Error = output, + }; + + await parseResult.InvokeAsync(commandLineConfiguration); + + if (commandArgs is null) + { + throw new InvalidOperationException("Command arguments were not set during command execution. Output:" + output.ToString()); + } + + return commandArgs; + } + } +} From acd8708adde35462b60b64c60beac68c1d6f8c82 Mon Sep 17 00:00:00 2001 From: Nigusu Yenework Date: Thu, 16 Oct 2025 14:00:30 -0700 Subject: [PATCH 04/15] Cleanup --- .../Package/Download/PackageDownloadArgs.cs | 16 +- .../Download/PackageDownloadCommand.cs | 40 +-- .../Package/Download/PackageDownloadRunner.cs | 178 ++++++------ .../AddPackageReferenceCommand.cs | 2 +- .../NuGet.CommandLine.XPlat/Program.cs | 6 +- .../Strings.Designer.cs | 254 ++++++++---------- .../NuGet.CommandLine.XPlat/Strings.resx | 52 ++-- .../xlf/Strings.cs.xlf | 172 +++++------- .../xlf/Strings.de.xlf | 172 +++++------- .../xlf/Strings.es.xlf | 172 +++++------- .../xlf/Strings.fr.xlf | 172 +++++------- .../xlf/Strings.it.xlf | 172 +++++------- .../xlf/Strings.ja.xlf | 172 +++++------- .../xlf/Strings.ko.xlf | 172 +++++------- .../xlf/Strings.pl.xlf | 172 +++++------- .../xlf/Strings.pt-BR.xlf | 172 +++++------- .../xlf/Strings.ru.xlf | 172 +++++------- .../xlf/Strings.tr.xlf | 172 +++++------- .../xlf/Strings.zh-Hans.xlf | 172 +++++------- .../xlf/Strings.zh-Hant.xlf | 172 +++++------- .../Download/PackageDownloadCommandTests.cs | 63 ++--- .../Download/PackageDownloadRunnerTests.cs | 137 ++-------- 22 files changed, 1231 insertions(+), 1753 deletions(-) diff --git a/src/NuGet.Core/NuGet.CommandLine.XPlat/Commands/Package/Download/PackageDownloadArgs.cs b/src/NuGet.Core/NuGet.CommandLine.XPlat/Commands/Package/Download/PackageDownloadArgs.cs index 9f0e52ed364..7a17fde3209 100644 --- a/src/NuGet.Core/NuGet.CommandLine.XPlat/Commands/Package/Download/PackageDownloadArgs.cs +++ b/src/NuGet.Core/NuGet.CommandLine.XPlat/Commands/Package/Download/PackageDownloadArgs.cs @@ -1,23 +1,21 @@ // 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.Collections.Generic; using NuGet.Common; namespace NuGet.CommandLine.XPlat.Commands.Package.PackageDownload; -internal class PackageDownloadArgs +internal record PackageDownloadArgs { - public IReadOnlyList Packages { get; set; } - public IList Sources { get; set; } - public string OutputDirectory { get; set; } - public string ConfigFile { get; set; } - + public IReadOnlyList? Packages { get; set; } + public IList? Sources { get; set; } + public string? OutputDirectory { get; set; } + public string? ConfigFile { get; set; } public bool IncludePrerelease { get; set; } - public bool DownloadOnly { get; set; } public bool AllowInsecureConnections { get; set; } public bool Interactive { get; set; } public LogLevel LogLevel { get; set; } } - -internal enum Verbosity { Quiet, Normal, Detailed } diff --git a/src/NuGet.Core/NuGet.CommandLine.XPlat/Commands/Package/Download/PackageDownloadCommand.cs b/src/NuGet.Core/NuGet.CommandLine.XPlat/Commands/Package/Download/PackageDownloadCommand.cs index 900769d12e2..f480f6e27fa 100644 --- a/src/NuGet.Core/NuGet.CommandLine.XPlat/Commands/Package/Download/PackageDownloadCommand.cs +++ b/src/NuGet.Core/NuGet.CommandLine.XPlat/Commands/Package/Download/PackageDownloadCommand.cs @@ -4,7 +4,6 @@ using System; using System.Collections.Generic; using System.CommandLine; -using System.CommandLine.Help; using System.Threading; using System.Threading.Tasks; @@ -16,60 +15,50 @@ internal static void Register(Command packageCommand, Option interactiveOp { Register(packageCommand, interactiveOption, PackageDownloadRunner.RunAsync); } + public static void Register(Command packageCommand, Option interactiveOption, Func> action) { var downloadCommand = new DocumentedCommand( "download", - Strings.pkgDownload_descritpion, + Strings.PackageDownloadCommand_descritpion, "https://aka.ms/dotnet/package/download"); // Arguments - var packagesArguments = new Argument>("packages") + var packagesArguments = new Argument>("packages") { Description = Strings.PackageUpdate_PackageArgumentDescription, - Arity = ArgumentArity.ZeroOrMore, - CustomParser = Package.Parse + Arity = ArgumentArity.OneOrMore, + CustomParser = PackageWithNuGetVersion.Parse }; // Options var allowInsecureConnections = new Option("--allow-insecure-connections") { - Description = Strings.pkgDownload_AllowInsecureConnectionsDescritption, + Description = Strings.PackageDownloadCommand_AllowInsecureConnectionsDescritption, Arity = ArgumentArity.Zero }; var configFile = new Option("--configfile") { - Description = Strings.pkgDownload_configFileDesciption, + Description = Strings.Option_ConfigFile, Arity = ArgumentArity.ExactlyOne }; - var downloadOnly = new Option("--download-only") - { - Description = Strings.pkgDownload_downloadOnlyDeciption, - Arity = ArgumentArity.Zero - }; - - var help = new HelpOption() - { - Arity = ArgumentArity.Zero - }; - - var outputDirectory = new Option("--output-directory") + var outputDirectory = new Option("--output", "-o") { - Description = Strings.pkgDownload_OutputDirectoryDescription, + Description = Strings.PackageDownloadCommand_OutputDirectoryDescription, Arity = ArgumentArity.ExactlyOne }; var prerelease = new Option("--prerelease") { - Description = Strings.pkgDownload_prereleaseDescription, + Description = Strings.Prerelease_Description, Arity = ArgumentArity.Zero }; - var sources = new Option>("--source") + var sources = new Option>("--source", "-s") { - Description = Strings.pkgDownload_sourcesDescription, + Description = Strings.PackageDownloadCommand_SourcesDescription, Arity = ArgumentArity.OneOrMore }; @@ -79,8 +68,6 @@ public static void Register(Command packageCommand, Option interactiveOpti downloadCommand.Arguments.Add(packagesArguments); downloadCommand.Options.Add(allowInsecureConnections); downloadCommand.Options.Add(configFile); - downloadCommand.Options.Add(downloadOnly); - downloadCommand.Options.Add(help); downloadCommand.Options.Add(interactiveOption); downloadCommand.Options.Add(outputDirectory); downloadCommand.Options.Add(prerelease); @@ -89,14 +76,13 @@ public static void Register(Command packageCommand, Option interactiveOpti downloadCommand.SetAction(async (parserResult, cancellationToken) => { - IReadOnlyList packages = parserResult.GetValue(packagesArguments) ?? []; + IReadOnlyList packages = parserResult.GetValue(packagesArguments) ?? []; var args = new PackageDownloadArgs() { Packages = packages, Sources = parserResult.GetValue(sources), OutputDirectory = parserResult.GetValue(outputDirectory), IncludePrerelease = parserResult.GetValue(prerelease), - DownloadOnly = parserResult.GetValue(downloadOnly), AllowInsecureConnections = parserResult.GetValue(allowInsecureConnections), Interactive = parserResult.GetValue(interactiveOption), ConfigFile = parserResult.GetValue(configFile), diff --git a/src/NuGet.Core/NuGet.CommandLine.XPlat/Commands/Package/Download/PackageDownloadRunner.cs b/src/NuGet.Core/NuGet.CommandLine.XPlat/Commands/Package/Download/PackageDownloadRunner.cs index e77215eced2..7e42dc83897 100644 --- a/src/NuGet.Core/NuGet.CommandLine.XPlat/Commands/Package/Download/PackageDownloadRunner.cs +++ b/src/NuGet.Core/NuGet.CommandLine.XPlat/Commands/Package/Download/PackageDownloadRunner.cs @@ -8,6 +8,7 @@ using System.Linq; using System.Threading; using System.Threading.Tasks; +using NuGet.CommandLine.XPlat.Utility; using NuGet.Commands; using NuGet.Configuration; using NuGet.Credentials; @@ -34,16 +35,13 @@ public static async Task RunAsync(PackageDownloadArgs args, CancellationTok HidePrefixForInfoAndMinimal = true }; - // Custom logger for package downloads. - // It wraps the provided logger and uses verbosity levels to determine which messages to log. XPlatUtility.ConfigureProtocol(); DefaultCredentialServiceUtility.SetupDefaultCredentialService(logger, !args.Interactive); ISettings settings = Settings.LoadDefaultSettings( Directory.GetCurrentDirectory(), args.ConfigFile, new XPlatMachineWideSetting()); - PackageSourceProvider sourceProvider = new PackageSourceProvider(settings); - var packageSources = GetPackageSources(args.Sources, sourceProvider); + IEnumerable packageSources = GetPackageSources(args.Sources, new PackageSourceProvider(settings)); return await RunAsync(args, logger, packageSources, settings, token); } @@ -51,53 +49,34 @@ public static async Task RunAsync(PackageDownloadArgs args, CancellationTok public static async Task RunAsync(PackageDownloadArgs args, ILoggerWithColor logger, IEnumerable packageSources, ISettings settings, CancellationToken token) { // Check for insecure sources - if (!args.AllowInsecureConnections) + if (DetectAndReportInsecureSources(args.AllowInsecureConnections, packageSources, logger)) { - var insecureSources = packageSources.Where(source => source.SourceUri.AbsoluteUri.StartsWith("http://", StringComparison.OrdinalIgnoreCase)); - - if (insecureSources.Any()) - { - if (insecureSources.Count() == 1) - { - logger.LogError(string.Format( - CultureInfo.CurrentCulture, - Strings.Error_HttpServerUsage, - "download", - insecureSources.First())); - } - else - { - logger.LogError(string.Format( - CultureInfo.CurrentCulture, - Strings.Error_HttpServerUsage_MultipleSources, - "download", - string.Join(", ", insecureSources))); - } - - return ExitCodeError; - } + return ExitCodeError; } - var cache = new SourceCacheContext() - { - NoCache = true, - DirectDownload = true - }; - + string outputDirectory = args.OutputDirectory ?? Directory.GetCurrentDirectory(); + var cache = new SourceCacheContext(); + IEnumerable sourceRepositories = GetSourceRepositories(packageSources); bool downloadedAllSuccessfully = true; foreach (var package in args.Packages) { logger.LogMinimal(string.Format( CultureInfo.CurrentCulture, - Strings.PkgDownload_Starting, + Strings.PackageDownloadCommand_Starting, package.Id, - string.IsNullOrEmpty(package.VersionRange?.ToNormalizedString()) ? "latest version" : package.VersionRange.ToNormalizedString())); + string.IsNullOrEmpty(package.NuGetVersion?.ToNormalizedString()) ? Strings.PackageDownloadCommand_LatestVersion : package.NuGetVersion.ToNormalizedString())); -#pragma warning disable CA1031 // Do not catch general exception types try { - (NuGetVersion version, SourceRepository downloadRepository) = await ResolvePackageDownloadVersion(package, packageSources, cache, logger, args.IncludePrerelease, token); + (NuGetVersion version, SourceRepository downloadRepository) = + await ResolvePackageDownloadVersion( + package, + sourceRepositories, + cache, + logger, + args.IncludePrerelease, + token); if (version == null) { @@ -106,13 +85,13 @@ public static async Task RunAsync(PackageDownloadArgs args, ILoggerWithColo continue; } - bool download = await InstallPackageAsync( + bool download = await DownloadPackageAsync( package.Id, version, downloadRepository, cache, settings, - args.OutputDirectory, + outputDirectory, logger, token); @@ -120,22 +99,23 @@ public static async Task RunAsync(PackageDownloadArgs args, ILoggerWithColo { logger.LogMinimal(string.Format( CultureInfo.CurrentCulture, - Strings.PkgDownload_Succeeded, + Strings.PackageDownloadCommand_Succeeded, package.Id, version, - args.OutputDirectory)); + outputDirectory)); } else { logger.LogError(string.Format( CultureInfo.CurrentCulture, - Strings.PkgDownload_Failed, + Strings.PackageDownloadCommand_Failed, package.Id, version)); downloadedAllSuccessfully &= false; } } +#pragma warning disable CA1031 // Do not catch general exception types catch (Exception ex) { logger.LogError(ex.ToString()); @@ -148,77 +128,67 @@ public static async Task RunAsync(PackageDownloadArgs args, ILoggerWithColo } internal static async Task<(NuGetVersion, SourceRepository)> ResolvePackageDownloadVersion( - Package package, - IEnumerable packageSources, + PackageWithNuGetVersion package, + IEnumerable sourceRepositories, SourceCacheContext cache, ILoggerWithColor logger, bool includePrerelease, CancellationToken token) { - // Determine if an exact version is requested. - // If the original string contains a comma, it's a range. - bool isExactVersionRequested = - package.VersionRange != null && - !string.IsNullOrEmpty(package.VersionRange.OriginalString) && - !package.VersionRange.OriginalString.Contains(","); - NuGetVersion requestedExactVersion = package.VersionRange?.MinVersion; - NuGetVersion versionToDownload = null; SourceRepository downloadSourceRepository = null; + bool versionSpecified = package.NuGetVersion != null; - bool pickLatest = false; - - if (package.VersionRange == null) + foreach (var repo in sourceRepositories) { - // If the version is not defined, pick the latest - pickLatest = true; - } + var finder = await repo.GetResourceAsync(token); + var packages = await finder.GetMetadataAsync( + package.Id, + includePrerelease, + includeUnlisted: false, + sourceCacheContext: cache, + logger, + token); - foreach (var source in packageSources) - { - var repo = Repository.Factory.GetCoreV3(source); - var finder = await repo.GetResourceAsync(token); - var versions = await finder.GetAllVersionsAsync(package.Id, cache, logger, token); + if (packages == null) + { + continue; + } - if (isExactVersionRequested) + var versions = packages?.Select(p => p.Identity.Version); + if (versionSpecified) { - // If an exact version is requested, check if the version exists at this source - if (versions != null && versions.Contains(requestedExactVersion)) + // If an exact version is specified, check if it exists at this source + foreach (var p in packages) { - versionToDownload = requestedExactVersion; - downloadSourceRepository = repo; - return (versionToDownload, downloadSourceRepository); + if (p?.Identity?.Version == package.NuGetVersion) + { + return (package.NuGetVersion, repo); + } } - else - { - // continue to the next source - continue; - } - } - - // If a version range is specified, find the best match at this source - var candidates = versions? - .Where(v => (package.VersionRange == null || package.VersionRange.Satisfies(v)) && (includePrerelease || !v.IsPrerelease)) - .OrderByDescending(v => v); - var candidate = pickLatest ? candidates?.FirstOrDefault() : candidates?.LastOrDefault(); + continue; + } - if (candidate != null && (versionToDownload == null || pickLatest ? candidate > versionToDownload : candidate < versionToDownload)) + foreach (var p in packages) { - versionToDownload = candidate; - downloadSourceRepository = repo; + var v = p.Identity.Version; + if (versionToDownload is null || v > versionToDownload) + { + versionToDownload = v; + } } } if (versionToDownload == null) { - logger.LogError("Unable to find a valid package version"); + logger.LogError(Strings.Error_PackageDownload_VersionNotFound); } return (versionToDownload, downloadSourceRepository); } - private static async Task InstallPackageAsync( + private static async Task DownloadPackageAsync( string id, NuGetVersion version, SourceRepository repo, @@ -242,7 +212,7 @@ private static async Task InstallPackageAsync( { logger.LogMinimal(string.Format( CultureInfo.CurrentCulture, - Strings.PkgDownload_AlreadyInstalled, + Strings.PackageDownloadCommand_AlreadyInstalled, id, version.ToNormalizedString(), outputDirectory)); @@ -281,7 +251,7 @@ private static async Task InstallPackageAsync( // Unable to download the package logger.LogError(string.Format( CultureInfo.CurrentCulture, - Strings.PkgDownload_UnableToDownload, + Strings.PackageDownloadCommand_UnableToDownload, id, version.ToNormalizedString(), repo.PackageSource.Source)); @@ -312,5 +282,35 @@ private static IEnumerable GetPackageSources(IList source return packageSources; } + + private static bool DetectAndReportInsecureSources( + bool allowInsecureConnections, + IEnumerable packageSources, + ILoggerWithColor logger) + { + if (!allowInsecureConnections) + { + var insecureSources = HttpSourcesUtility.GetDisallowedInsecureHttpSources([.. packageSources]); + if (insecureSources.Any()) + { + logger.LogError(HttpSourcesUtility.BuildHttpSourceErrorMessage(insecureSources, "package download")); + return true; + } + } + + return false; + } + + private static IEnumerable GetSourceRepositories(IEnumerable packageSources) + { + IEnumerable> providers = Repository.Provider.GetCoreV3(); + List sourceRepositories = []; + foreach (var source in packageSources) + { + sourceRepositories.Add(Repository.CreateSource(providers, source, FeedType.Undefined)); + } + + return sourceRepositories; + } } } diff --git a/src/NuGet.Core/NuGet.CommandLine.XPlat/Commands/PackageReferenceCommands/AddPackageReferenceCommand.cs b/src/NuGet.Core/NuGet.CommandLine.XPlat/Commands/PackageReferenceCommands/AddPackageReferenceCommand.cs index 41d770b98db..e42ac09ccdf 100644 --- a/src/NuGet.Core/NuGet.CommandLine.XPlat/Commands/PackageReferenceCommands/AddPackageReferenceCommand.cs +++ b/src/NuGet.Core/NuGet.CommandLine.XPlat/Commands/PackageReferenceCommands/AddPackageReferenceCommand.cs @@ -72,7 +72,7 @@ public static void Register(CommandLineApplication app, Func getLogger, var prerelease = addpkg.Option( "--prerelease", - Strings.AddPkg_PackagePrerelease, + Strings.Prerelease_Description, CommandOptionType.NoValue); addpkg.OnExecute(() => diff --git a/src/NuGet.Core/NuGet.CommandLine.XPlat/Program.cs b/src/NuGet.Core/NuGet.CommandLine.XPlat/Program.cs index f625524ad53..da6afdf4c6c 100644 --- a/src/NuGet.Core/NuGet.CommandLine.XPlat/Program.cs +++ b/src/NuGet.Core/NuGet.CommandLine.XPlat/Program.cs @@ -8,6 +8,7 @@ using System.Linq; using System.Threading; using Microsoft.Extensions.CommandLineUtils; +using NuGet.CommandLine.XPlat.Commands.Package.PackageDownload; using NuGet.CommandLine.XPlat.Commands.Package.Update; using NuGet.Commands; using NuGet.Common; @@ -100,6 +101,9 @@ public static int MainInternal(string[] args, CommandOutputLogger log, IEnvironm PackageSearchCommand.Register(packageCommand, getHidePrefixLogger); PackageUpdateCommand.Register(packageCommand, interactiveOption); +#if DEBUG + PackageDownloadCommand.Register(packageCommand, interactiveOption); +#endif } else { @@ -246,7 +250,7 @@ private static bool IsSystemCommandLineParsedCommand(string[] args) if (args.Length >= 2 && arg0 == "package") { string arg1 = args[1]; - if (arg1 == "search" || arg1 == "update") + if (arg1 == "search" || arg1 == "update" || arg1 == "download") { return true; } diff --git a/src/NuGet.Core/NuGet.CommandLine.XPlat/Strings.Designer.cs b/src/NuGet.Core/NuGet.CommandLine.XPlat/Strings.Designer.cs index b57132df8bc..9eaf5532b96 100644 --- a/src/NuGet.Core/NuGet.CommandLine.XPlat/Strings.Designer.cs +++ b/src/NuGet.Core/NuGet.CommandLine.XPlat/Strings.Designer.cs @@ -150,15 +150,6 @@ internal static string AddPkg_PackageIdDescription { } } - /// - /// Looks up a localized string similar to Allows prerelease packages to be installed.. - /// - internal static string AddPkg_PackagePrerelease { - get { - return ResourceManager.GetString("AddPkg_PackagePrerelease", resourceCulture); - } - } - /// /// Looks up a localized string similar to Version of the package to be added.. /// @@ -681,14 +672,6 @@ internal static string Error_InvalidVersion { return ResourceManager.GetString("Error_InvalidVersion", resourceCulture); } } - - /// Looks up a localized string similar to Invalid verbosity level '{0}'. Allowed values are: quiet, normal, detailed.. - /// - internal static string Error_InvalidVerbosity { - get { - return ResourceManager.GetString("Error_InvalidVerbosity", resourceCulture); - } - } /// /// Looks up a localized string similar to Invalid version range '{0}'. @@ -771,6 +754,15 @@ internal static string Error_NoVersionsAvailable { } } + /// + /// Looks up a localized string similar to Unable to find a valid package version. + /// + internal static string Error_PackageDownload_VersionNotFound { + get { + return ResourceManager.GetString("Error_PackageDownload_VersionNotFound", resourceCulture); + } + } + /// /// Looks up a localized string similar to Package source mapping is enabled, but no mapping for package {0} was found. See https://aka.ms/nuget/psm for more information.. /// @@ -1607,308 +1599,263 @@ internal static string OutputNuGetVersion { } /// - /// Looks up a localized string similar to All packages are already up to date.. - /// - internal static string PackageUpdate_AllPackagesAlreadyUpToDate { - get { - return ResourceManager.GetString("PackageUpdate_AllPackagesAlreadyUpToDate", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to All versions of package {0} have security advisories. Unable to find a non-vulnerable version to update to.. - /// - internal static string PackageUpdate_AllVersionsHaveAdvisories { - get { - return ResourceManager.GetString("PackageUpdate_AllVersionsHaveAdvisories", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to All packages are up to date. - /// - internal static string PackageUpdate_AlreadyUpToDate { - get { - return ResourceManager.GetString("PackageUpdate_AlreadyUpToDate", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to This command requires NuGetAudit to be enabled at this time. See https://aka.ms/nuget/audit for more information.. - /// - internal static string PackageUpdate_AuditDisabled { - get { - return ResourceManager.GetString("PackageUpdate_AuditDisabled", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to NuGetAuditMode is set to direct, which means that transitive packages with known vulnerabilities will not be updated. See https://aka.ms/nuget/audit for more information.. + /// Looks up a localized string similar to Allows downloading from HTTP (non-HTTPS) package sources.. /// - internal static string PackageUpdate_AuditModeIsDirect { + internal static string PackageDownloadCommand_AllowInsecureConnectionsDescritption { get { - return ResourceManager.GetString("PackageUpdate_AuditModeIsDirect", resourceCulture); + return ResourceManager.GetString("PackageDownloadCommand_AllowInsecureConnectionsDescritption", resourceCulture); } } /// - /// Looks up a localized string similar to Updated {0} packages in {1} scanned packages.. + /// Looks up a localized string similar to Skipping download. Package '{0}' version {1} already exists at '{2}'.. /// - internal static string PackageUpdate_FinalSummary { + internal static string PackageDownloadCommand_AlreadyInstalled { get { - return ResourceManager.GetString("PackageUpdate_FinalSummary", resourceCulture); + return ResourceManager.GetString("PackageDownloadCommand_AlreadyInstalled", resourceCulture); } } /// - /// Looks up a localized string similar to Finding versions of packages to update.. + /// Looks up a localized string similar to Downloads a NuGet package to a local folder without requiring a project file.. /// - internal static string PackageUpdate_FindingUpdateVersions { + internal static string PackageDownloadCommand_descritpion { get { - return ResourceManager.GetString("PackageUpdate_FindingUpdateVersions", resourceCulture); + return ResourceManager.GetString("PackageDownloadCommand_descritpion", resourceCulture); } } /// - /// Looks up a localized string similar to Loading Project(s).. + /// Looks up a localized string similar to Package '{0}' ({1}) failed to download.. /// - internal static string PackageUpdate_LoadingDGSpec { + internal static string PackageDownloadCommand_Failed { get { - return ResourceManager.GetString("PackageUpdate_LoadingDGSpec", resourceCulture); + return ResourceManager.GetString("PackageDownloadCommand_Failed", resourceCulture); } } /// - /// Looks up a localized string similar to No upgradable packages have known vulnerabilities.. + /// Looks up a localized string similar to latest version. /// - internal static string PackageUpdate_NoVulnerablePackages { + internal static string PackageDownloadCommand_LatestVersion { get { - return ResourceManager.GetString("PackageUpdate_NoVulnerablePackages", resourceCulture); + return ResourceManager.GetString("PackageDownloadCommand_LatestVersion", resourceCulture); } } /// - /// Looks up a localized string similar to Package reference in the form of a package identifier like 'Newtonsoft.Json' or package identifier and version separated by '@' like 'Newtonsoft.Json@13.0.3'.. + /// Looks up a localized string similar to Directory where the package will be placed. Defaults to the current working directory.. /// - internal static string PackageUpdate_PackageArgumentDescription { + internal static string PackageDownloadCommand_OutputDirectoryDescription { get { - return ResourceManager.GetString("PackageUpdate_PackageArgumentDescription", resourceCulture); + return ResourceManager.GetString("PackageDownloadCommand_OutputDirectoryDescription", resourceCulture); } } /// - /// Looks up a localized string similar to Running preview restore to check package compatibility.. + /// Looks up a localized string similar to Package identifier (e.g. 'Newtonsoft.Json').. /// - internal static string PackageUpdate_PreviewRestore { + internal static string PackageDownloadCommand_PackageIdDescription { get { - return ResourceManager.GetString("PackageUpdate_PreviewRestore", resourceCulture); + return ResourceManager.GetString("PackageDownloadCommand_PackageIdDescription", resourceCulture); } } /// - /// Looks up a localized string similar to Preview restore with updated packages was not successful. Force mode is not yet available.. + /// Looks up a localized string similar to Specifies one or more NuGet package sources to use.. /// - internal static string PackageUpdate_PreviewRestoreFailed { + internal static string PackageDownloadCommand_SourcesDescription { get { - return ResourceManager.GetString("PackageUpdate_PreviewRestoreFailed", resourceCulture); + return ResourceManager.GetString("PackageDownloadCommand_SourcesDescription", resourceCulture); } } /// - /// Looks up a localized string similar to Updating {0} {1} to {2}.. - /// - internal static string PackageUpdate_UpdatedMessage { - get { - return ResourceManager.GetString("PackageUpdate_UpdatedMessage", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to Updating outdated packages in {0}.. + /// Looks up a localized string similar to Downloading package {0}, version {1}.. /// - internal static string PackageUpdate_UpdatingOutdatedPackages { + internal static string PackageDownloadCommand_Starting { get { - return ResourceManager.GetString("PackageUpdate_UpdatingOutdatedPackages", resourceCulture); + return ResourceManager.GetString("PackageDownloadCommand_Starting", resourceCulture); } } /// - /// Looks up a localized string similar to Updating packages with security advisories in {0}. + /// Looks up a localized string similar to Package '{0}' ({1}) successfully downloaded to '{2}'.. /// - internal static string PackageUpdate_UpdatingVulnerablePackages { + internal static string PackageDownloadCommand_Succeeded { get { - return ResourceManager.GetString("PackageUpdate_UpdatingVulnerablePackages", resourceCulture); + return ResourceManager.GetString("PackageDownloadCommand_Succeeded", resourceCulture); } } /// - /// Looks up a localized string similar to Update referenced packages in a project or solution.. + /// Looks up a localized string similar to Package download failed for `{0} {1}` from source `{2}`.. /// - internal static string PackageUpdateCommand_Description { + internal static string PackageDownloadCommand_UnableToDownload { get { - return ResourceManager.GetString("PackageUpdateCommand_Description", resourceCulture); + return ResourceManager.GetString("PackageDownloadCommand_UnableToDownload", resourceCulture); } } /// - /// Looks up a localized string similar to Path to a project or solution file, or a directory.. + /// Looks up a localized string similar to All packages are already up to date.. /// - internal static string PackageUpdateCommand_ProjectOptionDescription { + internal static string PackageUpdate_AllPackagesAlreadyUpToDate { get { - return ResourceManager.GetString("PackageUpdateCommand_ProjectOptionDescription", resourceCulture); + return ResourceManager.GetString("PackageUpdate_AllPackagesAlreadyUpToDate", resourceCulture); } } /// - /// Looks up a localized string similar to Upgrade packages with known vulnerabilities.. + /// Looks up a localized string similar to All versions of package {0} have security advisories. Unable to find a non-vulnerable version to update to.. /// - internal static string PackageUpdateCommand_VulnerableOptionDescription { + internal static string PackageUpdate_AllVersionsHaveAdvisories { get { - return ResourceManager.GetString("PackageUpdateCommand_VulnerableOptionDescription", resourceCulture); + return ResourceManager.GetString("PackageUpdate_AllVersionsHaveAdvisories", resourceCulture); } } /// - /// Looks up a localized string similar to Allows downloading from HTTP (non-HTTPS) package sources.. + /// Looks up a localized string similar to All packages are up to date. /// - internal static string pkgDownload_AllowInsecureConnectionsDescritption { + internal static string PackageUpdate_AlreadyUpToDate { get { - return ResourceManager.GetString("pkgDownload_AllowInsecureConnectionsDescritption", resourceCulture); + return ResourceManager.GetString("PackageUpdate_AlreadyUpToDate", resourceCulture); } } /// - /// Looks up a localized string similar to Skipping download. Package '{0}' version {1} already exists at '{2}'.. + /// Looks up a localized string similar to This command requires NuGetAudit to be enabled at this time. See https://aka.ms/nuget/audit for more information.. /// - internal static string PkgDownload_AlreadyInstalled { + internal static string PackageUpdate_AuditDisabled { get { - return ResourceManager.GetString("PkgDownload_AlreadyInstalled", resourceCulture); + return ResourceManager.GetString("PackageUpdate_AuditDisabled", resourceCulture); } } /// - /// Looks up a localized string similar to Path to a NuGet.config file to use for settings and sources.. + /// Looks up a localized string similar to NuGetAuditMode is set to direct, which means that transitive packages with known vulnerabilities will not be updated. See https://aka.ms/nuget/audit for more information.. /// - internal static string pkgDownload_configFileDesciption { + internal static string PackageUpdate_AuditModeIsDirect { get { - return ResourceManager.GetString("pkgDownload_configFileDesciption", resourceCulture); + return ResourceManager.GetString("PackageUpdate_AuditModeIsDirect", resourceCulture); } } /// - /// Looks up a localized string similar to Downloads a NuGet package to a local folder without requiring a project file.. + /// Looks up a localized string similar to Updated {0} packages in {1} scanned packages.. /// - internal static string pkgDownload_descritpion { + internal static string PackageUpdate_FinalSummary { get { - return ResourceManager.GetString("pkgDownload_descritpion", resourceCulture); + return ResourceManager.GetString("PackageUpdate_FinalSummary", resourceCulture); } } /// - /// Looks up a localized string similar to Download only the .nupkg file without extracting it.. + /// Looks up a localized string similar to Finding versions of packages to update.. /// - internal static string pkgDownload_downloadOnlyDeciption { + internal static string PackageUpdate_FindingUpdateVersions { get { - return ResourceManager.GetString("pkgDownload_downloadOnlyDeciption", resourceCulture); + return ResourceManager.GetString("PackageUpdate_FindingUpdateVersions", resourceCulture); } } /// - /// Looks up a localized string similar to Package '{0}' ({1}) failed to download.. + /// Looks up a localized string similar to Loading Project(s).. /// - internal static string PkgDownload_Failed { + internal static string PackageUpdate_LoadingDGSpec { get { - return ResourceManager.GetString("PkgDownload_Failed", resourceCulture); + return ResourceManager.GetString("PackageUpdate_LoadingDGSpec", resourceCulture); } } /// - /// Looks up a localized string similar to Allows interactive authentication if required.. + /// Looks up a localized string similar to No upgradable packages have known vulnerabilities.. /// - internal static string pkgDownload_interactiveDecription { + internal static string PackageUpdate_NoVulnerablePackages { get { - return ResourceManager.GetString("pkgDownload_interactiveDecription", resourceCulture); + return ResourceManager.GetString("PackageUpdate_NoVulnerablePackages", resourceCulture); } } /// - /// Looks up a localized string similar to Directory where the package will be placed. Defaults to the current working directory.. + /// Looks up a localized string similar to Package reference in the form of a package identifier like 'Newtonsoft.Json' or package identifier and version separated by '@' like 'Newtonsoft.Json@13.0.3'.. /// - internal static string pkgDownload_OutputDirectoryDescription { + internal static string PackageUpdate_PackageArgumentDescription { get { - return ResourceManager.GetString("pkgDownload_OutputDirectoryDescription", resourceCulture); + return ResourceManager.GetString("PackageUpdate_PackageArgumentDescription", resourceCulture); } } /// - /// Looks up a localized string similar to Package identifier (e.g. 'Newtonsoft.Json').. + /// Looks up a localized string similar to Running preview restore to check package compatibility.. /// - internal static string pkgDownload_packageIdDescription { + internal static string PackageUpdate_PreviewRestore { get { - return ResourceManager.GetString("pkgDownload_packageIdDescription", resourceCulture); + return ResourceManager.GetString("PackageUpdate_PreviewRestore", resourceCulture); } } /// - /// Looks up a localized string similar to Allows downloading prerelease versions.. + /// Looks up a localized string similar to Preview restore with updated packages was not successful. Force mode is not yet available.. /// - internal static string pkgDownload_prereleaseDescription { + internal static string PackageUpdate_PreviewRestoreFailed { get { - return ResourceManager.GetString("pkgDownload_prereleaseDescription", resourceCulture); + return ResourceManager.GetString("PackageUpdate_PreviewRestoreFailed", resourceCulture); } } /// - /// Looks up a localized string similar to Specifies one or more NuGet package sources to use.. + /// Looks up a localized string similar to Updating {0} {1} to {2}.. /// - internal static string pkgDownload_sourcesDescription { + internal static string PackageUpdate_UpdatedMessage { get { - return ResourceManager.GetString("pkgDownload_sourcesDescription", resourceCulture); + return ResourceManager.GetString("PackageUpdate_UpdatedMessage", resourceCulture); } } /// - /// Looks up a localized string similar to Downloading package {0}, version {1}.. + /// Looks up a localized string similar to Updating outdated packages in {0}.. /// - internal static string PkgDownload_Starting { + internal static string PackageUpdate_UpdatingOutdatedPackages { get { - return ResourceManager.GetString("PkgDownload_Starting", resourceCulture); + return ResourceManager.GetString("PackageUpdate_UpdatingOutdatedPackages", resourceCulture); } } /// - /// Looks up a localized string similar to Package '{0}' ({1}) successfully downloaded to '{2}'.. + /// Looks up a localized string similar to Updating packages with security advisories in {0}. /// - internal static string PkgDownload_Succeeded { + internal static string PackageUpdate_UpdatingVulnerablePackages { get { - return ResourceManager.GetString("PkgDownload_Succeeded", resourceCulture); + return ResourceManager.GetString("PackageUpdate_UpdatingVulnerablePackages", resourceCulture); } } /// - /// Looks up a localized string similar to Package download failed for `{0} {1}` from source `{2}`.. + /// Looks up a localized string similar to Update referenced packages in a project or solution.. /// - internal static string PkgDownload_UnableToDownload { + internal static string PackageUpdateCommand_Description { get { - return ResourceManager.GetString("PkgDownload_UnableToDownload", resourceCulture); + return ResourceManager.GetString("PackageUpdateCommand_Description", resourceCulture); } } /// - /// Looks up a localized string similar to Set the verbosity level. Allowed values: quiet | normal | detailed.. + /// Looks up a localized string similar to Path to a project or solution file, or a directory.. /// - internal static string pkgDownload_verbosityDescription { + internal static string PackageUpdateCommand_ProjectOptionDescription { get { - return ResourceManager.GetString("pkgDownload_verbosityDescription", resourceCulture); + return ResourceManager.GetString("PackageUpdateCommand_ProjectOptionDescription", resourceCulture); } } /// - /// Looks up a localized string similar to Specific package version to download. + /// Looks up a localized string similar to Upgrade packages with known vulnerabilities.. /// - internal static string pkgDownload_versionDescription { + internal static string PackageUpdateCommand_VulnerableOptionDescription { get { - return ResourceManager.GetString("pkgDownload_versionDescription", resourceCulture); + return ResourceManager.GetString("PackageUpdateCommand_VulnerableOptionDescription", resourceCulture); } } @@ -2002,6 +1949,15 @@ internal static string pkgSearch_VerbosityDescription { } } + /// + /// Looks up a localized string similar to Allows prerelease packages to be installed.. + /// + internal static string Prerelease_Description { + get { + return ResourceManager.GetString("Prerelease_Description", resourceCulture); + } + } + /// /// Looks up a localized string similar to There are no stable versions available, {0} is the best available. Consider adding the --prerelease option. /// diff --git a/src/NuGet.Core/NuGet.CommandLine.XPlat/Strings.resx b/src/NuGet.Core/NuGet.CommandLine.XPlat/Strings.resx index 7c35466b93d..03ae477ee23 100644 --- a/src/NuGet.Core/NuGet.CommandLine.XPlat/Strings.resx +++ b/src/NuGet.Core/NuGet.CommandLine.XPlat/Strings.resx @@ -586,7 +586,7 @@ For more information, visit https://docs.nuget.org/docs/reference/command-line-r Set the verbosity level of the command. Allowed values are q[uiet], m[inimal], n[ormal], d[etailed], and diag[nostic]. Please don't localize "q[uiet]", "m[inimal]", "n[ormal]", "d[etailed]", or "diag[nostic]" - + Allows prerelease packages to be installed. @@ -1108,71 +1108,53 @@ Do not translate "PackageVersion" Invalid version value '{0}'. 0 - package version - + Downloads a NuGet package to a local folder without requiring a project file. - + Package identifier (e.g. 'Newtonsoft.Json'). - + Allows downloading from HTTP (non-HTTPS) package sources. - - Path to a NuGet.config file to use for settings and sources. - - - Download only the .nupkg file without extracting it. - - - Allows interactive authentication if required. - - + Directory where the package will be placed. Defaults to the current working directory. - - Allows downloading prerelease versions. - - + Specifies one or more NuGet package sources to use. - - Set the verbosity level. Allowed values: quiet | normal | detailed. - Please do not translate `quiet`, `normal`, `detailed` - - - Specific package version to download - - + Downloading package {0}, version {1}. 0- package id 1- package version - + Skipping download. Package '{0}' version {1} already exists at '{2}'. 0 - package id 1 - version 2 - directory path where the package was downloaded - + Package '{0}' ({1}) successfully downloaded to '{2}'. 0 - package id 1 - package version 2 - package download location - + Package '{0}' ({1}) failed to download. 0 - package id 1 - package version - - Invalid verbosity level '{0}'. Allowed values are: quiet, normal, detailed. - 0 - verbosity level -do not translate: quiet, normal, detailed. - - + Package download failed for `{0} {1}` from source `{2}`. 0 package id 1 package version 2 package source + + latest version + + + Unable to find a valid package version + \ No newline at end of file diff --git a/src/NuGet.Core/NuGet.CommandLine.XPlat/xlf/Strings.cs.xlf b/src/NuGet.Core/NuGet.CommandLine.XPlat/xlf/Strings.cs.xlf index 5c317e7a031..7fd60d6720c 100644 --- a/src/NuGet.Core/NuGet.CommandLine.XPlat/xlf/Strings.cs.xlf +++ b/src/NuGet.Core/NuGet.CommandLine.XPlat/xlf/Strings.cs.xlf @@ -47,11 +47,6 @@ ID balíčku, který se má přidat - - Allows prerelease packages to be installed. - Umožňuje instalaci předběžných verzí balíčků. - - Version of the package to be added. Verze balíčku, který se má přidat @@ -358,12 +353,6 @@ NuGet vyžaduje zdroje HTTPS. Pokud chcete používat zdroje HTTP, musíte v sou Invalid version value '{0}'. 0 - package version - - Invalid verbosity level '{0}'. Allowed values are: quiet, normal, detailed. - Invalid verbosity level '{0}'. Allowed values are: quiet, normal, detailed. - 0 - verbosity level -do not translate: quiet, normal, detailed. - Invalid version range '{0}' Neplatný rozsah verzí {0} @@ -409,6 +398,11 @@ do not translate: quiet, normal, detailed. Projekt {0} používá pro balíčky NuGet package.config, ale daný příkaz funguje jen s projekty odkazů na balíčky. + + Unable to find a valid package version + Unable to find a valid package version + + Package source mapping is enabled, but no mapping for package {0} was found. See https://aka.ms/nuget/psm for more information. Mapování zdroje balíčku je povolené, ale pro balíček {0} nebylo nalezeno žádné mapování. Další informace najdete na https://aka.ms/nuget/audit. @@ -897,6 +891,69 @@ Další informace najdete tady: https://docs.nuget.org/docs/reference/command-li {0}: App full name {1}: App version + + Allows downloading from HTTP (non-HTTPS) package sources. + Allows downloading from HTTP (non-HTTPS) package sources. + + + + Skipping download. Package '{0}' version {1} already exists at '{2}'. + Skipping download. Package '{0}' version {1} already exists at '{2}'. + 0 - package id +1 - version +2 - directory path where the package was downloaded + + + Package '{0}' ({1}) failed to download. + Package '{0}' ({1}) failed to download. + 0 - package id +1 - package version + + + latest version + latest version + + + + Directory where the package will be placed. Defaults to the current working directory. + Directory where the package will be placed. Defaults to the current working directory. + + + + Package identifier (e.g. 'Newtonsoft.Json'). + Package identifier (e.g. 'Newtonsoft.Json'). + + + + Specifies one or more NuGet package sources to use. + Specifies one or more NuGet package sources to use. + + + + Downloading package {0}, version {1}. + Downloading package {0}, version {1}. + 0- package id +1- package version + + + Package '{0}' ({1}) successfully downloaded to '{2}'. + Package '{0}' ({1}) successfully downloaded to '{2}'. + 0 - package id +1 - package version +2 - package download location + + + Package download failed for `{0} {1}` from source `{2}`. + Package download failed for `{0} {1}` from source `{2}`. + 0 package id +1 package version +2 package source + + + Downloads a NuGet package to a local folder without requiring a project file. + Downloads a NuGet package to a local folder without requiring a project file. + + Update referenced packages in a project or solution. Aktualizujte odkazované balíčky v projektu nebo řešení. @@ -990,44 +1047,16 @@ Další informace najdete tady: https://docs.nuget.org/docs/reference/command-li Aktualizují se balíčky s bezpečnostními upozorněními v {0} {0} - the project/solution being checked - - Skipping download. Package '{0}' version {1} already exists at '{2}'. - Skipping download. Package '{0}' version {1} already exists at '{2}'. - 0 - package id -1 - version -2 - directory path where the package was downloaded - - - Package '{0}' ({1}) failed to download. - Package '{0}' ({1}) failed to download. - 0 - package id -1 - package version - - - Downloading package {0}, version {1}. - Downloading package {0}, version {1}. - 0- package id -1- package version - - - Package '{0}' ({1}) successfully downloaded to '{2}'. - Package '{0}' ({1}) successfully downloaded to '{2}'. - 0 - package id -1 - package version -2 - package download location - - - Package download failed for `{0} {1}` from source `{2}`. - Package download failed for `{0} {1}` from source `{2}`. - 0 package id -1 package version -2 package source - There are no stable versions available, {0} is the best available. Consider adding the --prerelease option Nejsou k dispozici žádné stabilní verze, nejlepší dostupná verze je {0}. Zvažte možnost přidat --prerelease. {0} - Package Version + + Allows prerelease packages to be installed. + Allows prerelease packages to be installed. + + If a package and version already exists, skip it and continue with the next package in the push, if any. Jestliže balíček a verze už existuje, přeskočí se a bude se pokračovat dalším balíčkem k publikování, pokud nějaký existuje. @@ -1490,61 +1519,6 @@ Hledání porovnává řetězce bez rozlišování malých a velkých písmen po Soubor prostředků {0} pro projekt {1} neobsahuje cíl pro zadanou vstupní architekturu {2}. {0} - Assets file path, {1} - Project name, {2} - Framework - - Allows downloading from HTTP (non-HTTPS) package sources. - Allows downloading from HTTP (non-HTTPS) package sources. - - - - Directory where the package will be placed. Defaults to the current working directory. - Directory where the package will be placed. Defaults to the current working directory. - - - - Path to a NuGet.config file to use for settings and sources. - Path to a NuGet.config file to use for settings and sources. - - - - Downloads a NuGet package to a local folder without requiring a project file. - Downloads a NuGet package to a local folder without requiring a project file. - - - - Download only the .nupkg file without extracting it. - Download only the .nupkg file without extracting it. - - - - Allows interactive authentication if required. - Allows interactive authentication if required. - - - - Package identifier (e.g. 'Newtonsoft.Json'). - Package identifier (e.g. 'Newtonsoft.Json'). - - - - Allows downloading prerelease versions. - Allows downloading prerelease versions. - - - - Specifies one or more NuGet package sources to use. - Specifies one or more NuGet package sources to use. - - - - Set the verbosity level. Allowed values: quiet | normal | detailed. - Set the verbosity level. Allowed values: quiet | normal | detailed. - Please do not translate `quiet`, `normal`, `detailed` - - - Specific package version to download - Specific package version to download - - Searches one or more package sources for packages that match a search term. If no sources are specified, all sources defined in the NuGet.Config are used. Vyhledá v jednom nebo více zdrojích balíčků balíčky, které odpovídají hledanému výrazu. Pokud nejsou zadány žádné zdroje, budou použity všechny zdroje definované v NuGet.Config. diff --git a/src/NuGet.Core/NuGet.CommandLine.XPlat/xlf/Strings.de.xlf b/src/NuGet.Core/NuGet.CommandLine.XPlat/xlf/Strings.de.xlf index 54b6c05bcf2..ca9fd24f5d9 100644 --- a/src/NuGet.Core/NuGet.CommandLine.XPlat/xlf/Strings.de.xlf +++ b/src/NuGet.Core/NuGet.CommandLine.XPlat/xlf/Strings.de.xlf @@ -47,11 +47,6 @@ ID des hinzuzufügenden Pakets. - - Allows prerelease packages to be installed. - Ermöglicht die Installation von Paketen mit Vorabversionen. - - Version of the package to be added. Version des hinzuzufügenden Pakets. @@ -358,12 +353,6 @@ NuGet erfordert HTTPS-Quellen. Um HTTP-Quellen zu verwenden, müssen Sie „allo Invalid version value '{0}'. 0 - package version - - Invalid verbosity level '{0}'. Allowed values are: quiet, normal, detailed. - Invalid verbosity level '{0}'. Allowed values are: quiet, normal, detailed. - 0 - verbosity level -do not translate: quiet, normal, detailed. - Invalid version range '{0}' Ungültiger Versionsbereich „{0}“ @@ -409,6 +398,11 @@ do not translate: quiet, normal, detailed. Das Projekt "{0}" verwendet "package.config" für NuGet-Pakete, der Befehl funktioniert jedoch nur mit Projekten mit Paketverweis. + + Unable to find a valid package version + Unable to find a valid package version + + Package source mapping is enabled, but no mapping for package {0} was found. See https://aka.ms/nuget/psm for more information. Die Paketquellzuordnung ist aktiviert, aber es wurde keine Zuordnung für das Paket {0} gefunden. Weitere Informationen finden Sie unter https://aka.ms/nuget/psm. @@ -897,6 +891,69 @@ Weitere Informationen finden Sie unter: https://docs.nuget.org/docs/reference/co {0}: App full name {1}: App version + + Allows downloading from HTTP (non-HTTPS) package sources. + Allows downloading from HTTP (non-HTTPS) package sources. + + + + Skipping download. Package '{0}' version {1} already exists at '{2}'. + Skipping download. Package '{0}' version {1} already exists at '{2}'. + 0 - package id +1 - version +2 - directory path where the package was downloaded + + + Package '{0}' ({1}) failed to download. + Package '{0}' ({1}) failed to download. + 0 - package id +1 - package version + + + latest version + latest version + + + + Directory where the package will be placed. Defaults to the current working directory. + Directory where the package will be placed. Defaults to the current working directory. + + + + Package identifier (e.g. 'Newtonsoft.Json'). + Package identifier (e.g. 'Newtonsoft.Json'). + + + + Specifies one or more NuGet package sources to use. + Specifies one or more NuGet package sources to use. + + + + Downloading package {0}, version {1}. + Downloading package {0}, version {1}. + 0- package id +1- package version + + + Package '{0}' ({1}) successfully downloaded to '{2}'. + Package '{0}' ({1}) successfully downloaded to '{2}'. + 0 - package id +1 - package version +2 - package download location + + + Package download failed for `{0} {1}` from source `{2}`. + Package download failed for `{0} {1}` from source `{2}`. + 0 package id +1 package version +2 package source + + + Downloads a NuGet package to a local folder without requiring a project file. + Downloads a NuGet package to a local folder without requiring a project file. + + Update referenced packages in a project or solution. Referenzierte Pakete in einem Projekt oder einer Projektmappe aktualisieren. @@ -990,44 +1047,16 @@ Weitere Informationen finden Sie unter: https://docs.nuget.org/docs/reference/co Pakete mit Sicherheitshinweisen in {0} werden aktualisiert. {0} - the project/solution being checked - - Skipping download. Package '{0}' version {1} already exists at '{2}'. - Skipping download. Package '{0}' version {1} already exists at '{2}'. - 0 - package id -1 - version -2 - directory path where the package was downloaded - - - Package '{0}' ({1}) failed to download. - Package '{0}' ({1}) failed to download. - 0 - package id -1 - package version - - - Downloading package {0}, version {1}. - Downloading package {0}, version {1}. - 0- package id -1- package version - - - Package '{0}' ({1}) successfully downloaded to '{2}'. - Package '{0}' ({1}) successfully downloaded to '{2}'. - 0 - package id -1 - package version -2 - package download location - - - Package download failed for `{0} {1}` from source `{2}`. - Package download failed for `{0} {1}` from source `{2}`. - 0 package id -1 package version -2 package source - There are no stable versions available, {0} is the best available. Consider adding the --prerelease option Es sind keine stabilen Versionen verfügbar. Beste verfügbare Version: {0}. Erwägen Sie, die Option "--prerelease" hinzuzufügen. {0} - Package Version + + Allows prerelease packages to be installed. + Allows prerelease packages to be installed. + + If a package and version already exists, skip it and continue with the next package in the push, if any. Wenn ein Paket und eine Version bereits vorhanden sind, überspringen Sie das Paket, und fahren Sie ggf. mit dem nächsten Paket im Pushvorgang fort. @@ -1490,61 +1519,6 @@ Bei der Suche handelt es sich um einen ungültigen Zeichenfolgenvergleich mit de Die Ressourcendaten "{0}" für das Projekt "{1}" enthält kein Ziel für das angegebene Eingabeframework "{2}". {0} - Assets file path, {1} - Project name, {2} - Framework - - Allows downloading from HTTP (non-HTTPS) package sources. - Allows downloading from HTTP (non-HTTPS) package sources. - - - - Directory where the package will be placed. Defaults to the current working directory. - Directory where the package will be placed. Defaults to the current working directory. - - - - Path to a NuGet.config file to use for settings and sources. - Path to a NuGet.config file to use for settings and sources. - - - - Downloads a NuGet package to a local folder without requiring a project file. - Downloads a NuGet package to a local folder without requiring a project file. - - - - Download only the .nupkg file without extracting it. - Download only the .nupkg file without extracting it. - - - - Allows interactive authentication if required. - Allows interactive authentication if required. - - - - Package identifier (e.g. 'Newtonsoft.Json'). - Package identifier (e.g. 'Newtonsoft.Json'). - - - - Allows downloading prerelease versions. - Allows downloading prerelease versions. - - - - Specifies one or more NuGet package sources to use. - Specifies one or more NuGet package sources to use. - - - - Set the verbosity level. Allowed values: quiet | normal | detailed. - Set the verbosity level. Allowed values: quiet | normal | detailed. - Please do not translate `quiet`, `normal`, `detailed` - - - Specific package version to download - Specific package version to download - - Searches one or more package sources for packages that match a search term. If no sources are specified, all sources defined in the NuGet.Config are used. Durchsucht mindestens eine Paketquelle nach Paketen, die mit einem Suchbegriff übereinstimmen. Wenn keine Quellen angegeben sind, werden alle in der NuGet.Config definierten Quellen verwendet. diff --git a/src/NuGet.Core/NuGet.CommandLine.XPlat/xlf/Strings.es.xlf b/src/NuGet.Core/NuGet.CommandLine.XPlat/xlf/Strings.es.xlf index a019c16d433..8d3ae83c00a 100644 --- a/src/NuGet.Core/NuGet.CommandLine.XPlat/xlf/Strings.es.xlf +++ b/src/NuGet.Core/NuGet.CommandLine.XPlat/xlf/Strings.es.xlf @@ -47,11 +47,6 @@ Id. del paquete que se va a agregar. - - Allows prerelease packages to be installed. - Permite que se instalen paquetes de versión preliminar. - - Version of the package to be added. Versión del paquete que se agregará. @@ -358,12 +353,6 @@ NuGet requiere orígenes HTTPS. Para usar orígenes HTTP, es necesario establece Invalid version value '{0}'. 0 - package version - - Invalid verbosity level '{0}'. Allowed values are: quiet, normal, detailed. - Invalid verbosity level '{0}'. Allowed values are: quiet, normal, detailed. - 0 - verbosity level -do not translate: quiet, normal, detailed. - Invalid version range '{0}' Intervalo de versiones no válido "{0}" @@ -409,6 +398,11 @@ do not translate: quiet, normal, detailed. El proyecto "{0}" usa package.config para los paquetes NuGet, aunque el comando solo funciona con proyectos de referencia de paquetes. + + Unable to find a valid package version + Unable to find a valid package version + + Package source mapping is enabled, but no mapping for package {0} was found. See https://aka.ms/nuget/psm for more information. La asignación de origen del paquete está habilitada, pero no se encontró ninguna asignación para el paquete {0}. Consulte https://aka.ms/nuget/psm para obtener más información. @@ -897,6 +891,69 @@ Para obtener más información, visite https://docs.nuget.org/docs/reference/com {0}: App full name {1}: App version + + Allows downloading from HTTP (non-HTTPS) package sources. + Allows downloading from HTTP (non-HTTPS) package sources. + + + + Skipping download. Package '{0}' version {1} already exists at '{2}'. + Skipping download. Package '{0}' version {1} already exists at '{2}'. + 0 - package id +1 - version +2 - directory path where the package was downloaded + + + Package '{0}' ({1}) failed to download. + Package '{0}' ({1}) failed to download. + 0 - package id +1 - package version + + + latest version + latest version + + + + Directory where the package will be placed. Defaults to the current working directory. + Directory where the package will be placed. Defaults to the current working directory. + + + + Package identifier (e.g. 'Newtonsoft.Json'). + Package identifier (e.g. 'Newtonsoft.Json'). + + + + Specifies one or more NuGet package sources to use. + Specifies one or more NuGet package sources to use. + + + + Downloading package {0}, version {1}. + Downloading package {0}, version {1}. + 0- package id +1- package version + + + Package '{0}' ({1}) successfully downloaded to '{2}'. + Package '{0}' ({1}) successfully downloaded to '{2}'. + 0 - package id +1 - package version +2 - package download location + + + Package download failed for `{0} {1}` from source `{2}`. + Package download failed for `{0} {1}` from source `{2}`. + 0 package id +1 package version +2 package source + + + Downloads a NuGet package to a local folder without requiring a project file. + Downloads a NuGet package to a local folder without requiring a project file. + + Update referenced packages in a project or solution. Actualizar los paquetes a los que se hace referencia en un proyecto o solución. @@ -990,44 +1047,16 @@ Para obtener más información, visite https://docs.nuget.org/docs/reference/com Actualizando paquetes con avisos de seguridad en {0} {0} - the project/solution being checked - - Skipping download. Package '{0}' version {1} already exists at '{2}'. - Skipping download. Package '{0}' version {1} already exists at '{2}'. - 0 - package id -1 - version -2 - directory path where the package was downloaded - - - Package '{0}' ({1}) failed to download. - Package '{0}' ({1}) failed to download. - 0 - package id -1 - package version - - - Downloading package {0}, version {1}. - Downloading package {0}, version {1}. - 0- package id -1- package version - - - Package '{0}' ({1}) successfully downloaded to '{2}'. - Package '{0}' ({1}) successfully downloaded to '{2}'. - 0 - package id -1 - package version -2 - package download location - - - Package download failed for `{0} {1}` from source `{2}`. - Package download failed for `{0} {1}` from source `{2}`. - 0 package id -1 package version -2 package source - There are no stable versions available, {0} is the best available. Consider adding the --prerelease option No hay ninguna versión estable que se pueda seleccionar; {0} es la mejor opción disponible. Puede agregar la opción --prerelease. {0} - Package Version + + Allows prerelease packages to be installed. + Allows prerelease packages to be installed. + + If a package and version already exists, skip it and continue with the next package in the push, if any. Si un paquete y una versión ya existen, omítalos y continúe con el siguiente en la inserción "push", en caso de haberlo. @@ -1490,61 +1519,6 @@ La búsqueda es una comparación de cadenas que no diferencia mayúsculas de min El archivo de recursos "{0}" para el proyecto "{1}" no contiene un destino para el marco de entrada "{2}" especificado. {0} - Assets file path, {1} - Project name, {2} - Framework - - Allows downloading from HTTP (non-HTTPS) package sources. - Allows downloading from HTTP (non-HTTPS) package sources. - - - - Directory where the package will be placed. Defaults to the current working directory. - Directory where the package will be placed. Defaults to the current working directory. - - - - Path to a NuGet.config file to use for settings and sources. - Path to a NuGet.config file to use for settings and sources. - - - - Downloads a NuGet package to a local folder without requiring a project file. - Downloads a NuGet package to a local folder without requiring a project file. - - - - Download only the .nupkg file without extracting it. - Download only the .nupkg file without extracting it. - - - - Allows interactive authentication if required. - Allows interactive authentication if required. - - - - Package identifier (e.g. 'Newtonsoft.Json'). - Package identifier (e.g. 'Newtonsoft.Json'). - - - - Allows downloading prerelease versions. - Allows downloading prerelease versions. - - - - Specifies one or more NuGet package sources to use. - Specifies one or more NuGet package sources to use. - - - - Set the verbosity level. Allowed values: quiet | normal | detailed. - Set the verbosity level. Allowed values: quiet | normal | detailed. - Please do not translate `quiet`, `normal`, `detailed` - - - Specific package version to download - Specific package version to download - - Searches one or more package sources for packages that match a search term. If no sources are specified, all sources defined in the NuGet.Config are used. Busca en uno o varios orígenes de paquetes los paquetes que coincidan con un término de búsqueda. Si no se especifica ningún origen, se usarán todos los orígenes definidos en NuGet.Config. diff --git a/src/NuGet.Core/NuGet.CommandLine.XPlat/xlf/Strings.fr.xlf b/src/NuGet.Core/NuGet.CommandLine.XPlat/xlf/Strings.fr.xlf index 178e6eef084..37595f3e427 100644 --- a/src/NuGet.Core/NuGet.CommandLine.XPlat/xlf/Strings.fr.xlf +++ b/src/NuGet.Core/NuGet.CommandLine.XPlat/xlf/Strings.fr.xlf @@ -47,11 +47,6 @@ ID du package à ajouter. - - Allows prerelease packages to be installed. - Permet d'installer les packages de préversion. - - Version of the package to be added. Version du package à ajouter. @@ -358,12 +353,6 @@ NuGet nécessite des sources HTTPS. Pour utiliser des sources HTTP, vous devez d Invalid version value '{0}'. 0 - package version - - Invalid verbosity level '{0}'. Allowed values are: quiet, normal, detailed. - Invalid verbosity level '{0}'. Allowed values are: quiet, normal, detailed. - 0 - verbosity level -do not translate: quiet, normal, detailed. - Invalid version range '{0}' Étendue de version non valide « {0} » @@ -409,6 +398,11 @@ do not translate: quiet, normal, detailed. Le projet '{0}' utilise package.config pour les packages NuGet, alors que la commande fonctionne uniquement avec les projets de référence de package. + + Unable to find a valid package version + Unable to find a valid package version + + Package source mapping is enabled, but no mapping for package {0} was found. See https://aka.ms/nuget/psm for more information. Le mappage de source du package est activé, mais aucun mappage pour le package {0} n’a été trouvé. Pour découvrir plus d’informations, consultez https://aka.ms/nuget/psm. @@ -897,6 +891,69 @@ Pour plus d'informations, visitez https://docs.nuget.org/docs/reference/command- {0}: App full name {1}: App version + + Allows downloading from HTTP (non-HTTPS) package sources. + Allows downloading from HTTP (non-HTTPS) package sources. + + + + Skipping download. Package '{0}' version {1} already exists at '{2}'. + Skipping download. Package '{0}' version {1} already exists at '{2}'. + 0 - package id +1 - version +2 - directory path where the package was downloaded + + + Package '{0}' ({1}) failed to download. + Package '{0}' ({1}) failed to download. + 0 - package id +1 - package version + + + latest version + latest version + + + + Directory where the package will be placed. Defaults to the current working directory. + Directory where the package will be placed. Defaults to the current working directory. + + + + Package identifier (e.g. 'Newtonsoft.Json'). + Package identifier (e.g. 'Newtonsoft.Json'). + + + + Specifies one or more NuGet package sources to use. + Specifies one or more NuGet package sources to use. + + + + Downloading package {0}, version {1}. + Downloading package {0}, version {1}. + 0- package id +1- package version + + + Package '{0}' ({1}) successfully downloaded to '{2}'. + Package '{0}' ({1}) successfully downloaded to '{2}'. + 0 - package id +1 - package version +2 - package download location + + + Package download failed for `{0} {1}` from source `{2}`. + Package download failed for `{0} {1}` from source `{2}`. + 0 package id +1 package version +2 package source + + + Downloads a NuGet package to a local folder without requiring a project file. + Downloads a NuGet package to a local folder without requiring a project file. + + Update referenced packages in a project or solution. Mettre à jour les packages référencés dans un projet ou une solution. @@ -990,44 +1047,16 @@ Pour plus d'informations, visitez https://docs.nuget.org/docs/reference/command- Mise à jour des packages ayant des avis de sécurité dans {0} {0} - the project/solution being checked - - Skipping download. Package '{0}' version {1} already exists at '{2}'. - Skipping download. Package '{0}' version {1} already exists at '{2}'. - 0 - package id -1 - version -2 - directory path where the package was downloaded - - - Package '{0}' ({1}) failed to download. - Package '{0}' ({1}) failed to download. - 0 - package id -1 - package version - - - Downloading package {0}, version {1}. - Downloading package {0}, version {1}. - 0- package id -1- package version - - - Package '{0}' ({1}) successfully downloaded to '{2}'. - Package '{0}' ({1}) successfully downloaded to '{2}'. - 0 - package id -1 - package version -2 - package download location - - - Package download failed for `{0} {1}` from source `{2}`. - Package download failed for `{0} {1}` from source `{2}`. - 0 package id -1 package version -2 package source - There are no stable versions available, {0} is the best available. Consider adding the --prerelease option Aucune version stable n'est disponible, {0} est la meilleure version disponible. Ajoutez l'option --prerelease {0} - Package Version + + Allows prerelease packages to be installed. + Allows prerelease packages to be installed. + + If a package and version already exists, skip it and continue with the next package in the push, if any. Si un package et une version existent déjà, ignorez-les et passez au package suivant dans l'envoi (push), le cas échéant. @@ -1490,61 +1519,6 @@ La recherche est une comparaison de chaînes qui ne respecte pas la casse à l Le fichier d’actifs « {0} » pour le « {1} » de projet ne contient pas de cible pour le framework d’entrée spécifié « {2} ». {0} - Assets file path, {1} - Project name, {2} - Framework - - Allows downloading from HTTP (non-HTTPS) package sources. - Allows downloading from HTTP (non-HTTPS) package sources. - - - - Directory where the package will be placed. Defaults to the current working directory. - Directory where the package will be placed. Defaults to the current working directory. - - - - Path to a NuGet.config file to use for settings and sources. - Path to a NuGet.config file to use for settings and sources. - - - - Downloads a NuGet package to a local folder without requiring a project file. - Downloads a NuGet package to a local folder without requiring a project file. - - - - Download only the .nupkg file without extracting it. - Download only the .nupkg file without extracting it. - - - - Allows interactive authentication if required. - Allows interactive authentication if required. - - - - Package identifier (e.g. 'Newtonsoft.Json'). - Package identifier (e.g. 'Newtonsoft.Json'). - - - - Allows downloading prerelease versions. - Allows downloading prerelease versions. - - - - Specifies one or more NuGet package sources to use. - Specifies one or more NuGet package sources to use. - - - - Set the verbosity level. Allowed values: quiet | normal | detailed. - Set the verbosity level. Allowed values: quiet | normal | detailed. - Please do not translate `quiet`, `normal`, `detailed` - - - Specific package version to download - Specific package version to download - - Searches one or more package sources for packages that match a search term. If no sources are specified, all sources defined in the NuGet.Config are used. Recherche dans une ou plusieurs sources de packages les packages qui correspondent à un terme de recherche. Si aucune source n’est spécifiée, toutes les sources définies dans NuGet.Config sont utilisées. diff --git a/src/NuGet.Core/NuGet.CommandLine.XPlat/xlf/Strings.it.xlf b/src/NuGet.Core/NuGet.CommandLine.XPlat/xlf/Strings.it.xlf index c02e5b11ee1..b63de5081db 100644 --- a/src/NuGet.Core/NuGet.CommandLine.XPlat/xlf/Strings.it.xlf +++ b/src/NuGet.Core/NuGet.CommandLine.XPlat/xlf/Strings.it.xlf @@ -47,11 +47,6 @@ ID del pacchetto da aggiungere. - - Allows prerelease packages to be installed. - Consente l'installazione di pacchetti non definitivi. - - Version of the package to be added. Versione del pacchetto da aggiungere. @@ -358,12 +353,6 @@ NuGet richiede origini HTTPS. Per utilizzare origini HTTP, è necessario imposta Invalid version value '{0}'. 0 - package version - - Invalid verbosity level '{0}'. Allowed values are: quiet, normal, detailed. - Invalid verbosity level '{0}'. Allowed values are: quiet, normal, detailed. - 0 - verbosity level -do not translate: quiet, normal, detailed. - Invalid version range '{0}' Intervallo versioni non valido '{0}' @@ -409,6 +398,11 @@ do not translate: quiet, normal, detailed. Il progetto `{0}` usa package.config per i pacchetti NuGet, mentre il comando funziona solo con progetti di riferimento al pacchetto. + + Unable to find a valid package version + Unable to find a valid package version + + Package source mapping is enabled, but no mapping for package {0} was found. See https://aka.ms/nuget/psm for more information. Il mapping dell'origine del pacchetto è abilitato, ma non è stato trovato alcun mapping per il pacchetto {0}. Per altre informazioni, vedere https://aka.ms/nuget/psm. @@ -897,6 +891,69 @@ Per altre informazioni, vedere https://docs.nuget.org/docs/reference/command-lin {0}: App full name {1}: App version + + Allows downloading from HTTP (non-HTTPS) package sources. + Allows downloading from HTTP (non-HTTPS) package sources. + + + + Skipping download. Package '{0}' version {1} already exists at '{2}'. + Skipping download. Package '{0}' version {1} already exists at '{2}'. + 0 - package id +1 - version +2 - directory path where the package was downloaded + + + Package '{0}' ({1}) failed to download. + Package '{0}' ({1}) failed to download. + 0 - package id +1 - package version + + + latest version + latest version + + + + Directory where the package will be placed. Defaults to the current working directory. + Directory where the package will be placed. Defaults to the current working directory. + + + + Package identifier (e.g. 'Newtonsoft.Json'). + Package identifier (e.g. 'Newtonsoft.Json'). + + + + Specifies one or more NuGet package sources to use. + Specifies one or more NuGet package sources to use. + + + + Downloading package {0}, version {1}. + Downloading package {0}, version {1}. + 0- package id +1- package version + + + Package '{0}' ({1}) successfully downloaded to '{2}'. + Package '{0}' ({1}) successfully downloaded to '{2}'. + 0 - package id +1 - package version +2 - package download location + + + Package download failed for `{0} {1}` from source `{2}`. + Package download failed for `{0} {1}` from source `{2}`. + 0 package id +1 package version +2 package source + + + Downloads a NuGet package to a local folder without requiring a project file. + Downloads a NuGet package to a local folder without requiring a project file. + + Update referenced packages in a project or solution. Aggiorna i pacchetti referenziati in un progetto o in una soluzione. @@ -990,44 +1047,16 @@ Per altre informazioni, vedere https://docs.nuget.org/docs/reference/command-lin È in corso l'aggiornamento dei pacchetti con avvisi di sicurezza in {0} {0} - the project/solution being checked - - Skipping download. Package '{0}' version {1} already exists at '{2}'. - Skipping download. Package '{0}' version {1} already exists at '{2}'. - 0 - package id -1 - version -2 - directory path where the package was downloaded - - - Package '{0}' ({1}) failed to download. - Package '{0}' ({1}) failed to download. - 0 - package id -1 - package version - - - Downloading package {0}, version {1}. - Downloading package {0}, version {1}. - 0- package id -1- package version - - - Package '{0}' ({1}) successfully downloaded to '{2}'. - Package '{0}' ({1}) successfully downloaded to '{2}'. - 0 - package id -1 - package version -2 - package download location - - - Package download failed for `{0} {1}` from source `{2}`. - Package download failed for `{0} {1}` from source `{2}`. - 0 package id -1 package version -2 package source - There are no stable versions available, {0} is the best available. Consider adding the --prerelease option Non sono disponibili versioni stabili e {0} è la migliore versione disponibile. Provare ad aggiungere l'opzione --prerelease {0} - Package Version + + Allows prerelease packages to be installed. + Allows prerelease packages to be installed. + + If a package and version already exists, skip it and continue with the next package in the push, if any. Se un pacchetto e una versione esistono già, ignorarli e continuare con il pacchetto successivo del push, se presente. @@ -1490,61 +1519,6 @@ La ricerca viene eseguita in un confronto di stringhe senza distinzione tra maiu Il file di asset {0} per il progetto '{1}' non contiene una destinazione per il framework di input specificato '{2}'. {0} - Assets file path, {1} - Project name, {2} - Framework - - Allows downloading from HTTP (non-HTTPS) package sources. - Allows downloading from HTTP (non-HTTPS) package sources. - - - - Directory where the package will be placed. Defaults to the current working directory. - Directory where the package will be placed. Defaults to the current working directory. - - - - Path to a NuGet.config file to use for settings and sources. - Path to a NuGet.config file to use for settings and sources. - - - - Downloads a NuGet package to a local folder without requiring a project file. - Downloads a NuGet package to a local folder without requiring a project file. - - - - Download only the .nupkg file without extracting it. - Download only the .nupkg file without extracting it. - - - - Allows interactive authentication if required. - Allows interactive authentication if required. - - - - Package identifier (e.g. 'Newtonsoft.Json'). - Package identifier (e.g. 'Newtonsoft.Json'). - - - - Allows downloading prerelease versions. - Allows downloading prerelease versions. - - - - Specifies one or more NuGet package sources to use. - Specifies one or more NuGet package sources to use. - - - - Set the verbosity level. Allowed values: quiet | normal | detailed. - Set the verbosity level. Allowed values: quiet | normal | detailed. - Please do not translate `quiet`, `normal`, `detailed` - - - Specific package version to download - Specific package version to download - - Searches one or more package sources for packages that match a search term. If no sources are specified, all sources defined in the NuGet.Config are used. Cerca in una o più origini pacchetti per pacchetti che corrispondono a un termine di ricerca. Se non vengono specificate origini, vengono usate tutte le origini definite in NuGet.Config. diff --git a/src/NuGet.Core/NuGet.CommandLine.XPlat/xlf/Strings.ja.xlf b/src/NuGet.Core/NuGet.CommandLine.XPlat/xlf/Strings.ja.xlf index 5b16081bf79..b54246f9d12 100644 --- a/src/NuGet.Core/NuGet.CommandLine.XPlat/xlf/Strings.ja.xlf +++ b/src/NuGet.Core/NuGet.CommandLine.XPlat/xlf/Strings.ja.xlf @@ -47,11 +47,6 @@ 追加されるパッケージの ID。 - - Allows prerelease packages to be installed. - プレリリース パッケージのインストールを許可します。 - - Version of the package to be added. 追加されるパッケージのバージョン。 @@ -358,12 +353,6 @@ NuGet には HTTPS ソースが必要です。HTTP ソースを使用するに Invalid version value '{0}'. 0 - package version - - Invalid verbosity level '{0}'. Allowed values are: quiet, normal, detailed. - Invalid verbosity level '{0}'. Allowed values are: quiet, normal, detailed. - 0 - verbosity level -do not translate: quiet, normal, detailed. - Invalid version range '{0}' バージョン範囲 '{0}' が無効です @@ -409,6 +398,11 @@ do not translate: quiet, normal, detailed. プロジェクト '{0}' では NuGet パッケージに package.config を使用しますが、コマンドはパッケージ参照プロジェクトでのみ動作します。 + + Unable to find a valid package version + Unable to find a valid package version + + Package source mapping is enabled, but no mapping for package {0} was found. See https://aka.ms/nuget/psm for more information. パッケージ ソース マッピングは有効ですが、パッケージ {0} のマッピングが見つかりませんでした。詳細については、https://aka.ms/nuget/psm を参照してください。 @@ -897,6 +891,69 @@ For more information, visit https://docs.nuget.org/docs/reference/command-line-r {0}: App full name {1}: App version + + Allows downloading from HTTP (non-HTTPS) package sources. + Allows downloading from HTTP (non-HTTPS) package sources. + + + + Skipping download. Package '{0}' version {1} already exists at '{2}'. + Skipping download. Package '{0}' version {1} already exists at '{2}'. + 0 - package id +1 - version +2 - directory path where the package was downloaded + + + Package '{0}' ({1}) failed to download. + Package '{0}' ({1}) failed to download. + 0 - package id +1 - package version + + + latest version + latest version + + + + Directory where the package will be placed. Defaults to the current working directory. + Directory where the package will be placed. Defaults to the current working directory. + + + + Package identifier (e.g. 'Newtonsoft.Json'). + Package identifier (e.g. 'Newtonsoft.Json'). + + + + Specifies one or more NuGet package sources to use. + Specifies one or more NuGet package sources to use. + + + + Downloading package {0}, version {1}. + Downloading package {0}, version {1}. + 0- package id +1- package version + + + Package '{0}' ({1}) successfully downloaded to '{2}'. + Package '{0}' ({1}) successfully downloaded to '{2}'. + 0 - package id +1 - package version +2 - package download location + + + Package download failed for `{0} {1}` from source `{2}`. + Package download failed for `{0} {1}` from source `{2}`. + 0 package id +1 package version +2 package source + + + Downloads a NuGet package to a local folder without requiring a project file. + Downloads a NuGet package to a local folder without requiring a project file. + + Update referenced packages in a project or solution. プロジェクトまたはソリューションで参照されているパッケージを更新します。 @@ -990,44 +1047,16 @@ For more information, visit https://docs.nuget.org/docs/reference/command-line-r {0} 内のセキュリティ アドバイザリがあるパッケージを更新しています {0} - the project/solution being checked - - Skipping download. Package '{0}' version {1} already exists at '{2}'. - Skipping download. Package '{0}' version {1} already exists at '{2}'. - 0 - package id -1 - version -2 - directory path where the package was downloaded - - - Package '{0}' ({1}) failed to download. - Package '{0}' ({1}) failed to download. - 0 - package id -1 - package version - - - Downloading package {0}, version {1}. - Downloading package {0}, version {1}. - 0- package id -1- package version - - - Package '{0}' ({1}) successfully downloaded to '{2}'. - Package '{0}' ({1}) successfully downloaded to '{2}'. - 0 - package id -1 - package version -2 - package download location - - - Package download failed for `{0} {1}` from source `{2}`. - Package download failed for `{0} {1}` from source `{2}`. - 0 package id -1 package version -2 package source - There are no stable versions available, {0} is the best available. Consider adding the --prerelease option 利用可能な安定バージョンがありません。利用可能な中では {0} が最善です。--prerelease オプションを追加することをご検討ください {0} - Package Version + + Allows prerelease packages to be installed. + Allows prerelease packages to be installed. + + If a package and version already exists, skip it and continue with the next package in the push, if any. パッケージとバージョンが既に存在する場合は、それをスキップし、プッシュに次のパッケージがある場合は、それを続行します。 @@ -1490,61 +1519,6 @@ The search is a case-insensitive string comparison using the supplied value, whi プロジェクト '{1}' のアセット ファイル '{0}' に、指定された入力フレームワーク '{2}' のターゲットが含まれていません。 {0} - Assets file path, {1} - Project name, {2} - Framework - - Allows downloading from HTTP (non-HTTPS) package sources. - Allows downloading from HTTP (non-HTTPS) package sources. - - - - Directory where the package will be placed. Defaults to the current working directory. - Directory where the package will be placed. Defaults to the current working directory. - - - - Path to a NuGet.config file to use for settings and sources. - Path to a NuGet.config file to use for settings and sources. - - - - Downloads a NuGet package to a local folder without requiring a project file. - Downloads a NuGet package to a local folder without requiring a project file. - - - - Download only the .nupkg file without extracting it. - Download only the .nupkg file without extracting it. - - - - Allows interactive authentication if required. - Allows interactive authentication if required. - - - - Package identifier (e.g. 'Newtonsoft.Json'). - Package identifier (e.g. 'Newtonsoft.Json'). - - - - Allows downloading prerelease versions. - Allows downloading prerelease versions. - - - - Specifies one or more NuGet package sources to use. - Specifies one or more NuGet package sources to use. - - - - Set the verbosity level. Allowed values: quiet | normal | detailed. - Set the verbosity level. Allowed values: quiet | normal | detailed. - Please do not translate `quiet`, `normal`, `detailed` - - - Specific package version to download - Specific package version to download - - Searches one or more package sources for packages that match a search term. If no sources are specified, all sources defined in the NuGet.Config are used. 検索語句に一致するパッケージを 1 つ以上のパッケージ ソースで検索します。ソースが指定されていない場合、NuGet.Config で定義されているすべてのソースが使用されます。 diff --git a/src/NuGet.Core/NuGet.CommandLine.XPlat/xlf/Strings.ko.xlf b/src/NuGet.Core/NuGet.CommandLine.XPlat/xlf/Strings.ko.xlf index 3e4b9b12566..96fa5b86436 100644 --- a/src/NuGet.Core/NuGet.CommandLine.XPlat/xlf/Strings.ko.xlf +++ b/src/NuGet.Core/NuGet.CommandLine.XPlat/xlf/Strings.ko.xlf @@ -47,11 +47,6 @@ 추가할 패키지의 ID입니다. - - Allows prerelease packages to be installed. - 시험판 패키지를 설치할 수 있습니다. - - Version of the package to be added. 추가할 패키지의 버전입니다. @@ -358,12 +353,6 @@ NuGet에는 HTTPS 원본이 필요합니다. HTTP 원본을 사용하려면 NuGe Invalid version value '{0}'. 0 - package version - - Invalid verbosity level '{0}'. Allowed values are: quiet, normal, detailed. - Invalid verbosity level '{0}'. Allowed values are: quiet, normal, detailed. - 0 - verbosity level -do not translate: quiet, normal, detailed. - Invalid version range '{0}' 잘못된 버전 범위 '{0}' @@ -409,6 +398,11 @@ do not translate: quiet, normal, detailed. `{0}` 프로젝트는 NuGet 패키지에 대한 package.config를 사용합니다. 명령은 패키지 참조 프로젝트에서만 작동합니다. + + Unable to find a valid package version + Unable to find a valid package version + + Package source mapping is enabled, but no mapping for package {0} was found. See https://aka.ms/nuget/psm for more information. 패키지 원본 매핑이 사용하도록 설정되어 있지만 패키지 {0}에 대한 매핑을 찾을 수 없습니다. 자세한 내용은 https://aka.ms/nuget/psm을 참조하세요. @@ -897,6 +891,69 @@ For more information, visit https://docs.nuget.org/docs/reference/command-line-r {0}: App full name {1}: App version + + Allows downloading from HTTP (non-HTTPS) package sources. + Allows downloading from HTTP (non-HTTPS) package sources. + + + + Skipping download. Package '{0}' version {1} already exists at '{2}'. + Skipping download. Package '{0}' version {1} already exists at '{2}'. + 0 - package id +1 - version +2 - directory path where the package was downloaded + + + Package '{0}' ({1}) failed to download. + Package '{0}' ({1}) failed to download. + 0 - package id +1 - package version + + + latest version + latest version + + + + Directory where the package will be placed. Defaults to the current working directory. + Directory where the package will be placed. Defaults to the current working directory. + + + + Package identifier (e.g. 'Newtonsoft.Json'). + Package identifier (e.g. 'Newtonsoft.Json'). + + + + Specifies one or more NuGet package sources to use. + Specifies one or more NuGet package sources to use. + + + + Downloading package {0}, version {1}. + Downloading package {0}, version {1}. + 0- package id +1- package version + + + Package '{0}' ({1}) successfully downloaded to '{2}'. + Package '{0}' ({1}) successfully downloaded to '{2}'. + 0 - package id +1 - package version +2 - package download location + + + Package download failed for `{0} {1}` from source `{2}`. + Package download failed for `{0} {1}` from source `{2}`. + 0 package id +1 package version +2 package source + + + Downloads a NuGet package to a local folder without requiring a project file. + Downloads a NuGet package to a local folder without requiring a project file. + + Update referenced packages in a project or solution. 프로젝트 또는 솔루션에서 참조된 패키지를 업데이트합니다. @@ -990,44 +1047,16 @@ For more information, visit https://docs.nuget.org/docs/reference/command-line-r {0}에서 보안 권고가 있는 패키지를 업데이트하는 중 {0} - the project/solution being checked - - Skipping download. Package '{0}' version {1} already exists at '{2}'. - Skipping download. Package '{0}' version {1} already exists at '{2}'. - 0 - package id -1 - version -2 - directory path where the package was downloaded - - - Package '{0}' ({1}) failed to download. - Package '{0}' ({1}) failed to download. - 0 - package id -1 - package version - - - Downloading package {0}, version {1}. - Downloading package {0}, version {1}. - 0- package id -1- package version - - - Package '{0}' ({1}) successfully downloaded to '{2}'. - Package '{0}' ({1}) successfully downloaded to '{2}'. - 0 - package id -1 - package version -2 - package download location - - - Package download failed for `{0} {1}` from source `{2}`. - Package download failed for `{0} {1}` from source `{2}`. - 0 package id -1 package version -2 package source - There are no stable versions available, {0} is the best available. Consider adding the --prerelease option 사용할 수 있는 안정적인 버전이 없습니다. 사용할 수 있는 최상의 버전은 {0}입니다. --prerelease 옵션을 추가하는 것이 좋습니다. {0} - Package Version + + Allows prerelease packages to be installed. + Allows prerelease packages to be installed. + + If a package and version already exists, skip it and continue with the next package in the push, if any. 패키지 및 버전이 이미 있는 경우 건너뛴 후 푸시에서 다음 패키지(있는 경우)를 계속합니다. @@ -1491,61 +1520,6 @@ The search is a case-insensitive string comparison using the supplied value, whi 프로젝트 '{1}'의 자산 파일 '{0}'에 지정된 입력 프레임워크 '{2}' 대상이 없습니다. {0} - Assets file path, {1} - Project name, {2} - Framework - - Allows downloading from HTTP (non-HTTPS) package sources. - Allows downloading from HTTP (non-HTTPS) package sources. - - - - Directory where the package will be placed. Defaults to the current working directory. - Directory where the package will be placed. Defaults to the current working directory. - - - - Path to a NuGet.config file to use for settings and sources. - Path to a NuGet.config file to use for settings and sources. - - - - Downloads a NuGet package to a local folder without requiring a project file. - Downloads a NuGet package to a local folder without requiring a project file. - - - - Download only the .nupkg file without extracting it. - Download only the .nupkg file without extracting it. - - - - Allows interactive authentication if required. - Allows interactive authentication if required. - - - - Package identifier (e.g. 'Newtonsoft.Json'). - Package identifier (e.g. 'Newtonsoft.Json'). - - - - Allows downloading prerelease versions. - Allows downloading prerelease versions. - - - - Specifies one or more NuGet package sources to use. - Specifies one or more NuGet package sources to use. - - - - Set the verbosity level. Allowed values: quiet | normal | detailed. - Set the verbosity level. Allowed values: quiet | normal | detailed. - Please do not translate `quiet`, `normal`, `detailed` - - - Specific package version to download - Specific package version to download - - Searches one or more package sources for packages that match a search term. If no sources are specified, all sources defined in the NuGet.Config are used. 하나 이상의 패키지 원본에서 검색어와 일치하는 패키지를 검색합니다. 원본을 지정하지 않으면 NuGet.Config에 정의된 모든 원본이 사용됩니다. diff --git a/src/NuGet.Core/NuGet.CommandLine.XPlat/xlf/Strings.pl.xlf b/src/NuGet.Core/NuGet.CommandLine.XPlat/xlf/Strings.pl.xlf index 6a179a2be08..f5e6104c8d9 100644 --- a/src/NuGet.Core/NuGet.CommandLine.XPlat/xlf/Strings.pl.xlf +++ b/src/NuGet.Core/NuGet.CommandLine.XPlat/xlf/Strings.pl.xlf @@ -47,11 +47,6 @@ Identyfikator pakietu do dodania. - - Allows prerelease packages to be installed. - Zezwala na instalowanie pakietów wersji wstępnych. - - Version of the package to be added. Wersja pakietu do dodania. @@ -358,12 +353,6 @@ Menedżer NuGet wymaga źródeł HTTPS. Aby użyć źródeł HTTP, musisz wyraź Invalid version value '{0}'. 0 - package version - - Invalid verbosity level '{0}'. Allowed values are: quiet, normal, detailed. - Invalid verbosity level '{0}'. Allowed values are: quiet, normal, detailed. - 0 - verbosity level -do not translate: quiet, normal, detailed. - Invalid version range '{0}' Nieprawidłowy zakres wersji „{0}” @@ -409,6 +398,11 @@ do not translate: quiet, normal, detailed. Projekt „{0}” używa pliku package.config dla pakietów NuGet, podczas gdy polecenie działa tylko w przypadku projektów odwołania do pakietu. + + Unable to find a valid package version + Unable to find a valid package version + + Package source mapping is enabled, but no mapping for package {0} was found. See https://aka.ms/nuget/psm for more information. Mapowanie źródła pakietu jest włączone, ale nie znaleziono mapowania dla pakietu {0}. Aby uzyskać więcej informacji, zobacz https://aka.ms/nuget/psm. @@ -897,6 +891,69 @@ Aby uzyskać więcej informacji, odwiedź stronę https://docs.nuget.org/docs/re {0}: App full name {1}: App version + + Allows downloading from HTTP (non-HTTPS) package sources. + Allows downloading from HTTP (non-HTTPS) package sources. + + + + Skipping download. Package '{0}' version {1} already exists at '{2}'. + Skipping download. Package '{0}' version {1} already exists at '{2}'. + 0 - package id +1 - version +2 - directory path where the package was downloaded + + + Package '{0}' ({1}) failed to download. + Package '{0}' ({1}) failed to download. + 0 - package id +1 - package version + + + latest version + latest version + + + + Directory where the package will be placed. Defaults to the current working directory. + Directory where the package will be placed. Defaults to the current working directory. + + + + Package identifier (e.g. 'Newtonsoft.Json'). + Package identifier (e.g. 'Newtonsoft.Json'). + + + + Specifies one or more NuGet package sources to use. + Specifies one or more NuGet package sources to use. + + + + Downloading package {0}, version {1}. + Downloading package {0}, version {1}. + 0- package id +1- package version + + + Package '{0}' ({1}) successfully downloaded to '{2}'. + Package '{0}' ({1}) successfully downloaded to '{2}'. + 0 - package id +1 - package version +2 - package download location + + + Package download failed for `{0} {1}` from source `{2}`. + Package download failed for `{0} {1}` from source `{2}`. + 0 package id +1 package version +2 package source + + + Downloads a NuGet package to a local folder without requiring a project file. + Downloads a NuGet package to a local folder without requiring a project file. + + Update referenced packages in a project or solution. Zaktualizuj przywoływane pakiety w projekcie lub rozwiązaniu. @@ -990,44 +1047,16 @@ Aby uzyskać więcej informacji, odwiedź stronę https://docs.nuget.org/docs/re Aktualizowanie pakietów z ostrzeżeniami dotyczącymi zabezpieczeń w {0} {0} - the project/solution being checked - - Skipping download. Package '{0}' version {1} already exists at '{2}'. - Skipping download. Package '{0}' version {1} already exists at '{2}'. - 0 - package id -1 - version -2 - directory path where the package was downloaded - - - Package '{0}' ({1}) failed to download. - Package '{0}' ({1}) failed to download. - 0 - package id -1 - package version - - - Downloading package {0}, version {1}. - Downloading package {0}, version {1}. - 0- package id -1- package version - - - Package '{0}' ({1}) successfully downloaded to '{2}'. - Package '{0}' ({1}) successfully downloaded to '{2}'. - 0 - package id -1 - package version -2 - package download location - - - Package download failed for `{0} {1}` from source `{2}`. - Package download failed for `{0} {1}` from source `{2}`. - 0 package id -1 package version -2 package source - There are no stable versions available, {0} is the best available. Consider adding the --prerelease option Brak dostępnych stabilnych wersji. Najlepsza dostępna opcja to {0}. Rozważ dodanie opcji --prerelease {0} - Package Version + + Allows prerelease packages to be installed. + Allows prerelease packages to be installed. + + If a package and version already exists, skip it and continue with the next package in the push, if any. Jeśli pakiet i wersja już istnieje, pomiń to i kontynuuj przy użyciu następnego pakietu w ramach wypchnięcia, jeśli istnieje. @@ -1490,61 +1519,6 @@ Wyszukiwanie polega na porównywaniu łańcuchów bez rozróżniania wielkości Plik zasobów „{0}” dla projektu „{1}” nie zawiera celu dla określonej struktury wejściowej „{2}”. {0} - Assets file path, {1} - Project name, {2} - Framework - - Allows downloading from HTTP (non-HTTPS) package sources. - Allows downloading from HTTP (non-HTTPS) package sources. - - - - Directory where the package will be placed. Defaults to the current working directory. - Directory where the package will be placed. Defaults to the current working directory. - - - - Path to a NuGet.config file to use for settings and sources. - Path to a NuGet.config file to use for settings and sources. - - - - Downloads a NuGet package to a local folder without requiring a project file. - Downloads a NuGet package to a local folder without requiring a project file. - - - - Download only the .nupkg file without extracting it. - Download only the .nupkg file without extracting it. - - - - Allows interactive authentication if required. - Allows interactive authentication if required. - - - - Package identifier (e.g. 'Newtonsoft.Json'). - Package identifier (e.g. 'Newtonsoft.Json'). - - - - Allows downloading prerelease versions. - Allows downloading prerelease versions. - - - - Specifies one or more NuGet package sources to use. - Specifies one or more NuGet package sources to use. - - - - Set the verbosity level. Allowed values: quiet | normal | detailed. - Set the verbosity level. Allowed values: quiet | normal | detailed. - Please do not translate `quiet`, `normal`, `detailed` - - - Specific package version to download - Specific package version to download - - Searches one or more package sources for packages that match a search term. If no sources are specified, all sources defined in the NuGet.Config are used. Wyszukuje co najmniej jedno źródło pakietów, które są zgodne z wyszukiwanym terminem. Jeśli nie określono żadnego źródła, zostaną użyte wszystkie źródła zdefiniowane w NuGet.Config. diff --git a/src/NuGet.Core/NuGet.CommandLine.XPlat/xlf/Strings.pt-BR.xlf b/src/NuGet.Core/NuGet.CommandLine.XPlat/xlf/Strings.pt-BR.xlf index 182ae13e675..c12d9e48c37 100644 --- a/src/NuGet.Core/NuGet.CommandLine.XPlat/xlf/Strings.pt-BR.xlf +++ b/src/NuGet.Core/NuGet.CommandLine.XPlat/xlf/Strings.pt-BR.xlf @@ -47,11 +47,6 @@ ID do pacote a ser adicionado. - - Allows prerelease packages to be installed. - Permite a instalação de pacotes de pré-lançamento. - - Version of the package to be added. Versão do pacote a ser adicionado. @@ -358,12 +353,6 @@ O NuGet requer fontes HTTPS. Para usar fontes HTTP, você deve definir explicita Invalid version value '{0}'. 0 - package version - - Invalid verbosity level '{0}'. Allowed values are: quiet, normal, detailed. - Invalid verbosity level '{0}'. Allowed values are: quiet, normal, detailed. - 0 - verbosity level -do not translate: quiet, normal, detailed. - Invalid version range '{0}' Intervalo de versão "{0}" inválido @@ -409,6 +398,11 @@ do not translate: quiet, normal, detailed. O projeto '{0}' usa package.config para pacotes do NuGet, enquanto o comando funciona somente com projetos de referência do pacote. + + Unable to find a valid package version + Unable to find a valid package version + + Package source mapping is enabled, but no mapping for package {0} was found. See https://aka.ms/nuget/psm for more information. O mapeamento de origem do pacote está habilitado, mas nenhum mapeamento para {0} foi encontrado. Consulte https://aka.ms/nuget/psm para obter mais informações. @@ -897,6 +891,69 @@ Para obter mais informações, acesse https://docs.nuget.org/docs/reference/comm {0}: App full name {1}: App version + + Allows downloading from HTTP (non-HTTPS) package sources. + Allows downloading from HTTP (non-HTTPS) package sources. + + + + Skipping download. Package '{0}' version {1} already exists at '{2}'. + Skipping download. Package '{0}' version {1} already exists at '{2}'. + 0 - package id +1 - version +2 - directory path where the package was downloaded + + + Package '{0}' ({1}) failed to download. + Package '{0}' ({1}) failed to download. + 0 - package id +1 - package version + + + latest version + latest version + + + + Directory where the package will be placed. Defaults to the current working directory. + Directory where the package will be placed. Defaults to the current working directory. + + + + Package identifier (e.g. 'Newtonsoft.Json'). + Package identifier (e.g. 'Newtonsoft.Json'). + + + + Specifies one or more NuGet package sources to use. + Specifies one or more NuGet package sources to use. + + + + Downloading package {0}, version {1}. + Downloading package {0}, version {1}. + 0- package id +1- package version + + + Package '{0}' ({1}) successfully downloaded to '{2}'. + Package '{0}' ({1}) successfully downloaded to '{2}'. + 0 - package id +1 - package version +2 - package download location + + + Package download failed for `{0} {1}` from source `{2}`. + Package download failed for `{0} {1}` from source `{2}`. + 0 package id +1 package version +2 package source + + + Downloads a NuGet package to a local folder without requiring a project file. + Downloads a NuGet package to a local folder without requiring a project file. + + Update referenced packages in a project or solution. Atualize os pacotes referenciados em um projeto ou solução. @@ -990,44 +1047,16 @@ Para obter mais informações, acesse https://docs.nuget.org/docs/reference/comm Atualizando pacotes com avisos de segurança em {0} {0} - the project/solution being checked - - Skipping download. Package '{0}' version {1} already exists at '{2}'. - Skipping download. Package '{0}' version {1} already exists at '{2}'. - 0 - package id -1 - version -2 - directory path where the package was downloaded - - - Package '{0}' ({1}) failed to download. - Package '{0}' ({1}) failed to download. - 0 - package id -1 - package version - - - Downloading package {0}, version {1}. - Downloading package {0}, version {1}. - 0- package id -1- package version - - - Package '{0}' ({1}) successfully downloaded to '{2}'. - Package '{0}' ({1}) successfully downloaded to '{2}'. - 0 - package id -1 - package version -2 - package download location - - - Package download failed for `{0} {1}` from source `{2}`. - Package download failed for `{0} {1}` from source `{2}`. - 0 package id -1 package version -2 package source - There are no stable versions available, {0} is the best available. Consider adding the --prerelease option Não há versões estáveis disponíveis. {0} é a melhor disponível. Considere a adição da opção --prerelease {0} - Package Version + + Allows prerelease packages to be installed. + Allows prerelease packages to be installed. + + If a package and version already exists, skip it and continue with the next package in the push, if any. Se já existir um pacote e uma versão, ignore-os e continue com o próximo pacote no push, se houver. @@ -1490,61 +1519,6 @@ A pesquisa é uma comparação de cadeia de caracteres que não faz distinção O arquivo de ativos "{0}" para o projeto "{1}" não contém um destino para a estrutura de entrada especificada "{2}". {0} - Assets file path, {1} - Project name, {2} - Framework - - Allows downloading from HTTP (non-HTTPS) package sources. - Allows downloading from HTTP (non-HTTPS) package sources. - - - - Directory where the package will be placed. Defaults to the current working directory. - Directory where the package will be placed. Defaults to the current working directory. - - - - Path to a NuGet.config file to use for settings and sources. - Path to a NuGet.config file to use for settings and sources. - - - - Downloads a NuGet package to a local folder without requiring a project file. - Downloads a NuGet package to a local folder without requiring a project file. - - - - Download only the .nupkg file without extracting it. - Download only the .nupkg file without extracting it. - - - - Allows interactive authentication if required. - Allows interactive authentication if required. - - - - Package identifier (e.g. 'Newtonsoft.Json'). - Package identifier (e.g. 'Newtonsoft.Json'). - - - - Allows downloading prerelease versions. - Allows downloading prerelease versions. - - - - Specifies one or more NuGet package sources to use. - Specifies one or more NuGet package sources to use. - - - - Set the verbosity level. Allowed values: quiet | normal | detailed. - Set the verbosity level. Allowed values: quiet | normal | detailed. - Please do not translate `quiet`, `normal`, `detailed` - - - Specific package version to download - Specific package version to download - - Searches one or more package sources for packages that match a search term. If no sources are specified, all sources defined in the NuGet.Config are used. Pesquisa uma ou mais fontes de pacote em busca de pacotes que correspondam a um termo de pesquisa. Se nenhuma fonte for especificada, todas as fontes definidas no NuGet.Config serão usadas. diff --git a/src/NuGet.Core/NuGet.CommandLine.XPlat/xlf/Strings.ru.xlf b/src/NuGet.Core/NuGet.CommandLine.XPlat/xlf/Strings.ru.xlf index 8f9667ebc0e..ea40be5079e 100644 --- a/src/NuGet.Core/NuGet.CommandLine.XPlat/xlf/Strings.ru.xlf +++ b/src/NuGet.Core/NuGet.CommandLine.XPlat/xlf/Strings.ru.xlf @@ -47,11 +47,6 @@ Идентификатор добавляемого пакета. - - Allows prerelease packages to be installed. - Разрешает установку пакетов предварительного выпуска. - - Version of the package to be added. Версия добавляемого пакета. @@ -358,12 +353,6 @@ NuGet requires HTTPS sources. To use HTTP sources, you must explicitly set 'allo Invalid version value '{0}'. 0 - package version - - Invalid verbosity level '{0}'. Allowed values are: quiet, normal, detailed. - Invalid verbosity level '{0}'. Allowed values are: quiet, normal, detailed. - 0 - verbosity level -do not translate: quiet, normal, detailed. - Invalid version range '{0}' Недопустимый диапазон версий "{0}" @@ -409,6 +398,11 @@ do not translate: quiet, normal, detailed. В проекте "{0}" используется файл package.config для пакетов NuGet, а команда работает только с проектами, содержащими ссылки на пакеты. + + Unable to find a valid package version + Unable to find a valid package version + + Package source mapping is enabled, but no mapping for package {0} was found. See https://aka.ms/nuget/psm for more information. Сопоставление источника пакета включено, но сопоставление для пакета {0} не найдено. Дополнительные сведения см. на странице https://aka.ms/nuget/psm. @@ -897,6 +891,69 @@ For more information, visit https://docs.nuget.org/docs/reference/command-line-r {0}: App full name {1}: App version + + Allows downloading from HTTP (non-HTTPS) package sources. + Allows downloading from HTTP (non-HTTPS) package sources. + + + + Skipping download. Package '{0}' version {1} already exists at '{2}'. + Skipping download. Package '{0}' version {1} already exists at '{2}'. + 0 - package id +1 - version +2 - directory path where the package was downloaded + + + Package '{0}' ({1}) failed to download. + Package '{0}' ({1}) failed to download. + 0 - package id +1 - package version + + + latest version + latest version + + + + Directory where the package will be placed. Defaults to the current working directory. + Directory where the package will be placed. Defaults to the current working directory. + + + + Package identifier (e.g. 'Newtonsoft.Json'). + Package identifier (e.g. 'Newtonsoft.Json'). + + + + Specifies one or more NuGet package sources to use. + Specifies one or more NuGet package sources to use. + + + + Downloading package {0}, version {1}. + Downloading package {0}, version {1}. + 0- package id +1- package version + + + Package '{0}' ({1}) successfully downloaded to '{2}'. + Package '{0}' ({1}) successfully downloaded to '{2}'. + 0 - package id +1 - package version +2 - package download location + + + Package download failed for `{0} {1}` from source `{2}`. + Package download failed for `{0} {1}` from source `{2}`. + 0 package id +1 package version +2 package source + + + Downloads a NuGet package to a local folder without requiring a project file. + Downloads a NuGet package to a local folder without requiring a project file. + + Update referenced packages in a project or solution. Обновите пакеты, на которые ссылается проект или решение. @@ -990,44 +1047,16 @@ For more information, visit https://docs.nuget.org/docs/reference/command-line-r Обновление пакетов с предупреждениями по безопасности в {0} {0} - the project/solution being checked - - Skipping download. Package '{0}' version {1} already exists at '{2}'. - Skipping download. Package '{0}' version {1} already exists at '{2}'. - 0 - package id -1 - version -2 - directory path where the package was downloaded - - - Package '{0}' ({1}) failed to download. - Package '{0}' ({1}) failed to download. - 0 - package id -1 - package version - - - Downloading package {0}, version {1}. - Downloading package {0}, version {1}. - 0- package id -1- package version - - - Package '{0}' ({1}) successfully downloaded to '{2}'. - Package '{0}' ({1}) successfully downloaded to '{2}'. - 0 - package id -1 - package version -2 - package download location - - - Package download failed for `{0} {1}` from source `{2}`. - Package download failed for `{0} {1}` from source `{2}`. - 0 package id -1 package version -2 package source - There are no stable versions available, {0} is the best available. Consider adding the --prerelease option Нет доступных стабильных версий. Наилучшей из доступных является {0}. Попробуйте добавить параметр --prerelease. {0} - Package Version + + Allows prerelease packages to be installed. + Allows prerelease packages to be installed. + + If a package and version already exists, skip it and continue with the next package in the push, if any. Если пакет и версия уже существуют, они пропускаются и осуществляется переход к следующему отправляемому пакету, если он есть. @@ -1490,61 +1519,6 @@ The search is a case-insensitive string comparison using the supplied value, whi Файл ресурсов "{0}" для проекта "{1}" не содержит целевой объект для указанной входной платформы "{2}". {0} - Assets file path, {1} - Project name, {2} - Framework - - Allows downloading from HTTP (non-HTTPS) package sources. - Allows downloading from HTTP (non-HTTPS) package sources. - - - - Directory where the package will be placed. Defaults to the current working directory. - Directory where the package will be placed. Defaults to the current working directory. - - - - Path to a NuGet.config file to use for settings and sources. - Path to a NuGet.config file to use for settings and sources. - - - - Downloads a NuGet package to a local folder without requiring a project file. - Downloads a NuGet package to a local folder without requiring a project file. - - - - Download only the .nupkg file without extracting it. - Download only the .nupkg file without extracting it. - - - - Allows interactive authentication if required. - Allows interactive authentication if required. - - - - Package identifier (e.g. 'Newtonsoft.Json'). - Package identifier (e.g. 'Newtonsoft.Json'). - - - - Allows downloading prerelease versions. - Allows downloading prerelease versions. - - - - Specifies one or more NuGet package sources to use. - Specifies one or more NuGet package sources to use. - - - - Set the verbosity level. Allowed values: quiet | normal | detailed. - Set the verbosity level. Allowed values: quiet | normal | detailed. - Please do not translate `quiet`, `normal`, `detailed` - - - Specific package version to download - Specific package version to download - - Searches one or more package sources for packages that match a search term. If no sources are specified, all sources defined in the NuGet.Config are used. Ищет в одном или нескольких источниках пакетов пакеты, соответствующие поисковому запросу. Если источники не указаны, используются все источники, определенные в NuGet.Config. diff --git a/src/NuGet.Core/NuGet.CommandLine.XPlat/xlf/Strings.tr.xlf b/src/NuGet.Core/NuGet.CommandLine.XPlat/xlf/Strings.tr.xlf index 45dab8e0979..367538d3a6b 100644 --- a/src/NuGet.Core/NuGet.CommandLine.XPlat/xlf/Strings.tr.xlf +++ b/src/NuGet.Core/NuGet.CommandLine.XPlat/xlf/Strings.tr.xlf @@ -47,11 +47,6 @@ Eklenecek paketin kimliği. - - Allows prerelease packages to be installed. - Ön sürüm paketlerinin yüklenmesini sağlar. - - Version of the package to be added. Eklenecek paket sürümü. @@ -359,12 +354,6 @@ NuGet için HTTPS kaynakları gereklidir. HTTP kaynaklarını kullanmak için Nu Invalid version value '{0}'. 0 - package version - - Invalid verbosity level '{0}'. Allowed values are: quiet, normal, detailed. - Invalid verbosity level '{0}'. Allowed values are: quiet, normal, detailed. - 0 - verbosity level -do not translate: quiet, normal, detailed. - Invalid version range '{0}' Geçersiz sürüm aralığı '{0}' @@ -410,6 +399,11 @@ do not translate: quiet, normal, detailed. `{0}` projesi, NuGuet paketleri için package.config dosyasını kullanıyor fakat komut yalnızca paket başvurusu projeleriyle çalışır. + + Unable to find a valid package version + Unable to find a valid package version + + Package source mapping is enabled, but no mapping for package {0} was found. See https://aka.ms/nuget/psm for more information. Paket kaynağı eşlemesi etkinleştirildi, ancak paket {0} için eşleme bulunamadı. Daha fazla bilgi için https://aka.ms/nuget/psm adresini ziyaret edin. @@ -898,6 +892,69 @@ Daha fazla bilgi için bkz. https://docs.nuget.org/docs/reference/command-line-r {0}: App full name {1}: App version + + Allows downloading from HTTP (non-HTTPS) package sources. + Allows downloading from HTTP (non-HTTPS) package sources. + + + + Skipping download. Package '{0}' version {1} already exists at '{2}'. + Skipping download. Package '{0}' version {1} already exists at '{2}'. + 0 - package id +1 - version +2 - directory path where the package was downloaded + + + Package '{0}' ({1}) failed to download. + Package '{0}' ({1}) failed to download. + 0 - package id +1 - package version + + + latest version + latest version + + + + Directory where the package will be placed. Defaults to the current working directory. + Directory where the package will be placed. Defaults to the current working directory. + + + + Package identifier (e.g. 'Newtonsoft.Json'). + Package identifier (e.g. 'Newtonsoft.Json'). + + + + Specifies one or more NuGet package sources to use. + Specifies one or more NuGet package sources to use. + + + + Downloading package {0}, version {1}. + Downloading package {0}, version {1}. + 0- package id +1- package version + + + Package '{0}' ({1}) successfully downloaded to '{2}'. + Package '{0}' ({1}) successfully downloaded to '{2}'. + 0 - package id +1 - package version +2 - package download location + + + Package download failed for `{0} {1}` from source `{2}`. + Package download failed for `{0} {1}` from source `{2}`. + 0 package id +1 package version +2 package source + + + Downloads a NuGet package to a local folder without requiring a project file. + Downloads a NuGet package to a local folder without requiring a project file. + + Update referenced packages in a project or solution. Bir projede veya çözümde başvurulan paketleri güncelleştirin. @@ -991,44 +1048,16 @@ Daha fazla bilgi için bkz. https://docs.nuget.org/docs/reference/command-line-r {0} içerisinde güvenlik uyarıları içeren paketler güncelleştiriliyor {0} - the project/solution being checked - - Skipping download. Package '{0}' version {1} already exists at '{2}'. - Skipping download. Package '{0}' version {1} already exists at '{2}'. - 0 - package id -1 - version -2 - directory path where the package was downloaded - - - Package '{0}' ({1}) failed to download. - Package '{0}' ({1}) failed to download. - 0 - package id -1 - package version - - - Downloading package {0}, version {1}. - Downloading package {0}, version {1}. - 0- package id -1- package version - - - Package '{0}' ({1}) successfully downloaded to '{2}'. - Package '{0}' ({1}) successfully downloaded to '{2}'. - 0 - package id -1 - package version -2 - package download location - - - Package download failed for `{0} {1}` from source `{2}`. - Package download failed for `{0} {1}` from source `{2}`. - 0 package id -1 package version -2 package source - There are no stable versions available, {0} is the best available. Consider adding the --prerelease option Kullanılabilir kararlı bir sürüm olmadığından {0} en iyi seçenektir. --prerelease seçeneğini eklemeyi göz önünde bulundurun {0} - Package Version + + Allows prerelease packages to be installed. + Allows prerelease packages to be installed. + + If a package and version already exists, skip it and continue with the next package in the push, if any. Paket ve sürüm zaten mevcutsa bu paketi atlayın ve varsa gönderimde bulunan bir sonraki paketle devam edin. @@ -1491,61 +1520,6 @@ Arama, sağlanan değeri kullanan büyük/küçük harfe duyarsız bir dize kar '{1}' projesinin '{0}' varlık dosyası, belirtilen '{2}' giriş çerçevesi için bir hedef içermiyor. {0} - Assets file path, {1} - Project name, {2} - Framework - - Allows downloading from HTTP (non-HTTPS) package sources. - Allows downloading from HTTP (non-HTTPS) package sources. - - - - Directory where the package will be placed. Defaults to the current working directory. - Directory where the package will be placed. Defaults to the current working directory. - - - - Path to a NuGet.config file to use for settings and sources. - Path to a NuGet.config file to use for settings and sources. - - - - Downloads a NuGet package to a local folder without requiring a project file. - Downloads a NuGet package to a local folder without requiring a project file. - - - - Download only the .nupkg file without extracting it. - Download only the .nupkg file without extracting it. - - - - Allows interactive authentication if required. - Allows interactive authentication if required. - - - - Package identifier (e.g. 'Newtonsoft.Json'). - Package identifier (e.g. 'Newtonsoft.Json'). - - - - Allows downloading prerelease versions. - Allows downloading prerelease versions. - - - - Specifies one or more NuGet package sources to use. - Specifies one or more NuGet package sources to use. - - - - Set the verbosity level. Allowed values: quiet | normal | detailed. - Set the verbosity level. Allowed values: quiet | normal | detailed. - Please do not translate `quiet`, `normal`, `detailed` - - - Specific package version to download - Specific package version to download - - Searches one or more package sources for packages that match a search term. If no sources are specified, all sources defined in the NuGet.Config are used. Bir arama terimiyle eşleşen paketler için bir veya daha fazla paket kaynağı arar. Kaynak belirtilmezse NuGet.Config’de tanımlanan tüm kaynaklar kullanılır. diff --git a/src/NuGet.Core/NuGet.CommandLine.XPlat/xlf/Strings.zh-Hans.xlf b/src/NuGet.Core/NuGet.CommandLine.XPlat/xlf/Strings.zh-Hans.xlf index 87fd9aa758a..735609c4b84 100644 --- a/src/NuGet.Core/NuGet.CommandLine.XPlat/xlf/Strings.zh-Hans.xlf +++ b/src/NuGet.Core/NuGet.CommandLine.XPlat/xlf/Strings.zh-Hans.xlf @@ -47,11 +47,6 @@ 要添加的包 ID。 - - Allows prerelease packages to be installed. - 允许安装预发行包。 - - Version of the package to be added. 要添加的包版本。 @@ -358,12 +353,6 @@ NuGet 需要 HTTPS 源。要使用 HTTP 源,必须在 NuGet.Config 文件中 Invalid version value '{0}'. 0 - package version - - Invalid verbosity level '{0}'. Allowed values are: quiet, normal, detailed. - Invalid verbosity level '{0}'. Allowed values are: quiet, normal, detailed. - 0 - verbosity level -do not translate: quiet, normal, detailed. - Invalid version range '{0}' 版本范围“{0}”无效 @@ -409,6 +398,11 @@ do not translate: quiet, normal, detailed. 项目“{0}”对 NuGet 包使用 package.config,但该命令仅适用于包引用项目。 + + Unable to find a valid package version + Unable to find a valid package version + + Package source mapping is enabled, but no mapping for package {0} was found. See https://aka.ms/nuget/psm for more information. 已启用包源映射,但未找到包 {0} 的映射。有关详细信息,请参阅 https://aka.ms/nuget/psm。 @@ -897,6 +891,69 @@ For more information, visit https://docs.nuget.org/docs/reference/command-line-r {0}: App full name {1}: App version + + Allows downloading from HTTP (non-HTTPS) package sources. + Allows downloading from HTTP (non-HTTPS) package sources. + + + + Skipping download. Package '{0}' version {1} already exists at '{2}'. + Skipping download. Package '{0}' version {1} already exists at '{2}'. + 0 - package id +1 - version +2 - directory path where the package was downloaded + + + Package '{0}' ({1}) failed to download. + Package '{0}' ({1}) failed to download. + 0 - package id +1 - package version + + + latest version + latest version + + + + Directory where the package will be placed. Defaults to the current working directory. + Directory where the package will be placed. Defaults to the current working directory. + + + + Package identifier (e.g. 'Newtonsoft.Json'). + Package identifier (e.g. 'Newtonsoft.Json'). + + + + Specifies one or more NuGet package sources to use. + Specifies one or more NuGet package sources to use. + + + + Downloading package {0}, version {1}. + Downloading package {0}, version {1}. + 0- package id +1- package version + + + Package '{0}' ({1}) successfully downloaded to '{2}'. + Package '{0}' ({1}) successfully downloaded to '{2}'. + 0 - package id +1 - package version +2 - package download location + + + Package download failed for `{0} {1}` from source `{2}`. + Package download failed for `{0} {1}` from source `{2}`. + 0 package id +1 package version +2 package source + + + Downloads a NuGet package to a local folder without requiring a project file. + Downloads a NuGet package to a local folder without requiring a project file. + + Update referenced packages in a project or solution. 更新项目或解决方案中引用的包。 @@ -990,44 +1047,16 @@ For more information, visit https://docs.nuget.org/docs/reference/command-line-r 正在更新 {0} 中带有安全公告的包 {0} - the project/solution being checked - - Skipping download. Package '{0}' version {1} already exists at '{2}'. - Skipping download. Package '{0}' version {1} already exists at '{2}'. - 0 - package id -1 - version -2 - directory path where the package was downloaded - - - Package '{0}' ({1}) failed to download. - Package '{0}' ({1}) failed to download. - 0 - package id -1 - package version - - - Downloading package {0}, version {1}. - Downloading package {0}, version {1}. - 0- package id -1- package version - - - Package '{0}' ({1}) successfully downloaded to '{2}'. - Package '{0}' ({1}) successfully downloaded to '{2}'. - 0 - package id -1 - package version -2 - package download location - - - Package download failed for `{0} {1}` from source `{2}`. - Package download failed for `{0} {1}` from source `{2}`. - 0 package id -1 package version -2 package source - There are no stable versions available, {0} is the best available. Consider adding the --prerelease option 没有可用的稳定版本,{0} 是可用的最佳版本。请考虑添加 --prerelease 选项 {0} - Package Version + + Allows prerelease packages to be installed. + Allows prerelease packages to be installed. + + If a package and version already exists, skip it and continue with the next package in the push, if any. 如果包和版本已存在,则跳过它并继续推送中的下一个包(若有)。 @@ -1490,61 +1519,6 @@ The search is a case-insensitive string comparison using the supplied value, whi 项目“{1}”的资产文件“{0}”不包含指定输入框架“{2}”的目标。 {0} - Assets file path, {1} - Project name, {2} - Framework - - Allows downloading from HTTP (non-HTTPS) package sources. - Allows downloading from HTTP (non-HTTPS) package sources. - - - - Directory where the package will be placed. Defaults to the current working directory. - Directory where the package will be placed. Defaults to the current working directory. - - - - Path to a NuGet.config file to use for settings and sources. - Path to a NuGet.config file to use for settings and sources. - - - - Downloads a NuGet package to a local folder without requiring a project file. - Downloads a NuGet package to a local folder without requiring a project file. - - - - Download only the .nupkg file without extracting it. - Download only the .nupkg file without extracting it. - - - - Allows interactive authentication if required. - Allows interactive authentication if required. - - - - Package identifier (e.g. 'Newtonsoft.Json'). - Package identifier (e.g. 'Newtonsoft.Json'). - - - - Allows downloading prerelease versions. - Allows downloading prerelease versions. - - - - Specifies one or more NuGet package sources to use. - Specifies one or more NuGet package sources to use. - - - - Set the verbosity level. Allowed values: quiet | normal | detailed. - Set the verbosity level. Allowed values: quiet | normal | detailed. - Please do not translate `quiet`, `normal`, `detailed` - - - Specific package version to download - Specific package version to download - - Searches one or more package sources for packages that match a search term. If no sources are specified, all sources defined in the NuGet.Config are used. 在一个或多个包源中搜索与搜索词匹配的包。如果未指定任何源,则会使用 NuGet.Config 中定义的所有源。 diff --git a/src/NuGet.Core/NuGet.CommandLine.XPlat/xlf/Strings.zh-Hant.xlf b/src/NuGet.Core/NuGet.CommandLine.XPlat/xlf/Strings.zh-Hant.xlf index 4da72c30f92..efbc728dd39 100644 --- a/src/NuGet.Core/NuGet.CommandLine.XPlat/xlf/Strings.zh-Hant.xlf +++ b/src/NuGet.Core/NuGet.CommandLine.XPlat/xlf/Strings.zh-Hant.xlf @@ -47,11 +47,6 @@ 要新增的套件之識別碼。 - - Allows prerelease packages to be installed. - 允許安裝發行前版本套件。 - - Version of the package to be added. 要新增的套件版本。 @@ -358,12 +353,6 @@ NuGet 需要 HTTPS 來源。您必須在 NuGet.Config 檔案中將 'allowInsecur Invalid version value '{0}'. 0 - package version - - Invalid verbosity level '{0}'. Allowed values are: quiet, normal, detailed. - Invalid verbosity level '{0}'. Allowed values are: quiet, normal, detailed. - 0 - verbosity level -do not translate: quiet, normal, detailed. - Invalid version range '{0}' 不正確版本範圍 '{0}' @@ -409,6 +398,11 @@ do not translate: quiet, normal, detailed. 專案 `{0}` 會使用 NuGet 套件的 package.config,且命令僅能搭配套件參考專案使用。 + + Unable to find a valid package version + Unable to find a valid package version + + Package source mapping is enabled, but no mapping for package {0} was found. See https://aka.ms/nuget/psm for more information. 已啟用套件來源對應,但找不到套件 {0} 的對應。如需詳細資訊,請參閱 https://aka.ms/nuget/psm。 @@ -897,6 +891,69 @@ For more information, visit https://docs.nuget.org/docs/reference/command-line-r {0}: App full name {1}: App version + + Allows downloading from HTTP (non-HTTPS) package sources. + Allows downloading from HTTP (non-HTTPS) package sources. + + + + Skipping download. Package '{0}' version {1} already exists at '{2}'. + Skipping download. Package '{0}' version {1} already exists at '{2}'. + 0 - package id +1 - version +2 - directory path where the package was downloaded + + + Package '{0}' ({1}) failed to download. + Package '{0}' ({1}) failed to download. + 0 - package id +1 - package version + + + latest version + latest version + + + + Directory where the package will be placed. Defaults to the current working directory. + Directory where the package will be placed. Defaults to the current working directory. + + + + Package identifier (e.g. 'Newtonsoft.Json'). + Package identifier (e.g. 'Newtonsoft.Json'). + + + + Specifies one or more NuGet package sources to use. + Specifies one or more NuGet package sources to use. + + + + Downloading package {0}, version {1}. + Downloading package {0}, version {1}. + 0- package id +1- package version + + + Package '{0}' ({1}) successfully downloaded to '{2}'. + Package '{0}' ({1}) successfully downloaded to '{2}'. + 0 - package id +1 - package version +2 - package download location + + + Package download failed for `{0} {1}` from source `{2}`. + Package download failed for `{0} {1}` from source `{2}`. + 0 package id +1 package version +2 package source + + + Downloads a NuGet package to a local folder without requiring a project file. + Downloads a NuGet package to a local folder without requiring a project file. + + Update referenced packages in a project or solution. 更新專案或解決方案中參照的套件。 @@ -990,44 +1047,16 @@ For more information, visit https://docs.nuget.org/docs/reference/command-line-r 正在更新 {0} 中含有安全性建議的套件 {0} - the project/solution being checked - - Skipping download. Package '{0}' version {1} already exists at '{2}'. - Skipping download. Package '{0}' version {1} already exists at '{2}'. - 0 - package id -1 - version -2 - directory path where the package was downloaded - - - Package '{0}' ({1}) failed to download. - Package '{0}' ({1}) failed to download. - 0 - package id -1 - package version - - - Downloading package {0}, version {1}. - Downloading package {0}, version {1}. - 0- package id -1- package version - - - Package '{0}' ({1}) successfully downloaded to '{2}'. - Package '{0}' ({1}) successfully downloaded to '{2}'. - 0 - package id -1 - package version -2 - package download location - - - Package download failed for `{0} {1}` from source `{2}`. - Package download failed for `{0} {1}` from source `{2}`. - 0 package id -1 package version -2 package source - There are no stable versions available, {0} is the best available. Consider adding the --prerelease option 沒有可用的穩定版本,{0} 是最適用的版本。請考慮新增 --prerelease 選項 {0} - Package Version + + Allows prerelease packages to be installed. + Allows prerelease packages to be installed. + + If a package and version already exists, skip it and continue with the next package in the push, if any. 如果套件和版本已經存在,請予以跳過並繼續進行推送中的下一個套件 (如果有)。 @@ -1490,61 +1519,6 @@ The search is a case-insensitive string comparison using the supplied value, whi 專案 '{1}' 的資產檔案 '{0}' 未包含指定輸入架構 '{2}' 的目標。 {0} - Assets file path, {1} - Project name, {2} - Framework - - Allows downloading from HTTP (non-HTTPS) package sources. - Allows downloading from HTTP (non-HTTPS) package sources. - - - - Directory where the package will be placed. Defaults to the current working directory. - Directory where the package will be placed. Defaults to the current working directory. - - - - Path to a NuGet.config file to use for settings and sources. - Path to a NuGet.config file to use for settings and sources. - - - - Downloads a NuGet package to a local folder without requiring a project file. - Downloads a NuGet package to a local folder without requiring a project file. - - - - Download only the .nupkg file without extracting it. - Download only the .nupkg file without extracting it. - - - - Allows interactive authentication if required. - Allows interactive authentication if required. - - - - Package identifier (e.g. 'Newtonsoft.Json'). - Package identifier (e.g. 'Newtonsoft.Json'). - - - - Allows downloading prerelease versions. - Allows downloading prerelease versions. - - - - Specifies one or more NuGet package sources to use. - Specifies one or more NuGet package sources to use. - - - - Set the verbosity level. Allowed values: quiet | normal | detailed. - Set the verbosity level. Allowed values: quiet | normal | detailed. - Please do not translate `quiet`, `normal`, `detailed` - - - Specific package version to download - Specific package version to download - - Searches one or more package sources for packages that match a search term. If no sources are specified, all sources defined in the NuGet.Config are used. 搜尋一或多個套件來源以尋找符合搜尋字詞的套件。如果未指定來源,則會使用 NuGet.Config 中定義的所有來源。 diff --git a/test/NuGet.Core.Tests/NuGet.CommandLine.Xplat.Tests/Commands/Package/Download/PackageDownloadCommandTests.cs b/test/NuGet.Core.Tests/NuGet.CommandLine.Xplat.Tests/Commands/Package/Download/PackageDownloadCommandTests.cs index ccc5b5ac7cb..3d00cc5d9a8 100644 --- a/test/NuGet.Core.Tests/NuGet.CommandLine.Xplat.Tests/Commands/Package/Download/PackageDownloadCommandTests.cs +++ b/test/NuGet.Core.Tests/NuGet.CommandLine.Xplat.Tests/Commands/Package/Download/PackageDownloadCommandTests.cs @@ -17,25 +17,13 @@ namespace NuGet.CommandLine.Xplat.Tests.Commands.Package.Download public class PackageDownloadCommandTests { [Fact] - public async Task NoArguments_HasDefaultOptions() + public async Task NoArguments_ThrowsAnExceptionForMissingArg() { // Arrange string args = "package download"; - // Act - var result = await RunAsync(args); - - // Assert - result.Should().NotBeNull(); - result.Packages.Should().BeEmpty(); - result.Sources.Should().BeEmpty(); - result.OutputDirectory.Should().BeNull(); - result.ConfigFile.Should().BeNull(); - result.IncludePrerelease.Should().BeFalse(); - result.DownloadOnly.Should().BeFalse(); - result.AllowInsecureConnections.Should().BeFalse(); - result.Interactive.Should().BeFalse(); - result.LogLevel.Should().Be(LogLevel.Information); + // Act & Assert + await Assert.ThrowsAsync(() => RunAsync(args)); } [Fact] @@ -51,7 +39,7 @@ public async Task WithSinglePackage_ShouldParsePackageId() result.Should().NotBeNull(); result.Packages.Should().HaveCount(1); result.Packages[0].Id.Should().Be("Contoso.Utils"); - result.Packages[0].VersionRange.Should().BeNull(); + result.Packages[0].NuGetVersion.Should().BeNull(); } [Fact] @@ -67,13 +55,13 @@ public async Task WithMultiplePackages_ShouldParseAllPackageIds() result.Should().NotBeNull(); result.Packages.Should().HaveCount(2); result.Packages[0].Id.Should().Be("Contoso.Utils"); - result.Packages[0].VersionRange.Should().BeNull(); + result.Packages[0].NuGetVersion.Should().BeNull(); result.Packages[1].Id.Should().Be("Contoso.Framework"); - result.Packages[1].VersionRange.Should().BeNull(); + result.Packages[1].NuGetVersion.Should().BeNull(); } [Fact] - public async Task WithPackageAndVersion_ShouldParsePackageWithVersionRange() + public async Task WithPackageAndVersion_ShouldParsePackageWithVersion() { // Arrange string args = "package download Contoso.Utils@2.1.0"; @@ -85,25 +73,18 @@ public async Task WithPackageAndVersion_ShouldParsePackageWithVersionRange() result.Should().NotBeNull(); result.Packages.Should().HaveCount(1); result.Packages[0].Id.Should().Be("Contoso.Utils"); - result.Packages[0].VersionRange.Should().NotBeNull(); - result.Packages[0].VersionRange!.ToString().Should().Be("[2.1.0, )"); + result.Packages[0].NuGetVersion.Should().NotBeNull(); + result.Packages[0].NuGetVersion!.ToString().Should().Be("2.1.0"); } [Fact] - public async Task WithPackageAndVersionRange_ShouldParsePackageWithVersionRange() + public async Task WithPackageAndVersionRange_ShouldFail() { // Arrange string args = "package download Contoso.Utils@[2.0.0,3.0.0)"; - // Act - var result = await RunAsync(args); - - // Assert - result.Should().NotBeNull(); - result.Packages.Should().HaveCount(1); - result.Packages[0].Id.Should().Be("Contoso.Utils"); - result.Packages[0].VersionRange.Should().NotBeNull(); - result.Packages[0].VersionRange!.ToString().Should().Be("[2.0.0, 3.0.0)"); + // Act & Assert + await Assert.ThrowsAsync(() => RunAsync(args)); } [Fact] @@ -119,9 +100,9 @@ public async Task WithMixedPackages_ShouldParseMixedPackagesCorrectly() result.Should().NotBeNull(); result.Packages.Should().HaveCount(2); result.Packages[0].Id.Should().Be("Contoso.Utils"); - result.Packages[0].VersionRange.Should().NotBeNull(); + result.Packages[0].NuGetVersion.ToString().Should().Be("2.1.0"); result.Packages[1].Id.Should().Be("Contoso.Framework"); - result.Packages[1].VersionRange.Should().BeNull(); + result.Packages[1].NuGetVersion.Should().BeNull(); } [Fact] @@ -132,7 +113,7 @@ public async Task WithOutputAndConfig_ShouldBindPaths() string outDir = Path.Combine(pathContext.WorkingDirectory, "out"); string cfg = Path.Combine(pathContext.WorkingDirectory, "nuget.config"); - string args = $"package download --output-directory \"{outDir}\" --configfile \"{cfg}\""; + string args = $"package download Contoso --output \"{outDir}\" --configfile \"{cfg}\""; // Act var result = await RunAsync(args); @@ -149,7 +130,7 @@ public async Task WithOutputAndConfig_ShouldBindPaths() public async Task WithInteractiveOption_ShouldSetCorrectInteractiveValue(bool value) { // Arrange - string args = $"package download --interactive:{value}"; + string args = $"package download Contoso --interactive:{value}"; // Act var result = await RunAsync(args); @@ -163,14 +144,13 @@ public async Task WithInteractiveOption_ShouldSetCorrectInteractiveValue(bool va public async Task WithBooleanFlags_ShouldSetAllFlagsTrue() { // Arrange - string args = "package download --prerelease --download-only --allow-insecure-connections --interactive"; + string args = "package download contoso --prerelease --allow-insecure-connections --interactive"; // Act var result = await RunAsync(args); // Assert result.IncludePrerelease.Should().BeTrue(); - result.DownloadOnly.Should().BeTrue(); result.AllowInsecureConnections.Should().BeTrue(); result.Interactive.Should().BeTrue(); } @@ -194,7 +174,7 @@ public async Task WithBooleanFlags_ShouldSetAllFlagsTrue() public async Task WithVerbosityOption_ShouldSetCorrectLogLevel(string verbosityArgs, LogLevel expectedLogLevel) { // Arrange - string args = $"package download {verbosityArgs}"; + string args = $"package download contoso {verbosityArgs}"; // Act var result = await RunAsync(args); @@ -205,7 +185,7 @@ public async Task WithVerbosityOption_ShouldSetCorrectLogLevel(string verbosityA } [Fact] - public void WithInvalidVersionRange_ShouldHaveParseErrors() + public void WithInvalidVersion_ShouldHaveParseErrors() { // Arrange string args = "package download Contoso.Utils@invalid-version"; @@ -238,7 +218,7 @@ public async Task WithAllOptions_ShouldParseAllOptionsCorrectly() string outDir = Path.Combine(pathContext.WorkingDirectory, "out"); string cfg = Path.Combine(pathContext.WorkingDirectory, "nuget.config"); - string args = $"package download Contoso.Utils@2.1.0 --output-directory \"{outDir}\" --configfile \"{cfg}\" --prerelease --download-only --allow-insecure-connections --source s1 --source s2 --verbosity detailed --interactive"; + string args = $"package download Contoso.Utils@2.1.0 --output \"{outDir}\" --configfile \"{cfg}\" --prerelease --allow-insecure-connections --source s1 --source s2 --verbosity detailed --interactive"; // Act var result = await RunAsync(args); @@ -247,11 +227,10 @@ public async Task WithAllOptions_ShouldParseAllOptionsCorrectly() result.Should().NotBeNull(); result.Packages.Should().HaveCount(1); result.Packages[0].Id.Should().Be("Contoso.Utils"); - result.Packages[0].VersionRange.Should().NotBeNull(); + result.Packages[0].NuGetVersion.ToString().Should().Be("2.1.0"); result.OutputDirectory.Should().Be(outDir); result.ConfigFile.Should().Be(cfg); result.IncludePrerelease.Should().BeTrue(); - result.DownloadOnly.Should().BeTrue(); result.AllowInsecureConnections.Should().BeTrue(); result.Sources.Should().ContainInOrder("s1", "s2"); result.LogLevel.Should().Be(LogLevel.Verbose); diff --git a/test/NuGet.Core.Tests/NuGet.CommandLine.Xplat.Tests/Commands/Package/Download/PackageDownloadRunnerTests.cs b/test/NuGet.Core.Tests/NuGet.CommandLine.Xplat.Tests/Commands/Package/Download/PackageDownloadRunnerTests.cs index cea54fde584..f7fe6d031ed 100644 --- a/test/NuGet.Core.Tests/NuGet.CommandLine.Xplat.Tests/Commands/Package/Download/PackageDownloadRunnerTests.cs +++ b/test/NuGet.Core.Tests/NuGet.CommandLine.Xplat.Tests/Commands/Package/Download/PackageDownloadRunnerTests.cs @@ -15,6 +15,7 @@ namespace NuGet.CommandLine.Xplat.Tests; using NuGet.CommandLine.XPlat.Commands.Package.PackageDownload; using NuGet.Common; using NuGet.Configuration; +using NuGet.Protocol; using NuGet.Protocol.Core.Types; using NuGet.Test.Utility; using NuGet.Versioning; @@ -41,7 +42,7 @@ public async Task RunAsync_ExplicitVersionFromLocalFolderSource_SucceedsAsync() var args = new PackageDownloadArgs() { - Packages = [new Package { Id = id, VersionRange = VersionRange.Parse(version) }], + Packages = [new PackageWithNuGetVersion { Id = id, NuGetVersion = NuGetVersion.Parse(version) }], OutputDirectory = outputDir, }; @@ -82,7 +83,7 @@ public async Task RunAsync_NoVersionWhenPrereleaseNotIncluded_PicksLatestStable( var args = new PackageDownloadArgs() { - Packages = [new Package { Id = id, VersionRange = null }], + Packages = [new PackageWithNuGetVersion { Id = id, NuGetVersion = null }], OutputDirectory = outputDir, }; @@ -123,7 +124,7 @@ public async Task RunAsync_NoVersionWithPrereleaseTrue_PicksHighestIncludingPrer var args = new PackageDownloadArgs() { - Packages = [new Package { Id = id, VersionRange = null }], + Packages = [new PackageWithNuGetVersion { Id = id, NuGetVersion = null }], OutputDirectory = outputDir, IncludePrerelease = true }; @@ -165,7 +166,7 @@ public async Task RunAsync_NoVersion_PicksHighestAcrossMultipleSources() var args = new PackageDownloadArgs() { - Packages = [new Package { Id = id, VersionRange = null }], + Packages = [new PackageWithNuGetVersion { Id = id, NuGetVersion = null }], OutputDirectory = outputDir, }; @@ -205,7 +206,7 @@ public async Task RunAsync_ExplicitVersionAlreadyInstalled_ShortCircuitsAndSucce // First run: install explicit version var args1 = new PackageDownloadArgs() { - Packages = [new Package { Id = id, VersionRange = VersionRange.Parse(v) }], + Packages = [new PackageWithNuGetVersion { Id = id, NuGetVersion = NuGetVersion.Parse(v) }], LogLevel = LogLevel.Verbose, OutputDirectory = outputDir, }; @@ -221,7 +222,7 @@ [new PackageSource(sourceDir)], // Second run: should short-circuit because already installed var args2 = new PackageDownloadArgs() { - Packages = [new Package { Id = id, VersionRange = VersionRange.Parse(v) }], + Packages = [new PackageWithNuGetVersion { Id = id, NuGetVersion = NuGetVersion.Parse(v) }], OutputDirectory = outputDir, }; @@ -256,7 +257,7 @@ public async Task RunAsync_WhenAllowInsecureConnectionsFalse_RejectsHttpSource() var args = new PackageDownloadArgs() { - Packages = [new Package { Id = "Contoso.Lib", VersionRange = null }], + Packages = [new PackageWithNuGetVersion { Id = "Contoso.Lib", NuGetVersion = null }], OutputDirectory = outputDir, }; @@ -293,7 +294,7 @@ public async Task RunAsync_PackageDoesNotExist_ReturnsError() var args = new PackageDownloadArgs() { - Packages = [new Package { Id = id, VersionRange = VersionRange.Parse(v) }], + Packages = [new PackageWithNuGetVersion { Id = id, NuGetVersion = NuGetVersion.Parse(v) }], OutputDirectory = outputDir, }; @@ -319,11 +320,11 @@ public async Task ResolvePackageDownloadVersion_ExactVersionAtFirstSource_Return // Arrange using var context = new SimpleTestPathContext(); var v123 = new NuGetVersion("1.2.3"); - var package = new Package { Id = "Contoso", VersionRange = VersionRange.Parse(v123.OriginalVersion) }; + var package = new PackageWithNuGetVersion { Id = "Contoso", NuGetVersion = NuGetVersion.Parse(v123.OriginalVersion) }; var sourceDir = Path.Combine(context.WorkingDirectory, "src"); Directory.CreateDirectory(sourceDir); - await SimpleTestPackageUtility.CreateFullPackageAsync(sourceDir, package.Id, package.VersionRange.OriginalString); - var sources = new[] { new PackageSource(sourceDir) }; + await SimpleTestPackageUtility.CreateFullPackageAsync(sourceDir, package.Id, package.NuGetVersion.ToNormalizedString()); + var sources = new[] { Repository.Factory.GetCoreV3(new PackageSource(sourceDir)) }; var logger = new Mock(MockBehavior.Loose); @@ -348,7 +349,7 @@ public async Task ResolvePackageDownloadVersion_ExactVersionMissingInFirstPresen using var context = new SimpleTestPathContext(); var v123 = new NuGetVersion("1.2.3"); var v100 = new NuGetVersion("1.0.0"); - var package = new Package { Id = "Contoso", VersionRange = VersionRange.Parse(v123.OriginalVersion) }; + var package = new PackageWithNuGetVersion { Id = "Contoso", NuGetVersion = NuGetVersion.Parse(v123.OriginalVersion) }; var sourceA = Path.Combine(context.WorkingDirectory, "srcA"); var sourceB = Path.Combine(context.WorkingDirectory, "srcB"); @@ -358,7 +359,7 @@ public async Task ResolvePackageDownloadVersion_ExactVersionMissingInFirstPresen await SimpleTestPackageUtility.CreateFullPackageAsync(sourceA, package.Id, v100.OriginalVersion); await SimpleTestPackageUtility.CreateFullPackageAsync(sourceB, package.Id, v123.OriginalVersion); - var sources = new[] { new PackageSource(sourceA), new PackageSource(sourceB) }; + var sources = new[] { Repository.Factory.GetCoreV3(new PackageSource(sourceA)), Repository.Factory.GetCoreV3(new PackageSource(sourceB)) }; var logger = new Mock(MockBehavior.Loose); // Act @@ -375,7 +376,7 @@ public async Task ResolvePackageDownloadVersion_NoVersionSpecifiedExcludePrerele { // Arrange using var context = new SimpleTestPathContext(); - var package = new Package { Id = "Contoso", VersionRange = null }; + var package = new PackageWithNuGetVersion { Id = "Contoso", NuGetVersion = null }; var sourceA = Path.Combine(context.WorkingDirectory, "srcA"); var sourceB = Path.Combine(context.WorkingDirectory, "srcB"); Directory.CreateDirectory(sourceA); @@ -385,7 +386,7 @@ public async Task ResolvePackageDownloadVersion_NoVersionSpecifiedExcludePrerele await SimpleTestPackageUtility.CreateFullPackageAsync(sourceA, package.Id, "1.5.0-alpha"); // prerelease; should be ignored await SimpleTestPackageUtility.CreateFullPackageAsync(sourceB, package.Id, "2.0.0"); // highest stable - var sources = new[] { new PackageSource(sourceA), new PackageSource(sourceB) }; + var sources = new[] { Repository.Factory.GetCoreV3(new PackageSource(sourceA)), Repository.Factory.GetCoreV3(new PackageSource(sourceB)) }; var logger = new Mock(MockBehavior.Loose); // Act @@ -402,7 +403,7 @@ public async Task ResolvePackageDownloadVersion_NoVersionSpecifiedIncludePrerele { // Arrange using var context = new SimpleTestPathContext(); - var package = new Package { Id = "Contoso", VersionRange = null }; // pickLatest + var package = new PackageWithNuGetVersion { Id = "Contoso", NuGetVersion = null }; // pickLatest var sourceA = Path.Combine(context.WorkingDirectory, "srcA"); var sourceB = Path.Combine(context.WorkingDirectory, "srcB"); Directory.CreateDirectory(sourceA); @@ -411,7 +412,7 @@ public async Task ResolvePackageDownloadVersion_NoVersionSpecifiedIncludePrerele await SimpleTestPackageUtility.CreateFullPackageAsync(sourceA, package.Id, "2.0.0"); // stable await SimpleTestPackageUtility.CreateFullPackageAsync(sourceB, package.Id, "2.1.0-rc.1"); // higher (prerelease) - var sources = new[] { new PackageSource(sourceA), new PackageSource(sourceB) }; + var sources = new[] { Repository.Factory.GetCoreV3(new PackageSource(sourceA)), Repository.Factory.GetCoreV3(new PackageSource(sourceB)) }; var logger = new Mock(MockBehavior.Loose); // Act @@ -423,76 +424,18 @@ public async Task ResolvePackageDownloadVersion_NoVersionSpecifiedIncludePrerele Assert.Equal(sourceB, resolvedRepo.PackageSource.Source); } - [Fact] - public async Task ResolvePackageDownloadVersion_MinInclusiveVersionRange_PicksLowestSatisfyingAcrossSources() - { - // Arrange - using var context = new SimpleTestPathContext(); - var min = new NuGetVersion("1.0.0"); - var max = new NuGetVersion("2.0.0"); - var range = VersionRange.Parse($"[{min.OriginalVersion}, {max.OriginalVersion})"); - - var package = new Package { Id = "Contoso", VersionRange = range }; - - var sourceA = Path.Combine(context.WorkingDirectory, "srcA"); - var sourceB = Path.Combine(context.WorkingDirectory, "srcB"); - Directory.CreateDirectory(sourceA); - Directory.CreateDirectory(sourceB); - - await SimpleTestPackageUtility.CreateFullPackageAsync(sourceA, package.Id, "1.0.0"); - await SimpleTestPackageUtility.CreateFullPackageAsync(sourceA, package.Id, "1.5.0"); - await SimpleTestPackageUtility.CreateFullPackageAsync(sourceB, package.Id, "1.8.0"); - - var sources = new[] { new PackageSource(sourceA), new PackageSource(sourceB) }; - var logger = new Mock(MockBehavior.Loose); - - // Act - (NuGetVersion resolved, SourceRepository resolvedRepo) = await PackageDownloadRunner.ResolvePackageDownloadVersion( - package, sources, new SourceCacheContext(), logger.Object, includePrerelease: false, CancellationToken.None); - - // Assert - resolved.Should().Be(min); - resolvedRepo.PackageSource.Source.Should().Be(sourceA); - } - - [Fact] - public async Task ResolvePackageDownloadVersion_MinAndMaxInclusiveRange_UsesMinVersionIfPresent() - { - // Arrange - using var context = new SimpleTestPathContext(); - var rangeText = "[1.0.0,2.0.0]"; - var package = new Package { Id = "Contoso", VersionRange = VersionRange.Parse(rangeText) }; - - var sourceA = Path.Combine(context.WorkingDirectory, "srcA"); - Directory.CreateDirectory(sourceA); - - await SimpleTestPackageUtility.CreateFullPackageAsync(sourceA, package.Id, "1.0.0"); // MinVersion present - await SimpleTestPackageUtility.CreateFullPackageAsync(sourceA, package.Id, "2.0.0"); - - var sources = new[] { new PackageSource(sourceA) }; - var logger = new Mock(MockBehavior.Loose); - - // Act - (NuGetVersion resolved, SourceRepository resolvedRepo) = await PackageDownloadRunner.ResolvePackageDownloadVersion( - package, sources, new SourceCacheContext(), logger.Object, includePrerelease: false, CancellationToken.None); - - // Assert (pins current behavior) - resolved.Should().Be(new NuGetVersion("1.0.0")); - resolvedRepo.PackageSource.Source.Should().Be(sourceA); - } - [Fact] public async Task ResolvePackageDownloadVersion_NoMatchesAnywhere_ReturnsNull() { using var context = new SimpleTestPathContext(); // Arrange - var package = new Package { Id = "Contoso", VersionRange = null }; // pickLatest + var package = new PackageWithNuGetVersion { Id = "Contoso", NuGetVersion = null }; // pickLatest var sourceA = Path.Combine(context.WorkingDirectory, "emptyA"); var sourceB = Path.Combine(context.WorkingDirectory, "emptyB"); Directory.CreateDirectory(sourceA); Directory.CreateDirectory(sourceB); - var sources = new[] { new PackageSource(sourceA), new PackageSource(sourceB) }; + var sources = new[] { Repository.Factory.GetCoreV3(new PackageSource(sourceA)), Repository.Factory.GetCoreV3(new PackageSource(sourceB)) }; var logger = new Mock(MockBehavior.Loose); logger.Setup(l => l.LogError(It.IsAny())); @@ -512,13 +455,13 @@ public async Task ResolvePackageDownloadVersion_ExactVersionNotFoundAnywhere_Ret // Arrange using var context = new SimpleTestPathContext(); var v123 = new NuGetVersion("1.2.3"); - var package = new Package { Id = "Contoso", VersionRange = VersionRange.Parse(v123.OriginalVersion) }; + var package = new PackageWithNuGetVersion { Id = "Contoso", NuGetVersion = NuGetVersion.Parse(v123.OriginalVersion) }; var sourceDir = Path.Combine(context.WorkingDirectory, "src"); Directory.CreateDirectory(sourceDir); await SimpleTestPackageUtility.CreateFullPackageAsync(sourceDir, package.Id, "1.0.0"); - var sources = new[] { new PackageSource(sourceDir) }; + var sources = new[] { Repository.Factory.GetCoreV3(new PackageSource(sourceDir)) }; var logger = new Mock(MockBehavior.Loose); logger.Setup(l => l.LogError(It.IsAny())); @@ -542,13 +485,13 @@ public async Task ResolvePackageDownloadVersion_OnlyPrereleaseExistsAndExcludePr using var context = new SimpleTestPathContext(); // Arrange - var package = new Package { Id = "Contoso", VersionRange = null }; // pickLatest = true + var package = new PackageWithNuGetVersion { Id = "Contoso", NuGetVersion = null }; // pickLatest = true var sourceDir = Path.Combine(context.WorkingDirectory, "src"); Directory.CreateDirectory(sourceDir); await SimpleTestPackageUtility.CreateFullPackageAsync(sourceDir, package.Id, "3.0.0-beta.1"); // prerelease only - var sources = new[] { new PackageSource(sourceDir) }; + var sources = new[] { Repository.Factory.GetCoreV3(new PackageSource(sourceDir)) }; var logger = new Mock(MockBehavior.Loose); logger.Setup(l => l.LogError(It.IsAny())); @@ -565,36 +508,4 @@ public async Task ResolvePackageDownloadVersion_OnlyPrereleaseExistsAndExcludePr Assert.Null(resolved); Assert.Null(resolvedRepo); } - - [Fact] - public async Task ResolvePackageDownloadVersion_VersionRangeMinNotAvailable_PicksNextLowestSatisfying() - { - // Arrange - using var context = new SimpleTestPathContext(); - var min = new NuGetVersion("1.0.0"); - var max = new NuGetVersion("3.0.0"); - - var package = new Package { Id = "Contoso", VersionRange = VersionRange.Parse($"[{min.OriginalVersion}, {max.OriginalVersion}]") }; - - var sourceDir = Path.Combine(context.WorkingDirectory, "src"); - Directory.CreateDirectory(sourceDir); - - await SimpleTestPackageUtility.CreateFullPackageAsync(sourceDir, package.Id, "1.5.0"); - await SimpleTestPackageUtility.CreateFullPackageAsync(sourceDir, package.Id, "2.0.0"); - - var sources = new[] { new PackageSource(sourceDir) }; - var logger = new Mock(MockBehavior.Loose); - - // Act - (NuGetVersion resolved, SourceRepository resolvedRepo) = await PackageDownloadRunner.ResolvePackageDownloadVersion( - package, - sources, - new SourceCacheContext(), - logger.Object, - includePrerelease: false, - CancellationToken.None); - - Assert.Equal(new NuGetVersion("1.5.0"), resolved); - Assert.Equal(sourceDir, resolvedRepo.PackageSource.Source); - } } From 8d14247d284d03d500ad0f7cbcab92d5e61400f0 Mon Sep 17 00:00:00 2001 From: Nigusu Yenework Date: Thu, 16 Oct 2025 14:02:24 -0700 Subject: [PATCH 05/15] oops --- .../Commands/Package/Download/PackageDownloadRunner.cs | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/NuGet.Core/NuGet.CommandLine.XPlat/Commands/Package/Download/PackageDownloadRunner.cs b/src/NuGet.Core/NuGet.CommandLine.XPlat/Commands/Package/Download/PackageDownloadRunner.cs index 7e42dc83897..bea659879e7 100644 --- a/src/NuGet.Core/NuGet.CommandLine.XPlat/Commands/Package/Download/PackageDownloadRunner.cs +++ b/src/NuGet.Core/NuGet.CommandLine.XPlat/Commands/Package/Download/PackageDownloadRunner.cs @@ -173,9 +173,10 @@ await ResolvePackageDownloadVersion( foreach (var p in packages) { var v = p.Identity.Version; - if (versionToDownload is null || v > versionToDownload) + if (versionToDownload == null || v > versionToDownload) { versionToDownload = v; + downloadSourceRepository = repo; } } } From 634550318a892e9fd8030b3c87664e0eec8498fe Mon Sep 17 00:00:00 2001 From: Nigusu Yenework Date: Thu, 16 Oct 2025 14:53:20 -0700 Subject: [PATCH 06/15] cleanup --- .../Commands/Package/Download/PackageDownloadCommand.cs | 1 - .../Commands/Package/Download/PackageDownloadRunner.cs | 1 - src/NuGet.Core/NuGet.CommandLine.XPlat/Program.cs | 4 +++- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/src/NuGet.Core/NuGet.CommandLine.XPlat/Commands/Package/Download/PackageDownloadCommand.cs b/src/NuGet.Core/NuGet.CommandLine.XPlat/Commands/Package/Download/PackageDownloadCommand.cs index f480f6e27fa..3224a0d4ebd 100644 --- a/src/NuGet.Core/NuGet.CommandLine.XPlat/Commands/Package/Download/PackageDownloadCommand.cs +++ b/src/NuGet.Core/NuGet.CommandLine.XPlat/Commands/Package/Download/PackageDownloadCommand.cs @@ -64,7 +64,6 @@ public static void Register(Command packageCommand, Option interactiveOpti var verbosity = CommonOptions.GetVerbosityOption(); - downloadCommand.Arguments.Add(packagesArguments); downloadCommand.Options.Add(allowInsecureConnections); downloadCommand.Options.Add(configFile); diff --git a/src/NuGet.Core/NuGet.CommandLine.XPlat/Commands/Package/Download/PackageDownloadRunner.cs b/src/NuGet.Core/NuGet.CommandLine.XPlat/Commands/Package/Download/PackageDownloadRunner.cs index bea659879e7..09806b2b816 100644 --- a/src/NuGet.Core/NuGet.CommandLine.XPlat/Commands/Package/Download/PackageDownloadRunner.cs +++ b/src/NuGet.Core/NuGet.CommandLine.XPlat/Commands/Package/Download/PackageDownloadRunner.cs @@ -155,7 +155,6 @@ await ResolvePackageDownloadVersion( continue; } - var versions = packages?.Select(p => p.Identity.Version); if (versionSpecified) { // If an exact version is specified, check if it exists at this source diff --git a/src/NuGet.Core/NuGet.CommandLine.XPlat/Program.cs b/src/NuGet.Core/NuGet.CommandLine.XPlat/Program.cs index da6afdf4c6c..bf1af280fa6 100644 --- a/src/NuGet.Core/NuGet.CommandLine.XPlat/Program.cs +++ b/src/NuGet.Core/NuGet.CommandLine.XPlat/Program.cs @@ -8,10 +8,12 @@ using System.Linq; using System.Threading; using Microsoft.Extensions.CommandLineUtils; -using NuGet.CommandLine.XPlat.Commands.Package.PackageDownload; using NuGet.CommandLine.XPlat.Commands.Package.Update; using NuGet.Commands; using NuGet.Common; +#if DEBUG +using NuGet.CommandLine.XPlat.Commands.Package.PackageDownload; +#endif namespace NuGet.CommandLine.XPlat { From ca34b3322d1e502385ba8562fecd340ab31c9166 Mon Sep 17 00:00:00 2001 From: Nigusu Yenework Date: Thu, 16 Oct 2025 15:04:50 -0700 Subject: [PATCH 07/15] move func tests --- .../NuGet.XPlat.FuncTest.csproj | 3 +- .../Download/PackageDownloadRunnerTests.cs | 314 ++++++++++++++++++ .../Download/PackageDownloadRunnerTests.cs | 295 ---------------- 3 files changed, 315 insertions(+), 297 deletions(-) create mode 100644 test/NuGet.Core.FuncTests/NuGet.XPlat.FuncTest/Package/Download/PackageDownloadRunnerTests.cs diff --git a/test/NuGet.Core.FuncTests/NuGet.XPlat.FuncTest/NuGet.XPlat.FuncTest.csproj b/test/NuGet.Core.FuncTests/NuGet.XPlat.FuncTest/NuGet.XPlat.FuncTest.csproj index 73610909851..816a7c37f2e 100644 --- a/test/NuGet.Core.FuncTests/NuGet.XPlat.FuncTest/NuGet.XPlat.FuncTest.csproj +++ b/test/NuGet.Core.FuncTests/NuGet.XPlat.FuncTest/NuGet.XPlat.FuncTest.csproj @@ -14,8 +14,7 @@ - + diff --git a/test/NuGet.Core.FuncTests/NuGet.XPlat.FuncTest/Package/Download/PackageDownloadRunnerTests.cs b/test/NuGet.Core.FuncTests/NuGet.XPlat.FuncTest/Package/Download/PackageDownloadRunnerTests.cs new file mode 100644 index 00000000000..ff08420d2b1 --- /dev/null +++ b/test/NuGet.Core.FuncTests/NuGet.XPlat.FuncTest/Package/Download/PackageDownloadRunnerTests.cs @@ -0,0 +1,314 @@ +// 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. + +namespace NuGet.CommandLine.Xplat.Tests; + +using System; +using System.IO; +using System.Linq; +using System.Threading; +using System.Threading.Tasks; +using FluentAssertions; +using Moq; +using NuGet.CommandLine.XPlat; +using NuGet.CommandLine.XPlat.Commands.Package; +using NuGet.CommandLine.XPlat.Commands.Package.PackageDownload; +using NuGet.Common; +using NuGet.Configuration; +using NuGet.Test.Utility; +using NuGet.Versioning; +using Xunit; + +public class PackageDownloadRunnerTests +{ + [Fact] + public async Task RunAsync_ExplicitVersionFromLocalFolderSource_SucceedsAsync() + { + // Arrange + using var context = new SimpleTestPathContext(); + var sourceDir = Path.Combine(context.WorkingDirectory, "src"); + var outputDir = Path.Combine(context.WorkingDirectory, "packages"); + Directory.CreateDirectory(sourceDir); + Directory.CreateDirectory(outputDir); + + var id = "Contoso.Lib"; + var version = "1.2.3"; + await SimpleTestPackageUtility.CreateFullPackageAsync(sourceDir, id, version); + + var logger = new Mock(MockBehavior.Loose); + var settings = new Mock(MockBehavior.Loose); + + var args = new PackageDownloadArgs() + { + Packages = [new PackageWithNuGetVersion { Id = id, NuGetVersion = NuGetVersion.Parse(version) }], + OutputDirectory = outputDir, + }; + + + // Act + var result = await PackageDownloadRunner.RunAsync( + args, + logger.Object, + [new(sourceDir)], + settings.Object, + CancellationToken.None); + + // Assert + result.Should().Be(PackageDownloadRunner.ExitCodeSuccess); + var installDir = Path.Combine(outputDir, id.ToLowerInvariant(), version); + Directory.Exists(installDir).Should().BeTrue(); + Directory.EnumerateFiles(installDir, "*.nupkg").Any().Should().BeTrue(); + File.Exists(Path.Combine(installDir, $"{id.ToLowerInvariant()}.{version}.nupkg")).Should().BeTrue(); + } + + [Fact] + public async Task RunAsync_NoVersionWhenPrereleaseNotIncluded_PicksLatestStable() + { + // Arrange + using var context = new SimpleTestPathContext(); + var sourceDir = Path.Combine(context.WorkingDirectory, "src"); + var outputDir = Path.Combine(context.WorkingDirectory, "packages"); + Directory.CreateDirectory(sourceDir); + Directory.CreateDirectory(outputDir); + + var id = "Contoso.Core"; + await SimpleTestPackageUtility.CreateFullPackageAsync(sourceDir, id, "1.0.0"); + await SimpleTestPackageUtility.CreateFullPackageAsync(sourceDir, id, "1.1.0"); + await SimpleTestPackageUtility.CreateFullPackageAsync(sourceDir, id, "2.0.0-beta"); + + var logger = new Mock(MockBehavior.Loose); + var settings = new Mock(MockBehavior.Loose); + + var args = new PackageDownloadArgs() + { + Packages = [new PackageWithNuGetVersion { Id = id, NuGetVersion = null }], + OutputDirectory = outputDir, + }; + + // Act + var result = await PackageDownloadRunner.RunAsync( + args, + logger.Object, + [new PackageSource(sourceDir)], + settings.Object, + CancellationToken.None); + + // Assert + result.Should().Be(PackageDownloadRunner.ExitCodeSuccess); + + var chosen = Path.Combine(outputDir, id.ToLowerInvariant(), "1.1.0"); + var notChosen = Path.Combine(outputDir, id.ToLowerInvariant(), "2.0.0-beta"); + Directory.Exists(chosen).Should().BeTrue("latest stable (1.1.0) should be chosen"); + Directory.Exists(notChosen).Should().BeFalse("prerelease (2.0.0-beta) should not be chosen"); + File.Exists(Path.Combine(chosen, $"{id.ToLowerInvariant()}.1.1.0.nupkg")).Should().BeTrue(); + } + + [Fact] + public async Task RunAsync_NoVersionWithPrereleaseTrue_PicksHighestIncludingPrerelease() + { + // Arrange + using var context = new SimpleTestPathContext(); + var sourceDir = Path.Combine(context.WorkingDirectory, "src"); + var outputDir = Path.Combine(context.WorkingDirectory, "packages"); + Directory.CreateDirectory(sourceDir); + Directory.CreateDirectory(outputDir); + + var id = "Contoso.Preview"; + await SimpleTestPackageUtility.CreateFullPackageAsync(sourceDir, id, "1.3.0"); + await SimpleTestPackageUtility.CreateFullPackageAsync(sourceDir, id, "2.0.0-beta.2"); + + var logger = new Mock(MockBehavior.Loose); + var settings = new Mock(MockBehavior.Loose); + + var args = new PackageDownloadArgs() + { + Packages = [new PackageWithNuGetVersion { Id = id, NuGetVersion = null }], + OutputDirectory = outputDir, + IncludePrerelease = true + }; + + // Act + var result = await PackageDownloadRunner.RunAsync( + args, + logger.Object, + [new PackageSource(sourceDir)], + settings.Object, + CancellationToken.None); + + // Assert + result.Should().Be(PackageDownloadRunner.ExitCodeSuccess); + + var chosen = Path.Combine(outputDir, id.ToLowerInvariant(), "2.0.0-beta.2"); + Directory.Exists(chosen).Should().BeTrue("IncludePrerelease should allow picking 2.0.0-beta.2"); + File.Exists(Path.Combine(chosen, $"{id.ToLowerInvariant()}.2.0.0-beta.2.nupkg")).Should().BeTrue(); + } + + [Fact] + public async Task RunAsync_NoVersion_PicksHighestAcrossMultipleSources() + { + // Arrange + using var context = new SimpleTestPathContext(); + var srcA = Path.Combine(context.WorkingDirectory, "srcA"); + var srcB = Path.Combine(context.WorkingDirectory, "srcB"); + var outputDir = Path.Combine(context.WorkingDirectory, "packages"); + Directory.CreateDirectory(srcA); + Directory.CreateDirectory(srcB); + Directory.CreateDirectory(outputDir); + + var id = "Contoso.Toolkit"; + await SimpleTestPackageUtility.CreateFullPackageAsync(srcA, id, "1.1.0"); + await SimpleTestPackageUtility.CreateFullPackageAsync(srcB, id, "1.2.0"); + + var logger = new Mock(MockBehavior.Loose); + var settings = new Mock(MockBehavior.Loose); + + var args = new PackageDownloadArgs() + { + Packages = [new PackageWithNuGetVersion { Id = id, NuGetVersion = null }], + OutputDirectory = outputDir, + }; + + // Act + var result = await PackageDownloadRunner.RunAsync( + args, + logger.Object, + [new PackageSource(srcA), new PackageSource(srcB)], + settings.Object, + CancellationToken.None); + + // Assert + result.Should().Be(PackageDownloadRunner.ExitCodeSuccess); + + var chosen = Path.Combine(outputDir, id.ToLowerInvariant(), "1.2.0"); + Directory.Exists(chosen).Should().BeTrue("should choose the highest version found across all sources"); + File.Exists(Path.Combine(chosen, $"{id.ToLowerInvariant()}.1.2.0.nupkg")).Should().BeTrue(); + } + + [Fact] + public async Task RunAsync_ExplicitVersionAlreadyInstalled_ShortCircuitsAndSucceeds() + { + // Arrange + using var context = new SimpleTestPathContext(); + var sourceDir = Path.Combine(context.WorkingDirectory, "src"); + var outputDir = Path.Combine(context.WorkingDirectory, "packages"); + Directory.CreateDirectory(sourceDir); + Directory.CreateDirectory(outputDir); + + var id = "Contoso.Utils"; + var v = "3.4.5"; + await SimpleTestPackageUtility.CreateFullPackageAsync(sourceDir, id, v); + + var logger = new Mock(MockBehavior.Loose); + var settings = new Mock(MockBehavior.Loose); + + // First run: install explicit version + var args1 = new PackageDownloadArgs() + { + Packages = [new PackageWithNuGetVersion { Id = id, NuGetVersion = NuGetVersion.Parse(v) }], + LogLevel = LogLevel.Verbose, + OutputDirectory = outputDir, + }; + + var first = await PackageDownloadRunner.RunAsync( + args1, + logger.Object, + [new PackageSource(sourceDir)], + settings.Object, + CancellationToken.None); + first.Should().Be(ExitCodes.Success); + + // Second run: should short-circuit because already installed + var args2 = new PackageDownloadArgs() + { + Packages = [new PackageWithNuGetVersion { Id = id, NuGetVersion = NuGetVersion.Parse(v) }], + OutputDirectory = outputDir, + }; + + // Act + var second = await PackageDownloadRunner.RunAsync( + args2, + logger.Object, + [new PackageSource(sourceDir)], + settings.Object, + CancellationToken.None); + + // Assert + second.Should().Be(PackageDownloadRunner.ExitCodeSuccess); + var installDir = Path.Combine(outputDir, id.ToLowerInvariant(), v); + Directory.Exists(installDir).Should().BeTrue(); + File.Exists(Path.Combine(installDir, $"{id.ToLowerInvariant()}.{v}.nupkg")).Should().BeTrue(); + logger.Verify(l => l.LogMinimal(It.Is(s => s.Contains("Skipping", StringComparison.OrdinalIgnoreCase))), Times.AtLeastOnce); + } + + [Fact] + public async Task RunAsync_WhenAllowInsecureConnectionsFalse_RejectsHttpSource() + { + // Arrange + using var context = new SimpleTestPathContext(); + + var outputDir = Path.Combine(context.WorkingDirectory, "packages"); + Directory.CreateDirectory(outputDir); + + var httpSource = "http://contoso/v3/index.json"; + var logger = new Mock(MockBehavior.Loose); + var settings = new Mock(MockBehavior.Loose); + + var args = new PackageDownloadArgs() + { + Packages = [new PackageWithNuGetVersion { Id = "Contoso.Lib", NuGetVersion = null }], + OutputDirectory = outputDir, + }; + + // Act + var result = await PackageDownloadRunner.RunAsync( + args, + logger.Object, + [new PackageSource(httpSource)], + settings.Object, + CancellationToken.None); + + // Assert + result.Should().Be(PackageDownloadRunner.ExitCodeError); + logger.Verify(l => l.LogError(It.Is(s => s.Contains(httpSource, StringComparison.OrdinalIgnoreCase))), Times.AtLeastOnce); + } + + [Fact] + public async Task RunAsync_PackageDoesNotExist_ReturnsError() + { + // Arrange + using var context = new SimpleTestPathContext(); + var srcA = Path.Combine(context.WorkingDirectory, "emptyA"); + var srcB = Path.Combine(context.WorkingDirectory, "emptyB"); + var outputDir = Path.Combine(context.WorkingDirectory, "packages"); + Directory.CreateDirectory(srcA); + Directory.CreateDirectory(srcB); + Directory.CreateDirectory(outputDir); + + var id = "Missing.Package"; + var v = "9.9.9"; + + var logger = new Mock(MockBehavior.Loose); + var settings = new Mock(MockBehavior.Loose); + + var args = new PackageDownloadArgs() + { + Packages = [new PackageWithNuGetVersion { Id = id, NuGetVersion = NuGetVersion.Parse(v) }], + OutputDirectory = outputDir, + }; + + // Act + var result = await PackageDownloadRunner.RunAsync( + args, + logger.Object, + [new PackageSource(srcA), new PackageSource(srcB)], + settings.Object, + CancellationToken.None); + + // Assert + result.Should().Be(PackageDownloadRunner.ExitCodeError); + logger.Verify(l => l.LogError(It.IsAny()), Times.AtLeastOnce); + + File.Exists(Path.Combine(outputDir, $"{id.ToLowerInvariant()}.{v}.nupkg")) + .Should().BeFalse("Package does not exist in sources"); + } +} diff --git a/test/NuGet.Core.Tests/NuGet.CommandLine.Xplat.Tests/Commands/Package/Download/PackageDownloadRunnerTests.cs b/test/NuGet.Core.Tests/NuGet.CommandLine.Xplat.Tests/Commands/Package/Download/PackageDownloadRunnerTests.cs index f7fe6d031ed..69c49c45a43 100644 --- a/test/NuGet.Core.Tests/NuGet.CommandLine.Xplat.Tests/Commands/Package/Download/PackageDownloadRunnerTests.cs +++ b/test/NuGet.Core.Tests/NuGet.CommandLine.Xplat.Tests/Commands/Package/Download/PackageDownloadRunnerTests.cs @@ -3,17 +3,13 @@ namespace NuGet.CommandLine.Xplat.Tests; -using System; using System.IO; -using System.Linq; using System.Threading; using System.Threading.Tasks; -using FluentAssertions; using Moq; using NuGet.CommandLine.XPlat; using NuGet.CommandLine.XPlat.Commands.Package; using NuGet.CommandLine.XPlat.Commands.Package.PackageDownload; -using NuGet.Common; using NuGet.Configuration; using NuGet.Protocol; using NuGet.Protocol.Core.Types; @@ -23,297 +19,6 @@ namespace NuGet.CommandLine.Xplat.Tests; public class PackageDownloadRunnerTests { - [Fact] - public async Task RunAsync_ExplicitVersionFromLocalFolderSource_SucceedsAsync() - { - // Arrange - using var context = new SimpleTestPathContext(); - var sourceDir = Path.Combine(context.WorkingDirectory, "src"); - var outputDir = Path.Combine(context.WorkingDirectory, "packages"); - Directory.CreateDirectory(sourceDir); - Directory.CreateDirectory(outputDir); - - var id = "Contoso.Lib"; - var version = "1.2.3"; - await SimpleTestPackageUtility.CreateFullPackageAsync(sourceDir, id, version); - - var logger = new Mock(MockBehavior.Loose); - var settings = new Mock(MockBehavior.Loose); - - var args = new PackageDownloadArgs() - { - Packages = [new PackageWithNuGetVersion { Id = id, NuGetVersion = NuGetVersion.Parse(version) }], - OutputDirectory = outputDir, - }; - - - // Act - var result = await PackageDownloadRunner.RunAsync( - args, - logger.Object, - [new(sourceDir)], - settings.Object, - CancellationToken.None); - - // Assert - result.Should().Be(PackageDownloadRunner.ExitCodeSuccess); - var installDir = Path.Combine(outputDir, id.ToLowerInvariant(), version); - Directory.Exists(installDir).Should().BeTrue(); - Directory.EnumerateFiles(installDir, "*.nupkg").Any().Should().BeTrue(); - File.Exists(Path.Combine(installDir, $"{id.ToLowerInvariant()}.{version}.nupkg")).Should().BeTrue(); - } - - [Fact] - public async Task RunAsync_NoVersionWhenPrereleaseNotIncluded_PicksLatestStable() - { - // Arrange - using var context = new SimpleTestPathContext(); - var sourceDir = Path.Combine(context.WorkingDirectory, "src"); - var outputDir = Path.Combine(context.WorkingDirectory, "packages"); - Directory.CreateDirectory(sourceDir); - Directory.CreateDirectory(outputDir); - - var id = "Contoso.Core"; - await SimpleTestPackageUtility.CreateFullPackageAsync(sourceDir, id, "1.0.0"); - await SimpleTestPackageUtility.CreateFullPackageAsync(sourceDir, id, "1.1.0"); - await SimpleTestPackageUtility.CreateFullPackageAsync(sourceDir, id, "2.0.0-beta"); - - var logger = new Mock(MockBehavior.Loose); - var settings = new Mock(MockBehavior.Loose); - - var args = new PackageDownloadArgs() - { - Packages = [new PackageWithNuGetVersion { Id = id, NuGetVersion = null }], - OutputDirectory = outputDir, - }; - - // Act - var result = await PackageDownloadRunner.RunAsync( - args, - logger.Object, - [new PackageSource(sourceDir)], - settings.Object, - CancellationToken.None); - - // Assert - result.Should().Be(PackageDownloadRunner.ExitCodeSuccess); - - var chosen = Path.Combine(outputDir, id.ToLowerInvariant(), "1.1.0"); - var notChosen = Path.Combine(outputDir, id.ToLowerInvariant(), "2.0.0-beta"); - Directory.Exists(chosen).Should().BeTrue("latest stable (1.1.0) should be chosen"); - Directory.Exists(notChosen).Should().BeFalse("prerelease (2.0.0-beta) should not be chosen"); - File.Exists(Path.Combine(chosen, $"{id.ToLowerInvariant()}.1.1.0.nupkg")).Should().BeTrue(); - } - - [Fact] - public async Task RunAsync_NoVersionWithPrereleaseTrue_PicksHighestIncludingPrerelease() - { - // Arrange - using var context = new SimpleTestPathContext(); - var sourceDir = Path.Combine(context.WorkingDirectory, "src"); - var outputDir = Path.Combine(context.WorkingDirectory, "packages"); - Directory.CreateDirectory(sourceDir); - Directory.CreateDirectory(outputDir); - - var id = "Contoso.Preview"; - await SimpleTestPackageUtility.CreateFullPackageAsync(sourceDir, id, "1.3.0"); - await SimpleTestPackageUtility.CreateFullPackageAsync(sourceDir, id, "2.0.0-beta.2"); - - var logger = new Mock(MockBehavior.Loose); - var settings = new Mock(MockBehavior.Loose); - - var args = new PackageDownloadArgs() - { - Packages = [new PackageWithNuGetVersion { Id = id, NuGetVersion = null }], - OutputDirectory = outputDir, - IncludePrerelease = true - }; - - // Act - var result = await PackageDownloadRunner.RunAsync( - args, - logger.Object, - [new PackageSource(sourceDir)], - settings.Object, - CancellationToken.None); - - // Assert - result.Should().Be(PackageDownloadRunner.ExitCodeSuccess); - - var chosen = Path.Combine(outputDir, id.ToLowerInvariant(), "2.0.0-beta.2"); - Directory.Exists(chosen).Should().BeTrue("IncludePrerelease should allow picking 2.0.0-beta.2"); - File.Exists(Path.Combine(chosen, $"{id.ToLowerInvariant()}.2.0.0-beta.2.nupkg")).Should().BeTrue(); - } - - [Fact] - public async Task RunAsync_NoVersion_PicksHighestAcrossMultipleSources() - { - // Arrange - using var context = new SimpleTestPathContext(); - var srcA = Path.Combine(context.WorkingDirectory, "srcA"); - var srcB = Path.Combine(context.WorkingDirectory, "srcB"); - var outputDir = Path.Combine(context.WorkingDirectory, "packages"); - Directory.CreateDirectory(srcA); - Directory.CreateDirectory(srcB); - Directory.CreateDirectory(outputDir); - - var id = "Contoso.Toolkit"; - await SimpleTestPackageUtility.CreateFullPackageAsync(srcA, id, "1.1.0"); - await SimpleTestPackageUtility.CreateFullPackageAsync(srcB, id, "1.2.0"); - - var logger = new Mock(MockBehavior.Loose); - var settings = new Mock(MockBehavior.Loose); - - var args = new PackageDownloadArgs() - { - Packages = [new PackageWithNuGetVersion { Id = id, NuGetVersion = null }], - OutputDirectory = outputDir, - }; - - // Act - var result = await PackageDownloadRunner.RunAsync( - args, - logger.Object, - [new PackageSource(srcA), new PackageSource(srcB)], - settings.Object, - CancellationToken.None); - - // Assert - result.Should().Be(PackageDownloadRunner.ExitCodeSuccess); - - var chosen = Path.Combine(outputDir, id.ToLowerInvariant(), "1.2.0"); - Directory.Exists(chosen).Should().BeTrue("should choose the highest version found across all sources"); - File.Exists(Path.Combine(chosen, $"{id.ToLowerInvariant()}.1.2.0.nupkg")).Should().BeTrue(); - } - - [Fact] - public async Task RunAsync_ExplicitVersionAlreadyInstalled_ShortCircuitsAndSucceeds() - { - // Arrange - using var context = new SimpleTestPathContext(); - var sourceDir = Path.Combine(context.WorkingDirectory, "src"); - var outputDir = Path.Combine(context.WorkingDirectory, "packages"); - Directory.CreateDirectory(sourceDir); - Directory.CreateDirectory(outputDir); - - var id = "Contoso.Utils"; - var v = "3.4.5"; - await SimpleTestPackageUtility.CreateFullPackageAsync(sourceDir, id, v); - - var logger = new Mock(MockBehavior.Loose); - var settings = new Mock(MockBehavior.Loose); - - // First run: install explicit version - var args1 = new PackageDownloadArgs() - { - Packages = [new PackageWithNuGetVersion { Id = id, NuGetVersion = NuGetVersion.Parse(v) }], - LogLevel = LogLevel.Verbose, - OutputDirectory = outputDir, - }; - - var first = await PackageDownloadRunner.RunAsync( - args1, - logger.Object, - [new PackageSource(sourceDir)], - settings.Object, - CancellationToken.None); - first.Should().Be(ExitCodes.Success); - - // Second run: should short-circuit because already installed - var args2 = new PackageDownloadArgs() - { - Packages = [new PackageWithNuGetVersion { Id = id, NuGetVersion = NuGetVersion.Parse(v) }], - OutputDirectory = outputDir, - }; - - // Act - var second = await PackageDownloadRunner.RunAsync( - args2, - logger.Object, - [new PackageSource(sourceDir)], - settings.Object, - CancellationToken.None); - - // Assert - second.Should().Be(PackageDownloadRunner.ExitCodeSuccess); - var installDir = Path.Combine(outputDir, id.ToLowerInvariant(), v); - Directory.Exists(installDir).Should().BeTrue(); - File.Exists(Path.Combine(installDir, $"{id.ToLowerInvariant()}.{v}.nupkg")).Should().BeTrue(); - logger.Verify(l => l.LogMinimal(It.Is(s => s.Contains("Skipping", StringComparison.OrdinalIgnoreCase))), Times.AtLeastOnce); - } - - [Fact] - public async Task RunAsync_WhenAllowInsecureConnectionsFalse_RejectsHttpSource() - { - // Arrange - using var context = new SimpleTestPathContext(); - - var outputDir = Path.Combine(context.WorkingDirectory, "packages"); - Directory.CreateDirectory(outputDir); - - var httpSource = "http://contoso/v3/index.json"; - var logger = new Mock(MockBehavior.Loose); - var settings = new Mock(MockBehavior.Loose); - - var args = new PackageDownloadArgs() - { - Packages = [new PackageWithNuGetVersion { Id = "Contoso.Lib", NuGetVersion = null }], - OutputDirectory = outputDir, - }; - - // Act - var result = await PackageDownloadRunner.RunAsync( - args, - logger.Object, - [new PackageSource(httpSource)], - settings.Object, - CancellationToken.None); - - // Assert - result.Should().Be(PackageDownloadRunner.ExitCodeError); - logger.Verify(l => l.LogError(It.Is(s => s.Contains(httpSource, StringComparison.OrdinalIgnoreCase))), Times.AtLeastOnce); - } - - [Fact] - public async Task RunAsync_PackageDoesNotExist_ReturnsError() - { - // Arrange - using var context = new SimpleTestPathContext(); - var srcA = Path.Combine(context.WorkingDirectory, "emptyA"); - var srcB = Path.Combine(context.WorkingDirectory, "emptyB"); - var outputDir = Path.Combine(context.WorkingDirectory, "packages"); - Directory.CreateDirectory(srcA); - Directory.CreateDirectory(srcB); - Directory.CreateDirectory(outputDir); - - var id = "Missing.Package"; - var v = "9.9.9"; - - var logger = new Mock(MockBehavior.Loose); - var settings = new Mock(MockBehavior.Loose); - - var args = new PackageDownloadArgs() - { - Packages = [new PackageWithNuGetVersion { Id = id, NuGetVersion = NuGetVersion.Parse(v) }], - OutputDirectory = outputDir, - }; - - // Act - var result = await PackageDownloadRunner.RunAsync( - args, - logger.Object, - [new PackageSource(srcA), new PackageSource(srcB)], - settings.Object, - CancellationToken.None); - - // Assert - result.Should().Be(PackageDownloadRunner.ExitCodeError); - logger.Verify(l => l.LogError(It.IsAny()), Times.AtLeastOnce); - - File.Exists(Path.Combine(outputDir, $"{id.ToLowerInvariant()}.{v}.nupkg")) - .Should().BeFalse("Package does not exist in sources"); - } - [Fact] public async Task ResolvePackageDownloadVersion_ExactVersionAtFirstSource_ReturnsEarly() { From 8472caf9c956389a34ceb8b0689ff6dd1a577664 Mon Sep 17 00:00:00 2001 From: Nigusu Yenework Date: Tue, 21 Oct 2025 17:27:14 -0700 Subject: [PATCH 08/15] Simplify --- .../Package/Download/PackageDownloadRunner.cs | 91 ++---- .../Download/PackageDownloadRunnerTests.cs | 199 ++++++------- .../Download/PackageDownloadCommandTests.cs | 2 +- .../Download/PackageDownloadRunnerTests.cs | 271 +++++++----------- 4 files changed, 215 insertions(+), 348 deletions(-) diff --git a/src/NuGet.Core/NuGet.CommandLine.XPlat/Commands/Package/Download/PackageDownloadRunner.cs b/src/NuGet.Core/NuGet.CommandLine.XPlat/Commands/Package/Download/PackageDownloadRunner.cs index 09806b2b816..be237f7bb51 100644 --- a/src/NuGet.Core/NuGet.CommandLine.XPlat/Commands/Package/Download/PackageDownloadRunner.cs +++ b/src/NuGet.Core/NuGet.CommandLine.XPlat/Commands/Package/Download/PackageDownloadRunner.cs @@ -41,12 +41,12 @@ public static async Task RunAsync(PackageDownloadArgs args, CancellationTok Directory.GetCurrentDirectory(), args.ConfigFile, new XPlatMachineWideSetting()); - IEnumerable packageSources = GetPackageSources(args.Sources, new PackageSourceProvider(settings)); + IReadOnlyList packageSources = GetPackageSources(args.Sources, new PackageSourceProvider(settings)); return await RunAsync(args, logger, packageSources, settings, token); } - public static async Task RunAsync(PackageDownloadArgs args, ILoggerWithColor logger, IEnumerable packageSources, ISettings settings, CancellationToken token) + public static async Task RunAsync(PackageDownloadArgs args, ILoggerWithColor logger, IReadOnlyList packageSources, ISettings settings, CancellationToken token) { // Check for insecure sources if (DetectAndReportInsecureSources(args.AllowInsecureConnections, packageSources, logger)) @@ -56,7 +56,7 @@ public static async Task RunAsync(PackageDownloadArgs args, ILoggerWithColo string outputDirectory = args.OutputDirectory ?? Directory.GetCurrentDirectory(); var cache = new SourceCacheContext(); - IEnumerable sourceRepositories = GetSourceRepositories(packageSources); + IReadOnlyList sourceRepositories = GetSourceRepositories(packageSources); bool downloadedAllSuccessfully = true; foreach (var package in args.Packages) @@ -85,7 +85,7 @@ await ResolvePackageDownloadVersion( continue; } - bool download = await DownloadPackageAsync( + bool success = await DownloadPackageAsync( package.Id, version, downloadRepository, @@ -95,7 +95,7 @@ await ResolvePackageDownloadVersion( logger, token); - if (download) + if (success) { logger.LogMinimal(string.Format( CultureInfo.CurrentCulture, @@ -128,7 +128,7 @@ await ResolvePackageDownloadVersion( } internal static async Task<(NuGetVersion, SourceRepository)> ResolvePackageDownloadVersion( - PackageWithNuGetVersion package, + PackageWithNuGetVersion packageWithNuGetVersion, IEnumerable sourceRepositories, SourceCacheContext cache, ILoggerWithColor logger, @@ -137,13 +137,13 @@ await ResolvePackageDownloadVersion( { NuGetVersion versionToDownload = null; SourceRepository downloadSourceRepository = null; - bool versionSpecified = package.NuGetVersion != null; + bool versionSpecified = packageWithNuGetVersion.NuGetVersion != null; foreach (var repo in sourceRepositories) { var finder = await repo.GetResourceAsync(token); var packages = await finder.GetMetadataAsync( - package.Id, + packageWithNuGetVersion.Id, includePrerelease, includeUnlisted: false, sourceCacheContext: cache, @@ -158,23 +158,23 @@ await ResolvePackageDownloadVersion( if (versionSpecified) { // If an exact version is specified, check if it exists at this source - foreach (var p in packages) + foreach (var package in packages) { - if (p?.Identity?.Version == package.NuGetVersion) + if (package?.Identity?.Version == packageWithNuGetVersion.NuGetVersion) { - return (package.NuGetVersion, repo); + return (packageWithNuGetVersion.NuGetVersion, repo); } } continue; } - foreach (var p in packages) + foreach (var package in packages) { - var v = p.Identity.Version; - if (versionToDownload == null || v > versionToDownload) + var version = package.Identity.Version; + if (versionToDownload == null || version > versionToDownload) { - versionToDownload = v; + versionToDownload = version; downloadSourceRepository = repo; } } @@ -220,67 +220,36 @@ private static async Task DownloadPackageAsync( return true; } - try + var packageIdentity = new PackageIdentity(id, version); + var provider = new SourceRepositoryDependencyProvider(sourceRepository: repo, logger: logger, cacheContext: cache, ignoreFailedSources: false, ignoreWarning: false); + using var downloader = await provider.GetPackageDownloaderAsync(packageIdentity, cache, logger, token); + bool success = await PackageExtractor.InstallFromSourceAsync(packageIdentity, downloader, resolver, extractionContext, token); + if (!success) { - var finder = await repo.GetResourceAsync(token); - bool installed = await PackageExtractor.InstallFromSourceAsync( - repo.PackageSource.Source, - new PackageIdentity(id, version), - async (destination) => - { - using var nupkg = new MemoryStream(); - bool ok = await finder.CopyNupkgToStreamAsync( - id, version, nupkg, cache, logger, token); - - if (!ok) throw new InvalidOperationException("Package not found."); - - nupkg.Position = 0; - await nupkg.CopyToAsync(destination, 81920); - }, - resolver, - extractionContext, - token); - - if (installed) - { - return true; - } - } - catch (InvalidOperationException) - { - // Unable to download the package logger.LogError(string.Format( - CultureInfo.CurrentCulture, - Strings.PackageDownloadCommand_UnableToDownload, - id, - version.ToNormalizedString(), - repo.PackageSource.Source)); + CultureInfo.CurrentCulture, + Strings.PackageDownloadCommand_UnableToDownload, + id, + version.ToNormalizedString(), + repo.PackageSource.Source)); return false; } - return false; + return success; } - private static IEnumerable GetPackageSources(IList sources, IPackageSourceProvider sourceProvider) + private static IReadOnlyList GetPackageSources(IList sources, IPackageSourceProvider sourceProvider) { IEnumerable configuredSources = sourceProvider.LoadPackageSources() .Where(s => s.IsEnabled); - IEnumerable packageSources; - if (sources != null && sources.Count > 0) { // Use sources specified on command line - packageSources = sources - .Select(s => PackageSourceProviderExtensions.ResolveSource(configuredSources, s)); - } - else - { - // Use all configured sources - packageSources = configuredSources; + return [.. sources.Select(s => PackageSourceProviderExtensions.ResolveSource(configuredSources, s))]; } - return packageSources; + return [.. configuredSources]; } private static bool DetectAndReportInsecureSources( @@ -301,7 +270,7 @@ private static bool DetectAndReportInsecureSources( return false; } - private static IEnumerable GetSourceRepositories(IEnumerable packageSources) + private static IReadOnlyList GetSourceRepositories(IReadOnlyList packageSources) { IEnumerable> providers = Repository.Provider.GetCoreV3(); List sourceRepositories = []; diff --git a/test/NuGet.Core.FuncTests/NuGet.XPlat.FuncTest/Package/Download/PackageDownloadRunnerTests.cs b/test/NuGet.Core.FuncTests/NuGet.XPlat.FuncTest/Package/Download/PackageDownloadRunnerTests.cs index ff08420d2b1..1f31c0d4e39 100644 --- a/test/NuGet.Core.FuncTests/NuGet.XPlat.FuncTest/Package/Download/PackageDownloadRunnerTests.cs +++ b/test/NuGet.Core.FuncTests/NuGet.XPlat.FuncTest/Package/Download/PackageDownloadRunnerTests.cs @@ -4,6 +4,7 @@ namespace NuGet.CommandLine.Xplat.Tests; using System; +using System.Collections.Generic; using System.IO; using System.Linq; using System.Threading; @@ -21,139 +22,122 @@ namespace NuGet.CommandLine.Xplat.Tests; public class PackageDownloadRunnerTests { - [Fact] - public async Task RunAsync_ExplicitVersionFromLocalFolderSource_SucceedsAsync() + public static IEnumerable PackageTestData() { - // Arrange - using var context = new SimpleTestPathContext(); - var sourceDir = Path.Combine(context.WorkingDirectory, "src"); - var outputDir = Path.Combine(context.WorkingDirectory, "packages"); - Directory.CreateDirectory(sourceDir); - Directory.CreateDirectory(outputDir); - - var id = "Contoso.Lib"; - var version = "1.2.3"; - await SimpleTestPackageUtility.CreateFullPackageAsync(sourceDir, id, version); - - var logger = new Mock(MockBehavior.Loose); - var settings = new Mock(MockBehavior.Loose); - - var args = new PackageDownloadArgs() + // Basic stable explicit version + yield return new object[] { - Packages = [new PackageWithNuGetVersion { Id = id, NuGetVersion = NuGetVersion.Parse(version) }], - OutputDirectory = outputDir, + new List<(string, string)> + { + ("Contoso.Core", "1.0.0"), + ("Contoso.Core", "1.1.0"), + ("Contoso.Core", "2.0.0-beta") + }, + "Contoso.Core", // downloadId argument + "1.1.0", // downloadVersion argument + false, // enablePrerelease + ("Contoso.Core", "1.1.0") // expected }; - - // Act - var result = await PackageDownloadRunner.RunAsync( - args, - logger.Object, - [new(sourceDir)], - settings.Object, - CancellationToken.None); - - // Assert - result.Should().Be(PackageDownloadRunner.ExitCodeSuccess); - var installDir = Path.Combine(outputDir, id.ToLowerInvariant(), version); - Directory.Exists(installDir).Should().BeTrue(); - Directory.EnumerateFiles(installDir, "*.nupkg").Any().Should().BeTrue(); - File.Exists(Path.Combine(installDir, $"{id.ToLowerInvariant()}.{version}.nupkg")).Should().BeTrue(); - } - - [Fact] - public async Task RunAsync_NoVersionWhenPrereleaseNotIncluded_PicksLatestStable() - { - // Arrange - using var context = new SimpleTestPathContext(); - var sourceDir = Path.Combine(context.WorkingDirectory, "src"); - var outputDir = Path.Combine(context.WorkingDirectory, "packages"); - Directory.CreateDirectory(sourceDir); - Directory.CreateDirectory(outputDir); - - var id = "Contoso.Core"; - await SimpleTestPackageUtility.CreateFullPackageAsync(sourceDir, id, "1.0.0"); - await SimpleTestPackageUtility.CreateFullPackageAsync(sourceDir, id, "1.1.0"); - await SimpleTestPackageUtility.CreateFullPackageAsync(sourceDir, id, "2.0.0-beta"); - - var logger = new Mock(MockBehavior.Loose); - var settings = new Mock(MockBehavior.Loose); - - var args = new PackageDownloadArgs() + // Mixed casing on the ID in the *download* argument + yield return new object[] { - Packages = [new PackageWithNuGetVersion { Id = id, NuGetVersion = null }], - OutputDirectory = outputDir, + new List<(string, string)> + { + ("Contoso.Core", "1.1.0") + }, + "contoso.core", // downloadId argument + "1.1.0", // downloadVersion argument + false, // enablePrerelease + ("Contoso.Core", "1.1.0") // expected }; - // Act - var result = await PackageDownloadRunner.RunAsync( - args, - logger.Object, - [new PackageSource(sourceDir)], - settings.Object, - CancellationToken.None); - - // Assert - result.Should().Be(PackageDownloadRunner.ExitCodeSuccess); + // prerelease with IncludePrerelease == true + yield return new object[] + { + new List<(string, string)> + { + ("Contoso.Preview", "1.3.0"), + ("Contoso.Preview", "2.0.0-beta.2") + }, + "Contoso.Preview", // downloadId argument + null, // downloadVersion argument + true, // enablePrerelease + ("Contoso.Preview", "2.0.0-beta.2") // expected + }; - var chosen = Path.Combine(outputDir, id.ToLowerInvariant(), "1.1.0"); - var notChosen = Path.Combine(outputDir, id.ToLowerInvariant(), "2.0.0-beta"); - Directory.Exists(chosen).Should().BeTrue("latest stable (1.1.0) should be chosen"); - Directory.Exists(notChosen).Should().BeFalse("prerelease (2.0.0-beta) should not be chosen"); - File.Exists(Path.Combine(chosen, $"{id.ToLowerInvariant()}.1.1.0.nupkg")).Should().BeTrue(); + // chose stable with IncludePrerelease == false + yield return new object[] + { + new List<(string, string)> + { + ("Contoso.Preview", "1.3.2"), + ("Contoso.Preview", "1.3.0"), + ("Contoso.Preview", "2.0.0-beta.2") + }, + "Contoso.Preview", // downloadId argument + null, // downloadVersion argument + false, // enablePrerelease + ("Contoso.Preview", "1.3.2") // expected + }; } - [Fact] - public async Task RunAsync_NoVersionWithPrereleaseTrue_PicksHighestIncludingPrerelease() + + [Theory] + [MemberData(nameof(PackageTestData))] + public async Task RunAsync_ExplicitVersionFromLocalFolderSource_SucceedsAsync( + IReadOnlyList<(string, string)> sourcePackages, + string downloadId, + string downloadVersion, + bool enablePrerelease, + (string, string) expectedPackage) { // Arrange using var context = new SimpleTestPathContext(); - var sourceDir = Path.Combine(context.WorkingDirectory, "src"); - var outputDir = Path.Combine(context.WorkingDirectory, "packages"); - Directory.CreateDirectory(sourceDir); - Directory.CreateDirectory(outputDir); + var sourceDir = context.PackageSource; + var outputDir = context.WorkingDirectory; - var id = "Contoso.Preview"; - await SimpleTestPackageUtility.CreateFullPackageAsync(sourceDir, id, "1.3.0"); - await SimpleTestPackageUtility.CreateFullPackageAsync(sourceDir, id, "2.0.0-beta.2"); + foreach (var (id, version) in sourcePackages) + { + await SimpleTestPackageUtility.CreateFullPackageAsync(sourceDir, id, version); + } var logger = new Mock(MockBehavior.Loose); var settings = new Mock(MockBehavior.Loose); var args = new PackageDownloadArgs() { - Packages = [new PackageWithNuGetVersion { Id = id, NuGetVersion = null }], + Packages = [new PackageWithNuGetVersion { Id = downloadId, NuGetVersion = downloadVersion == null ? null : NuGetVersion.Parse(downloadVersion) }], OutputDirectory = outputDir, - IncludePrerelease = true + IncludePrerelease = enablePrerelease }; + // Act var result = await PackageDownloadRunner.RunAsync( args, logger.Object, - [new PackageSource(sourceDir)], + [new(sourceDir)], settings.Object, CancellationToken.None); // Assert result.Should().Be(PackageDownloadRunner.ExitCodeSuccess); - - var chosen = Path.Combine(outputDir, id.ToLowerInvariant(), "2.0.0-beta.2"); - Directory.Exists(chosen).Should().BeTrue("IncludePrerelease should allow picking 2.0.0-beta.2"); - File.Exists(Path.Combine(chosen, $"{id.ToLowerInvariant()}.2.0.0-beta.2.nupkg")).Should().BeTrue(); + var installDir = Path.Combine(outputDir, expectedPackage.Item1.ToLowerInvariant(), expectedPackage.Item2); + Directory.Exists(installDir).Should().BeTrue(); + Directory.EnumerateFiles(installDir, "*.nupkg").Any().Should().BeTrue(); + File.Exists(Path.Combine(installDir, $"{expectedPackage.Item1.ToLowerInvariant()}.{expectedPackage.Item2}.nupkg")).Should().BeTrue(); } [Fact] public async Task RunAsync_NoVersion_PicksHighestAcrossMultipleSources() { // Arrange - using var context = new SimpleTestPathContext(); - var srcA = Path.Combine(context.WorkingDirectory, "srcA"); - var srcB = Path.Combine(context.WorkingDirectory, "srcB"); - var outputDir = Path.Combine(context.WorkingDirectory, "packages"); - Directory.CreateDirectory(srcA); - Directory.CreateDirectory(srcB); - Directory.CreateDirectory(outputDir); + using var contextA = new SimpleTestPathContext(); + using var contextB = new SimpleTestPathContext(); + var srcA = contextA.PackageSource; + var srcB = contextB.PackageSource; + var outputDir = contextA.WorkingDirectory; var id = "Contoso.Toolkit"; await SimpleTestPackageUtility.CreateFullPackageAsync(srcA, id, "1.1.0"); @@ -189,10 +173,8 @@ public async Task RunAsync_ExplicitVersionAlreadyInstalled_ShortCircuitsAndSucce { // Arrange using var context = new SimpleTestPathContext(); - var sourceDir = Path.Combine(context.WorkingDirectory, "src"); - var outputDir = Path.Combine(context.WorkingDirectory, "packages"); - Directory.CreateDirectory(sourceDir); - Directory.CreateDirectory(outputDir); + var sourceDir = context.PackageSource; + var outputDir = context.WorkingDirectory; var id = "Contoso.Utils"; var v = "3.4.5"; @@ -245,10 +227,6 @@ public async Task RunAsync_WhenAllowInsecureConnectionsFalse_RejectsHttpSource() { // Arrange using var context = new SimpleTestPathContext(); - - var outputDir = Path.Combine(context.WorkingDirectory, "packages"); - Directory.CreateDirectory(outputDir); - var httpSource = "http://contoso/v3/index.json"; var logger = new Mock(MockBehavior.Loose); var settings = new Mock(MockBehavior.Loose); @@ -256,7 +234,6 @@ public async Task RunAsync_WhenAllowInsecureConnectionsFalse_RejectsHttpSource() var args = new PackageDownloadArgs() { Packages = [new PackageWithNuGetVersion { Id = "Contoso.Lib", NuGetVersion = null }], - OutputDirectory = outputDir, }; // Act @@ -277,30 +254,22 @@ public async Task RunAsync_PackageDoesNotExist_ReturnsError() { // Arrange using var context = new SimpleTestPathContext(); - var srcA = Path.Combine(context.WorkingDirectory, "emptyA"); - var srcB = Path.Combine(context.WorkingDirectory, "emptyB"); - var outputDir = Path.Combine(context.WorkingDirectory, "packages"); - Directory.CreateDirectory(srcA); - Directory.CreateDirectory(srcB); - Directory.CreateDirectory(outputDir); - var id = "Missing.Package"; var v = "9.9.9"; - var logger = new Mock(MockBehavior.Loose); var settings = new Mock(MockBehavior.Loose); var args = new PackageDownloadArgs() { Packages = [new PackageWithNuGetVersion { Id = id, NuGetVersion = NuGetVersion.Parse(v) }], - OutputDirectory = outputDir, + OutputDirectory = context.WorkingDirectory, }; // Act var result = await PackageDownloadRunner.RunAsync( args, logger.Object, - [new PackageSource(srcA), new PackageSource(srcB)], + [new PackageSource(context.PackageSource)], settings.Object, CancellationToken.None); @@ -308,7 +277,7 @@ public async Task RunAsync_PackageDoesNotExist_ReturnsError() result.Should().Be(PackageDownloadRunner.ExitCodeError); logger.Verify(l => l.LogError(It.IsAny()), Times.AtLeastOnce); - File.Exists(Path.Combine(outputDir, $"{id.ToLowerInvariant()}.{v}.nupkg")) + File.Exists(Path.Combine(context.WorkingDirectory, $"{id.ToLowerInvariant()}.{v}.nupkg")) .Should().BeFalse("Package does not exist in sources"); } } diff --git a/test/NuGet.Core.Tests/NuGet.CommandLine.Xplat.Tests/Commands/Package/Download/PackageDownloadCommandTests.cs b/test/NuGet.Core.Tests/NuGet.CommandLine.Xplat.Tests/Commands/Package/Download/PackageDownloadCommandTests.cs index 3d00cc5d9a8..02075ea0132 100644 --- a/test/NuGet.Core.Tests/NuGet.CommandLine.Xplat.Tests/Commands/Package/Download/PackageDownloadCommandTests.cs +++ b/test/NuGet.Core.Tests/NuGet.CommandLine.Xplat.Tests/Commands/Package/Download/PackageDownloadCommandTests.cs @@ -14,7 +14,7 @@ namespace NuGet.CommandLine.Xplat.Tests.Commands.Package.Download { - public class PackageDownloadCommandTests + public class PackageDownloadCommandArgsTest { [Fact] public async Task NoArguments_ThrowsAnExceptionForMissingArg() diff --git a/test/NuGet.Core.Tests/NuGet.CommandLine.Xplat.Tests/Commands/Package/Download/PackageDownloadRunnerTests.cs b/test/NuGet.Core.Tests/NuGet.CommandLine.Xplat.Tests/Commands/Package/Download/PackageDownloadRunnerTests.cs index 69c49c45a43..0b365d26674 100644 --- a/test/NuGet.Core.Tests/NuGet.CommandLine.Xplat.Tests/Commands/Package/Download/PackageDownloadRunnerTests.cs +++ b/test/NuGet.Core.Tests/NuGet.CommandLine.Xplat.Tests/Commands/Package/Download/PackageDownloadRunnerTests.cs @@ -3,6 +3,7 @@ namespace NuGet.CommandLine.Xplat.Tests; +using System.Collections.Generic; using System.IO; using System.Threading; using System.Threading.Tasks; @@ -19,198 +20,126 @@ namespace NuGet.CommandLine.Xplat.Tests; public class PackageDownloadRunnerTests { - [Fact] - public async Task ResolvePackageDownloadVersion_ExactVersionAtFirstSource_ReturnsEarly() - { - // Arrange - using var context = new SimpleTestPathContext(); - var v123 = new NuGetVersion("1.2.3"); - var package = new PackageWithNuGetVersion { Id = "Contoso", NuGetVersion = NuGetVersion.Parse(v123.OriginalVersion) }; - var sourceDir = Path.Combine(context.WorkingDirectory, "src"); - Directory.CreateDirectory(sourceDir); - await SimpleTestPackageUtility.CreateFullPackageAsync(sourceDir, package.Id, package.NuGetVersion.ToNormalizedString()); - var sources = new[] { Repository.Factory.GetCoreV3(new PackageSource(sourceDir)) }; - - var logger = new Mock(MockBehavior.Loose); - - // Act - (NuGetVersion resolved, SourceRepository resolvedRepo) = await PackageDownloadRunner.ResolvePackageDownloadVersion( - package, - sources, - new SourceCacheContext(), - logger.Object, - includePrerelease: false, - CancellationToken.None); - - // Assert - Assert.Equal(v123, resolved); - Assert.Equal(sourceDir, resolvedRepo.PackageSource.Source); - } - - [Fact] - public async Task ResolvePackageDownloadVersion_ExactVersionMissingInFirstPresentInSecond_ReturnsSecondSource() - { - // Arrange - using var context = new SimpleTestPathContext(); - var v123 = new NuGetVersion("1.2.3"); - var v100 = new NuGetVersion("1.0.0"); - var package = new PackageWithNuGetVersion { Id = "Contoso", NuGetVersion = NuGetVersion.Parse(v123.OriginalVersion) }; - - var sourceA = Path.Combine(context.WorkingDirectory, "srcA"); - var sourceB = Path.Combine(context.WorkingDirectory, "srcB"); - Directory.CreateDirectory(sourceA); - Directory.CreateDirectory(sourceB); - - await SimpleTestPackageUtility.CreateFullPackageAsync(sourceA, package.Id, v100.OriginalVersion); - await SimpleTestPackageUtility.CreateFullPackageAsync(sourceB, package.Id, v123.OriginalVersion); - - var sources = new[] { Repository.Factory.GetCoreV3(new PackageSource(sourceA)), Repository.Factory.GetCoreV3(new PackageSource(sourceB)) }; - var logger = new Mock(MockBehavior.Loose); - - // Act - (NuGetVersion resolved, SourceRepository resolvedRepo) = await PackageDownloadRunner.ResolvePackageDownloadVersion( - package, sources, new SourceCacheContext(), logger.Object, includePrerelease: false, CancellationToken.None); - - // Assert - Assert.Equal(v123, resolved); - Assert.Equal(sourceB, resolvedRepo.PackageSource.Source); - } - - [Fact] - public async Task ResolvePackageDownloadVersion_NoVersionSpecifiedExcludePrerelease_PicksHighestStableAcrossSources() - { - // Arrange - using var context = new SimpleTestPathContext(); - var package = new PackageWithNuGetVersion { Id = "Contoso", NuGetVersion = null }; - var sourceA = Path.Combine(context.WorkingDirectory, "srcA"); - var sourceB = Path.Combine(context.WorkingDirectory, "srcB"); - Directory.CreateDirectory(sourceA); - Directory.CreateDirectory(sourceB); - - await SimpleTestPackageUtility.CreateFullPackageAsync(sourceA, package.Id, "1.0.0"); - await SimpleTestPackageUtility.CreateFullPackageAsync(sourceA, package.Id, "1.5.0-alpha"); // prerelease; should be ignored - await SimpleTestPackageUtility.CreateFullPackageAsync(sourceB, package.Id, "2.0.0"); // highest stable - - var sources = new[] { Repository.Factory.GetCoreV3(new PackageSource(sourceA)), Repository.Factory.GetCoreV3(new PackageSource(sourceB)) }; - var logger = new Mock(MockBehavior.Loose); - - // Act - (NuGetVersion resolved, SourceRepository resolvedRepo) = await PackageDownloadRunner.ResolvePackageDownloadVersion( - package, sources, new SourceCacheContext(), logger.Object, includePrerelease: false, CancellationToken.None); - - // Assert - Assert.Equal(new NuGetVersion("2.0.0"), resolved); - Assert.Equal(sourceB, resolvedRepo.PackageSource.Source); - } - - [Fact] - public async Task ResolvePackageDownloadVersion_NoVersionSpecifiedIncludePrerelease_PicksHighestIncludingPrerelease() - { - // Arrange - using var context = new SimpleTestPathContext(); - var package = new PackageWithNuGetVersion { Id = "Contoso", NuGetVersion = null }; // pickLatest - var sourceA = Path.Combine(context.WorkingDirectory, "srcA"); - var sourceB = Path.Combine(context.WorkingDirectory, "srcB"); - Directory.CreateDirectory(sourceA); - Directory.CreateDirectory(sourceB); - - await SimpleTestPackageUtility.CreateFullPackageAsync(sourceA, package.Id, "2.0.0"); // stable - await SimpleTestPackageUtility.CreateFullPackageAsync(sourceB, package.Id, "2.1.0-rc.1"); // higher (prerelease) - - var sources = new[] { Repository.Factory.GetCoreV3(new PackageSource(sourceA)), Repository.Factory.GetCoreV3(new PackageSource(sourceB)) }; - var logger = new Mock(MockBehavior.Loose); - - // Act - (NuGetVersion resolved, SourceRepository resolvedRepo) = await PackageDownloadRunner.ResolvePackageDownloadVersion( - package, sources, new SourceCacheContext(), logger.Object, includePrerelease: true, CancellationToken.None); + public sealed record SourceSpec(string Name, params string[] Versions); - // Assert - Assert.Equal(new NuGetVersion("2.1.0-rc.1"), resolved); - Assert.Equal(sourceB, resolvedRepo.PackageSource.Source); - } + public sealed record ResolveScenario( + string Id, + IReadOnlyList Sources, + string RequestedVersion, + bool IncludePrerelease, + string ExpectedSource, // null => expect no resolution + string ExpectedVersion); // null => expect no resolution - [Fact] - public async Task ResolvePackageDownloadVersion_NoMatchesAnywhere_ReturnsNull() + public sealed class ResolveVersionData : TheoryData { - using var context = new SimpleTestPathContext(); - - // Arrange - var package = new PackageWithNuGetVersion { Id = "Contoso", NuGetVersion = null }; // pickLatest - var sourceA = Path.Combine(context.WorkingDirectory, "emptyA"); - var sourceB = Path.Combine(context.WorkingDirectory, "emptyB"); - Directory.CreateDirectory(sourceA); - Directory.CreateDirectory(sourceB); - var sources = new[] { Repository.Factory.GetCoreV3(new PackageSource(sourceA)), Repository.Factory.GetCoreV3(new PackageSource(sourceB)) }; - - var logger = new Mock(MockBehavior.Loose); - logger.Setup(l => l.LogError(It.IsAny())); - - // Act - (NuGetVersion resolved, SourceRepository resolvedRepo) = await PackageDownloadRunner.ResolvePackageDownloadVersion( - package, sources, new SourceCacheContext(), logger.Object, includePrerelease: false, CancellationToken.None); - - // Assert - Assert.Null(resolved); - Assert.Null(resolvedRepo); + public ResolveVersionData() + { + // Exact version at first source -> returns early + Add(new ResolveScenario( + Id: "Contoso", + Sources: [new SourceSpec("src", "1.2.3")], + RequestedVersion: "1.2.3", + IncludePrerelease: false, + ExpectedSource: "src", + ExpectedVersion: "1.2.3")); + + // Exact version missing in first, present in second -> pick second + Add(new ResolveScenario( + "Contoso", + [new SourceSpec("srcA", "1.0.0"), new SourceSpec("srcB", "1.2.3")], + "1.2.3", + false, + "srcB", + "1.2.3")); + + // No version; exclude prerelease -> highest stable across sources + Add(new ResolveScenario( + "Contoso", + [new SourceSpec("srcA", "1.0.0", "1.5.0-alpha"), new SourceSpec("srcB", "2.0.0")], + null, + false, + "srcB", + "2.0.0")); + + // No version; include prerelease -> highest including prerelease + Add(new ResolveScenario( + "Contoso", + [new SourceSpec("srcA", "2.0.0"), new SourceSpec("srcB", "2.1.0-rc.1")], + null, + true, + "srcB", + "2.1.0-rc.1")); + + // No matches anywhere -> null + Add(new ResolveScenario( + "Contoso", + [new SourceSpec("emptyA"), new SourceSpec("emptyB")], + null, + false, + ExpectedSource: null, + ExpectedVersion: null)); + + // Exact version requested, but not found anywhere -> null + Add(new ResolveScenario( + "Contoso", + [new SourceSpec("src", "1.0.0")], + "1.2.3", + false, + null, + null)); + } } - [Fact] - public async Task ResolvePackageDownloadVersion_ExactVersionNotFoundAnywhere_ReturnsNull() + [Theory] + [ClassData(typeof(ResolveVersionData))] + public async Task ResolvePackageDownloadVersion_Scenarios(ResolveScenario scenario) { // Arrange using var context = new SimpleTestPathContext(); - var v123 = new NuGetVersion("1.2.3"); - var package = new PackageWithNuGetVersion { Id = "Contoso", NuGetVersion = NuGetVersion.Parse(v123.OriginalVersion) }; - var sourceDir = Path.Combine(context.WorkingDirectory, "src"); - Directory.CreateDirectory(sourceDir); - - await SimpleTestPackageUtility.CreateFullPackageAsync(sourceDir, package.Id, "1.0.0"); - - var sources = new[] { Repository.Factory.GetCoreV3(new PackageSource(sourceDir)) }; - var logger = new Mock(MockBehavior.Loose); - logger.Setup(l => l.LogError(It.IsAny())); - - // Act - (NuGetVersion resolved, SourceRepository resolvedRepo) = await PackageDownloadRunner.ResolvePackageDownloadVersion( - package, - sources, - new SourceCacheContext(), - logger.Object, - includePrerelease: false, - CancellationToken.None); - - // Assert - Assert.Null(resolved); - Assert.Null(resolvedRepo); - } - [Fact] - public async Task ResolvePackageDownloadVersion_OnlyPrereleaseExistsAndExcludePrerelease_ReturnsNull() - { - using var context = new SimpleTestPathContext(); + // Create source folders and packages + var repos = new List(); + foreach (var src in scenario.Sources) + { + var folder = Path.Combine(context.WorkingDirectory, src.Name); + Directory.CreateDirectory(folder); - // Arrange - var package = new PackageWithNuGetVersion { Id = "Contoso", NuGetVersion = null }; // pickLatest = true - var sourceDir = Path.Combine(context.WorkingDirectory, "src"); - Directory.CreateDirectory(sourceDir); + foreach (var version in src.Versions) + { + await SimpleTestPackageUtility.CreateFullPackageAsync(folder, scenario.Id, version); + } - await SimpleTestPackageUtility.CreateFullPackageAsync(sourceDir, package.Id, "3.0.0-beta.1"); // prerelease only + repos.Add(Repository.Factory.GetCoreV3(new PackageSource(folder))); + } - var sources = new[] { Repository.Factory.GetCoreV3(new PackageSource(sourceDir)) }; var logger = new Mock(MockBehavior.Loose); - logger.Setup(l => l.LogError(It.IsAny())); + var package = new PackageWithNuGetVersion + { + Id = scenario.Id, + NuGetVersion = scenario.RequestedVersion is null ? null : NuGetVersion.Parse(scenario.RequestedVersion) + }; // Act (NuGetVersion resolved, SourceRepository resolvedRepo) = await PackageDownloadRunner.ResolvePackageDownloadVersion( package, - sources, + [.. repos], new SourceCacheContext(), logger.Object, - includePrerelease: false, // do not allow prerelease + includePrerelease: scenario.IncludePrerelease, CancellationToken.None); // Assert - Assert.Null(resolved); - Assert.Null(resolvedRepo); + if (scenario.ExpectedVersion is null) + { + Assert.Null(resolved); + Assert.Null(resolvedRepo); + return; + } + + Assert.Equal(new NuGetVersion(scenario.ExpectedVersion), resolved); + Assert.NotNull(resolvedRepo); + + var expectedPath = Path.Combine(context.WorkingDirectory, scenario.ExpectedSource!); + Assert.Equal(expectedPath, resolvedRepo.PackageSource.Source); } } From 3f79933b1547f2b2a6a4025db60e34e0b276caa8 Mon Sep 17 00:00:00 2001 From: Nigusu Solomon Yenework <59111203+Nigusu-Allehu@users.noreply.github.com> Date: Tue, 21 Oct 2025 17:38:38 -0700 Subject: [PATCH 09/15] undo --- .../NuGet.XPlat.FuncTest/NuGet.XPlat.FuncTest.csproj | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/test/NuGet.Core.FuncTests/NuGet.XPlat.FuncTest/NuGet.XPlat.FuncTest.csproj b/test/NuGet.Core.FuncTests/NuGet.XPlat.FuncTest/NuGet.XPlat.FuncTest.csproj index 816a7c37f2e..73610909851 100644 --- a/test/NuGet.Core.FuncTests/NuGet.XPlat.FuncTest/NuGet.XPlat.FuncTest.csproj +++ b/test/NuGet.Core.FuncTests/NuGet.XPlat.FuncTest/NuGet.XPlat.FuncTest.csproj @@ -14,7 +14,8 @@ - + From aac5448b925452c9887e7896a41b054f4a534307 Mon Sep 17 00:00:00 2001 From: Nigusu Yenework Date: Thu, 23 Oct 2025 14:07:47 -0700 Subject: [PATCH 10/15] unlisted --- .../Package/Download/PackageDownloadRunner.cs | 2 +- .../Download/PackageDownloadRunnerTests.cs | 63 ++++++++++++++++++- 2 files changed, 62 insertions(+), 3 deletions(-) diff --git a/src/NuGet.Core/NuGet.CommandLine.XPlat/Commands/Package/Download/PackageDownloadRunner.cs b/src/NuGet.Core/NuGet.CommandLine.XPlat/Commands/Package/Download/PackageDownloadRunner.cs index be237f7bb51..67785f19bef 100644 --- a/src/NuGet.Core/NuGet.CommandLine.XPlat/Commands/Package/Download/PackageDownloadRunner.cs +++ b/src/NuGet.Core/NuGet.CommandLine.XPlat/Commands/Package/Download/PackageDownloadRunner.cs @@ -145,7 +145,7 @@ await ResolvePackageDownloadVersion( var packages = await finder.GetMetadataAsync( packageWithNuGetVersion.Id, includePrerelease, - includeUnlisted: false, + includeUnlisted: versionSpecified, // only load unlisted if an exact version is specified sourceCacheContext: cache, logger, token); diff --git a/test/NuGet.Core.Tests/NuGet.CommandLine.Xplat.Tests/Commands/Package/Download/PackageDownloadRunnerTests.cs b/test/NuGet.Core.Tests/NuGet.CommandLine.Xplat.Tests/Commands/Package/Download/PackageDownloadRunnerTests.cs index 0b365d26674..c4426e176ed 100644 --- a/test/NuGet.Core.Tests/NuGet.CommandLine.Xplat.Tests/Commands/Package/Download/PackageDownloadRunnerTests.cs +++ b/test/NuGet.Core.Tests/NuGet.CommandLine.Xplat.Tests/Commands/Package/Download/PackageDownloadRunnerTests.cs @@ -1,12 +1,11 @@ // 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. -namespace NuGet.CommandLine.Xplat.Tests; - using System.Collections.Generic; using System.IO; using System.Threading; using System.Threading.Tasks; +using FluentAssertions; using Moq; using NuGet.CommandLine.XPlat; using NuGet.CommandLine.XPlat.Commands.Package; @@ -16,8 +15,11 @@ namespace NuGet.CommandLine.Xplat.Tests; using NuGet.Protocol.Core.Types; using NuGet.Test.Utility; using NuGet.Versioning; +using Test.Utility; using Xunit; +namespace NuGet.CommandLine.Xplat.Tests; + public class PackageDownloadRunnerTests { public sealed record SourceSpec(string Name, params string[] Versions); @@ -142,4 +144,61 @@ public async Task ResolvePackageDownloadVersion_Scenarios(ResolveScenario scenar var expectedPath = Path.Combine(context.WorkingDirectory, scenario.ExpectedSource!); Assert.Equal(expectedPath, resolvedRepo.PackageSource.Source); } + + [Theory] + [InlineData("1.0.0", true)] // exact version specified -> should find even if unlisted + [InlineData(null, false)] // no exact version -> should return null when unlisted + public async Task ResolvePackageDownloadVersion_UnlistedPackage_BehavesAsExpected(string requestedVersion, bool expectFound) + { + // Arrange + using var pathContext = new SimpleTestPathContext(); + using var mockServer = new FileSystemBackedV3MockServer(pathContext.PackageSource); + + var cache = new SourceCacheContext(); + var logger = new Mock(); + var token = CancellationToken.None; + + var sourceRepo = Repository.Factory.GetCoreV3(mockServer.ServiceIndexUri); + + // Create the package and unlist it on the mock server. + var id = "Contoso.unlisted"; + var createdVersion = "1.0.0"; + var pkg = new SimpleTestPackageContext(id, createdVersion); + await SimpleTestPackageUtility.CreatePackagesAsync(pathContext.PackageSource, pkg); + mockServer.UnlistedPackages.Add(pkg.Identity); + + mockServer.Start(); + + var packageWithNuGetVersion = new PackageWithNuGetVersion + { + Id = id, + NuGetVersion = requestedVersion is null ? null : NuGetVersion.Parse(requestedVersion) + }; + + // Act + (NuGetVersion foundVersion, SourceRepository foundRepo) = await PackageDownloadRunner.ResolvePackageDownloadVersion( + packageWithNuGetVersion, + [sourceRepo], + cache, + logger.Object, + includePrerelease: false, + token); + + mockServer.Stop(); + + // Assert + if (expectFound) + { + foundVersion.Should().NotBeNull(); + foundVersion.OriginalVersion.Should().Be(requestedVersion); + foundRepo.Should().NotBeNull(); + foundRepo.PackageSource.Name.Should().Be(sourceRepo.PackageSource.Name); + } + else + { + foundVersion.Should().BeNull(); + foundRepo.Should().BeNull(); + } + } + } From b075e4cb69b4c86b18d35e9fc705c8cc3b818d89 Mon Sep 17 00:00:00 2001 From: Nigusu Yenework Date: Sun, 26 Oct 2025 18:38:47 -0700 Subject: [PATCH 11/15] more test --- .../Download/PackageDownloadCommand.cs | 4 +- .../Package/Download/PackageDownloadRunner.cs | 37 +++--- .../Strings.Designer.cs | 8 +- .../NuGet.CommandLine.XPlat/Strings.resx | 4 +- .../xlf/Strings.cs.xlf | 12 +- .../xlf/Strings.de.xlf | 12 +- .../xlf/Strings.es.xlf | 12 +- .../xlf/Strings.fr.xlf | 12 +- .../xlf/Strings.it.xlf | 12 +- .../xlf/Strings.ja.xlf | 12 +- .../xlf/Strings.ko.xlf | 12 +- .../xlf/Strings.pl.xlf | 12 +- .../xlf/Strings.pt-BR.xlf | 12 +- .../xlf/Strings.ru.xlf | 12 +- .../xlf/Strings.tr.xlf | 12 +- .../xlf/Strings.zh-Hans.xlf | 12 +- .../xlf/Strings.zh-Hant.xlf | 12 +- .../Download/PackageDownloadRunnerTests.cs | 106 ++++++++++++------ 18 files changed, 176 insertions(+), 139 deletions(-) diff --git a/src/NuGet.Core/NuGet.CommandLine.XPlat/Commands/Package/Download/PackageDownloadCommand.cs b/src/NuGet.Core/NuGet.CommandLine.XPlat/Commands/Package/Download/PackageDownloadCommand.cs index 3224a0d4ebd..b76da4b49b5 100644 --- a/src/NuGet.Core/NuGet.CommandLine.XPlat/Commands/Package/Download/PackageDownloadCommand.cs +++ b/src/NuGet.Core/NuGet.CommandLine.XPlat/Commands/Package/Download/PackageDownloadCommand.cs @@ -20,7 +20,7 @@ public static void Register(Command packageCommand, Option interactiveOpti { var downloadCommand = new DocumentedCommand( "download", - Strings.PackageDownloadCommand_descritpion, + Strings.PackageDownloadCommand_Description, "https://aka.ms/dotnet/package/download"); // Arguments @@ -34,7 +34,7 @@ public static void Register(Command packageCommand, Option interactiveOpti // Options var allowInsecureConnections = new Option("--allow-insecure-connections") { - Description = Strings.PackageDownloadCommand_AllowInsecureConnectionsDescritption, + Description = Strings.PackageDownloadCommand_AllowInsecureConnectionsDescription, Arity = ArgumentArity.Zero }; diff --git a/src/NuGet.Core/NuGet.CommandLine.XPlat/Commands/Package/Download/PackageDownloadRunner.cs b/src/NuGet.Core/NuGet.CommandLine.XPlat/Commands/Package/Download/PackageDownloadRunner.cs index 67785f19bef..f6a93645940 100644 --- a/src/NuGet.Core/NuGet.CommandLine.XPlat/Commands/Package/Download/PackageDownloadRunner.cs +++ b/src/NuGet.Core/NuGet.CommandLine.XPlat/Commands/Package/Download/PackageDownloadRunner.cs @@ -86,14 +86,14 @@ await ResolvePackageDownloadVersion( } bool success = await DownloadPackageAsync( - package.Id, - version, - downloadRepository, - cache, - settings, - outputDirectory, - logger, - token); + package.Id, + version, + downloadRepository, + cache, + settings, + outputDirectory, + logger, + token); if (success) { @@ -211,11 +211,11 @@ private static async Task DownloadPackageAsync( if (userPackageFolder.Exists(id, version)) { logger.LogMinimal(string.Format( - CultureInfo.CurrentCulture, - Strings.PackageDownloadCommand_AlreadyInstalled, - id, - version.ToNormalizedString(), - outputDirectory)); + CultureInfo.CurrentCulture, + Strings.PackageDownloadCommand_AlreadyInstalled, + id, + version.ToNormalizedString(), + outputDirectory)); return true; } @@ -224,14 +224,15 @@ private static async Task DownloadPackageAsync( var provider = new SourceRepositoryDependencyProvider(sourceRepository: repo, logger: logger, cacheContext: cache, ignoreFailedSources: false, ignoreWarning: false); using var downloader = await provider.GetPackageDownloaderAsync(packageIdentity, cache, logger, token); bool success = await PackageExtractor.InstallFromSourceAsync(packageIdentity, downloader, resolver, extractionContext, token); + if (!success) { logger.LogError(string.Format( - CultureInfo.CurrentCulture, - Strings.PackageDownloadCommand_UnableToDownload, - id, - version.ToNormalizedString(), - repo.PackageSource.Source)); + CultureInfo.CurrentCulture, + Strings.PackageDownloadCommand_UnableToDownload, + id, + version.ToNormalizedString(), + repo.PackageSource.Source)); return false; } diff --git a/src/NuGet.Core/NuGet.CommandLine.XPlat/Strings.Designer.cs b/src/NuGet.Core/NuGet.CommandLine.XPlat/Strings.Designer.cs index 9eaf5532b96..33be7a2c176 100644 --- a/src/NuGet.Core/NuGet.CommandLine.XPlat/Strings.Designer.cs +++ b/src/NuGet.Core/NuGet.CommandLine.XPlat/Strings.Designer.cs @@ -1601,9 +1601,9 @@ internal static string OutputNuGetVersion { /// /// Looks up a localized string similar to Allows downloading from HTTP (non-HTTPS) package sources.. /// - internal static string PackageDownloadCommand_AllowInsecureConnectionsDescritption { + internal static string PackageDownloadCommand_AllowInsecureConnectionsDescription { get { - return ResourceManager.GetString("PackageDownloadCommand_AllowInsecureConnectionsDescritption", resourceCulture); + return ResourceManager.GetString("PackageDownloadCommand_AllowInsecureConnectionsDescription", resourceCulture); } } @@ -1619,9 +1619,9 @@ internal static string PackageDownloadCommand_AlreadyInstalled { /// /// Looks up a localized string similar to Downloads a NuGet package to a local folder without requiring a project file.. /// - internal static string PackageDownloadCommand_descritpion { + internal static string PackageDownloadCommand_Description { get { - return ResourceManager.GetString("PackageDownloadCommand_descritpion", resourceCulture); + return ResourceManager.GetString("PackageDownloadCommand_Description", resourceCulture); } } diff --git a/src/NuGet.Core/NuGet.CommandLine.XPlat/Strings.resx b/src/NuGet.Core/NuGet.CommandLine.XPlat/Strings.resx index 03ae477ee23..76c0f91d27b 100644 --- a/src/NuGet.Core/NuGet.CommandLine.XPlat/Strings.resx +++ b/src/NuGet.Core/NuGet.CommandLine.XPlat/Strings.resx @@ -1108,13 +1108,13 @@ Do not translate "PackageVersion" Invalid version value '{0}'. 0 - package version - + Downloads a NuGet package to a local folder without requiring a project file. Package identifier (e.g. 'Newtonsoft.Json'). - + Allows downloading from HTTP (non-HTTPS) package sources. diff --git a/src/NuGet.Core/NuGet.CommandLine.XPlat/xlf/Strings.cs.xlf b/src/NuGet.Core/NuGet.CommandLine.XPlat/xlf/Strings.cs.xlf index 7fd60d6720c..7a54623b1e4 100644 --- a/src/NuGet.Core/NuGet.CommandLine.XPlat/xlf/Strings.cs.xlf +++ b/src/NuGet.Core/NuGet.CommandLine.XPlat/xlf/Strings.cs.xlf @@ -891,7 +891,7 @@ Další informace najdete tady: https://docs.nuget.org/docs/reference/command-li {0}: App full name {1}: App version - + Allows downloading from HTTP (non-HTTPS) package sources. Allows downloading from HTTP (non-HTTPS) package sources. @@ -903,6 +903,11 @@ Další informace najdete tady: https://docs.nuget.org/docs/reference/command-li 1 - version 2 - directory path where the package was downloaded + + Downloads a NuGet package to a local folder without requiring a project file. + Downloads a NuGet package to a local folder without requiring a project file. + + Package '{0}' ({1}) failed to download. Package '{0}' ({1}) failed to download. @@ -949,11 +954,6 @@ Další informace najdete tady: https://docs.nuget.org/docs/reference/command-li 1 package version 2 package source - - Downloads a NuGet package to a local folder without requiring a project file. - Downloads a NuGet package to a local folder without requiring a project file. - - Update referenced packages in a project or solution. Aktualizujte odkazované balíčky v projektu nebo řešení. diff --git a/src/NuGet.Core/NuGet.CommandLine.XPlat/xlf/Strings.de.xlf b/src/NuGet.Core/NuGet.CommandLine.XPlat/xlf/Strings.de.xlf index ca9fd24f5d9..4df698a6e77 100644 --- a/src/NuGet.Core/NuGet.CommandLine.XPlat/xlf/Strings.de.xlf +++ b/src/NuGet.Core/NuGet.CommandLine.XPlat/xlf/Strings.de.xlf @@ -891,7 +891,7 @@ Weitere Informationen finden Sie unter: https://docs.nuget.org/docs/reference/co {0}: App full name {1}: App version - + Allows downloading from HTTP (non-HTTPS) package sources. Allows downloading from HTTP (non-HTTPS) package sources. @@ -903,6 +903,11 @@ Weitere Informationen finden Sie unter: https://docs.nuget.org/docs/reference/co 1 - version 2 - directory path where the package was downloaded + + Downloads a NuGet package to a local folder without requiring a project file. + Downloads a NuGet package to a local folder without requiring a project file. + + Package '{0}' ({1}) failed to download. Package '{0}' ({1}) failed to download. @@ -949,11 +954,6 @@ Weitere Informationen finden Sie unter: https://docs.nuget.org/docs/reference/co 1 package version 2 package source - - Downloads a NuGet package to a local folder without requiring a project file. - Downloads a NuGet package to a local folder without requiring a project file. - - Update referenced packages in a project or solution. Referenzierte Pakete in einem Projekt oder einer Projektmappe aktualisieren. diff --git a/src/NuGet.Core/NuGet.CommandLine.XPlat/xlf/Strings.es.xlf b/src/NuGet.Core/NuGet.CommandLine.XPlat/xlf/Strings.es.xlf index 8d3ae83c00a..3f8b89bf311 100644 --- a/src/NuGet.Core/NuGet.CommandLine.XPlat/xlf/Strings.es.xlf +++ b/src/NuGet.Core/NuGet.CommandLine.XPlat/xlf/Strings.es.xlf @@ -891,7 +891,7 @@ Para obtener más información, visite https://docs.nuget.org/docs/reference/com {0}: App full name {1}: App version - + Allows downloading from HTTP (non-HTTPS) package sources. Allows downloading from HTTP (non-HTTPS) package sources. @@ -903,6 +903,11 @@ Para obtener más información, visite https://docs.nuget.org/docs/reference/com 1 - version 2 - directory path where the package was downloaded + + Downloads a NuGet package to a local folder without requiring a project file. + Downloads a NuGet package to a local folder without requiring a project file. + + Package '{0}' ({1}) failed to download. Package '{0}' ({1}) failed to download. @@ -949,11 +954,6 @@ Para obtener más información, visite https://docs.nuget.org/docs/reference/com 1 package version 2 package source - - Downloads a NuGet package to a local folder without requiring a project file. - Downloads a NuGet package to a local folder without requiring a project file. - - Update referenced packages in a project or solution. Actualizar los paquetes a los que se hace referencia en un proyecto o solución. diff --git a/src/NuGet.Core/NuGet.CommandLine.XPlat/xlf/Strings.fr.xlf b/src/NuGet.Core/NuGet.CommandLine.XPlat/xlf/Strings.fr.xlf index 37595f3e427..8708500291d 100644 --- a/src/NuGet.Core/NuGet.CommandLine.XPlat/xlf/Strings.fr.xlf +++ b/src/NuGet.Core/NuGet.CommandLine.XPlat/xlf/Strings.fr.xlf @@ -891,7 +891,7 @@ Pour plus d'informations, visitez https://docs.nuget.org/docs/reference/command- {0}: App full name {1}: App version - + Allows downloading from HTTP (non-HTTPS) package sources. Allows downloading from HTTP (non-HTTPS) package sources. @@ -903,6 +903,11 @@ Pour plus d'informations, visitez https://docs.nuget.org/docs/reference/command- 1 - version 2 - directory path where the package was downloaded + + Downloads a NuGet package to a local folder without requiring a project file. + Downloads a NuGet package to a local folder without requiring a project file. + + Package '{0}' ({1}) failed to download. Package '{0}' ({1}) failed to download. @@ -949,11 +954,6 @@ Pour plus d'informations, visitez https://docs.nuget.org/docs/reference/command- 1 package version 2 package source - - Downloads a NuGet package to a local folder without requiring a project file. - Downloads a NuGet package to a local folder without requiring a project file. - - Update referenced packages in a project or solution. Mettre à jour les packages référencés dans un projet ou une solution. diff --git a/src/NuGet.Core/NuGet.CommandLine.XPlat/xlf/Strings.it.xlf b/src/NuGet.Core/NuGet.CommandLine.XPlat/xlf/Strings.it.xlf index b63de5081db..2a30afecf31 100644 --- a/src/NuGet.Core/NuGet.CommandLine.XPlat/xlf/Strings.it.xlf +++ b/src/NuGet.Core/NuGet.CommandLine.XPlat/xlf/Strings.it.xlf @@ -891,7 +891,7 @@ Per altre informazioni, vedere https://docs.nuget.org/docs/reference/command-lin {0}: App full name {1}: App version - + Allows downloading from HTTP (non-HTTPS) package sources. Allows downloading from HTTP (non-HTTPS) package sources. @@ -903,6 +903,11 @@ Per altre informazioni, vedere https://docs.nuget.org/docs/reference/command-lin 1 - version 2 - directory path where the package was downloaded + + Downloads a NuGet package to a local folder without requiring a project file. + Downloads a NuGet package to a local folder without requiring a project file. + + Package '{0}' ({1}) failed to download. Package '{0}' ({1}) failed to download. @@ -949,11 +954,6 @@ Per altre informazioni, vedere https://docs.nuget.org/docs/reference/command-lin 1 package version 2 package source - - Downloads a NuGet package to a local folder without requiring a project file. - Downloads a NuGet package to a local folder without requiring a project file. - - Update referenced packages in a project or solution. Aggiorna i pacchetti referenziati in un progetto o in una soluzione. diff --git a/src/NuGet.Core/NuGet.CommandLine.XPlat/xlf/Strings.ja.xlf b/src/NuGet.Core/NuGet.CommandLine.XPlat/xlf/Strings.ja.xlf index b54246f9d12..d8aa3b3fbef 100644 --- a/src/NuGet.Core/NuGet.CommandLine.XPlat/xlf/Strings.ja.xlf +++ b/src/NuGet.Core/NuGet.CommandLine.XPlat/xlf/Strings.ja.xlf @@ -891,7 +891,7 @@ For more information, visit https://docs.nuget.org/docs/reference/command-line-r {0}: App full name {1}: App version - + Allows downloading from HTTP (non-HTTPS) package sources. Allows downloading from HTTP (non-HTTPS) package sources. @@ -903,6 +903,11 @@ For more information, visit https://docs.nuget.org/docs/reference/command-line-r 1 - version 2 - directory path where the package was downloaded + + Downloads a NuGet package to a local folder without requiring a project file. + Downloads a NuGet package to a local folder without requiring a project file. + + Package '{0}' ({1}) failed to download. Package '{0}' ({1}) failed to download. @@ -949,11 +954,6 @@ For more information, visit https://docs.nuget.org/docs/reference/command-line-r 1 package version 2 package source - - Downloads a NuGet package to a local folder without requiring a project file. - Downloads a NuGet package to a local folder without requiring a project file. - - Update referenced packages in a project or solution. プロジェクトまたはソリューションで参照されているパッケージを更新します。 diff --git a/src/NuGet.Core/NuGet.CommandLine.XPlat/xlf/Strings.ko.xlf b/src/NuGet.Core/NuGet.CommandLine.XPlat/xlf/Strings.ko.xlf index 96fa5b86436..6ba03ee2e91 100644 --- a/src/NuGet.Core/NuGet.CommandLine.XPlat/xlf/Strings.ko.xlf +++ b/src/NuGet.Core/NuGet.CommandLine.XPlat/xlf/Strings.ko.xlf @@ -891,7 +891,7 @@ For more information, visit https://docs.nuget.org/docs/reference/command-line-r {0}: App full name {1}: App version - + Allows downloading from HTTP (non-HTTPS) package sources. Allows downloading from HTTP (non-HTTPS) package sources. @@ -903,6 +903,11 @@ For more information, visit https://docs.nuget.org/docs/reference/command-line-r 1 - version 2 - directory path where the package was downloaded + + Downloads a NuGet package to a local folder without requiring a project file. + Downloads a NuGet package to a local folder without requiring a project file. + + Package '{0}' ({1}) failed to download. Package '{0}' ({1}) failed to download. @@ -949,11 +954,6 @@ For more information, visit https://docs.nuget.org/docs/reference/command-line-r 1 package version 2 package source - - Downloads a NuGet package to a local folder without requiring a project file. - Downloads a NuGet package to a local folder without requiring a project file. - - Update referenced packages in a project or solution. 프로젝트 또는 솔루션에서 참조된 패키지를 업데이트합니다. diff --git a/src/NuGet.Core/NuGet.CommandLine.XPlat/xlf/Strings.pl.xlf b/src/NuGet.Core/NuGet.CommandLine.XPlat/xlf/Strings.pl.xlf index f5e6104c8d9..7f31ead7118 100644 --- a/src/NuGet.Core/NuGet.CommandLine.XPlat/xlf/Strings.pl.xlf +++ b/src/NuGet.Core/NuGet.CommandLine.XPlat/xlf/Strings.pl.xlf @@ -891,7 +891,7 @@ Aby uzyskać więcej informacji, odwiedź stronę https://docs.nuget.org/docs/re {0}: App full name {1}: App version - + Allows downloading from HTTP (non-HTTPS) package sources. Allows downloading from HTTP (non-HTTPS) package sources. @@ -903,6 +903,11 @@ Aby uzyskać więcej informacji, odwiedź stronę https://docs.nuget.org/docs/re 1 - version 2 - directory path where the package was downloaded + + Downloads a NuGet package to a local folder without requiring a project file. + Downloads a NuGet package to a local folder without requiring a project file. + + Package '{0}' ({1}) failed to download. Package '{0}' ({1}) failed to download. @@ -949,11 +954,6 @@ Aby uzyskać więcej informacji, odwiedź stronę https://docs.nuget.org/docs/re 1 package version 2 package source - - Downloads a NuGet package to a local folder without requiring a project file. - Downloads a NuGet package to a local folder without requiring a project file. - - Update referenced packages in a project or solution. Zaktualizuj przywoływane pakiety w projekcie lub rozwiązaniu. diff --git a/src/NuGet.Core/NuGet.CommandLine.XPlat/xlf/Strings.pt-BR.xlf b/src/NuGet.Core/NuGet.CommandLine.XPlat/xlf/Strings.pt-BR.xlf index c12d9e48c37..2140037fd02 100644 --- a/src/NuGet.Core/NuGet.CommandLine.XPlat/xlf/Strings.pt-BR.xlf +++ b/src/NuGet.Core/NuGet.CommandLine.XPlat/xlf/Strings.pt-BR.xlf @@ -891,7 +891,7 @@ Para obter mais informações, acesse https://docs.nuget.org/docs/reference/comm {0}: App full name {1}: App version - + Allows downloading from HTTP (non-HTTPS) package sources. Allows downloading from HTTP (non-HTTPS) package sources. @@ -903,6 +903,11 @@ Para obter mais informações, acesse https://docs.nuget.org/docs/reference/comm 1 - version 2 - directory path where the package was downloaded + + Downloads a NuGet package to a local folder without requiring a project file. + Downloads a NuGet package to a local folder without requiring a project file. + + Package '{0}' ({1}) failed to download. Package '{0}' ({1}) failed to download. @@ -949,11 +954,6 @@ Para obter mais informações, acesse https://docs.nuget.org/docs/reference/comm 1 package version 2 package source - - Downloads a NuGet package to a local folder without requiring a project file. - Downloads a NuGet package to a local folder without requiring a project file. - - Update referenced packages in a project or solution. Atualize os pacotes referenciados em um projeto ou solução. diff --git a/src/NuGet.Core/NuGet.CommandLine.XPlat/xlf/Strings.ru.xlf b/src/NuGet.Core/NuGet.CommandLine.XPlat/xlf/Strings.ru.xlf index ea40be5079e..65f1b305575 100644 --- a/src/NuGet.Core/NuGet.CommandLine.XPlat/xlf/Strings.ru.xlf +++ b/src/NuGet.Core/NuGet.CommandLine.XPlat/xlf/Strings.ru.xlf @@ -891,7 +891,7 @@ For more information, visit https://docs.nuget.org/docs/reference/command-line-r {0}: App full name {1}: App version - + Allows downloading from HTTP (non-HTTPS) package sources. Allows downloading from HTTP (non-HTTPS) package sources. @@ -903,6 +903,11 @@ For more information, visit https://docs.nuget.org/docs/reference/command-line-r 1 - version 2 - directory path where the package was downloaded + + Downloads a NuGet package to a local folder without requiring a project file. + Downloads a NuGet package to a local folder without requiring a project file. + + Package '{0}' ({1}) failed to download. Package '{0}' ({1}) failed to download. @@ -949,11 +954,6 @@ For more information, visit https://docs.nuget.org/docs/reference/command-line-r 1 package version 2 package source - - Downloads a NuGet package to a local folder without requiring a project file. - Downloads a NuGet package to a local folder without requiring a project file. - - Update referenced packages in a project or solution. Обновите пакеты, на которые ссылается проект или решение. diff --git a/src/NuGet.Core/NuGet.CommandLine.XPlat/xlf/Strings.tr.xlf b/src/NuGet.Core/NuGet.CommandLine.XPlat/xlf/Strings.tr.xlf index 367538d3a6b..01e3e4eb894 100644 --- a/src/NuGet.Core/NuGet.CommandLine.XPlat/xlf/Strings.tr.xlf +++ b/src/NuGet.Core/NuGet.CommandLine.XPlat/xlf/Strings.tr.xlf @@ -892,7 +892,7 @@ Daha fazla bilgi için bkz. https://docs.nuget.org/docs/reference/command-line-r {0}: App full name {1}: App version - + Allows downloading from HTTP (non-HTTPS) package sources. Allows downloading from HTTP (non-HTTPS) package sources. @@ -904,6 +904,11 @@ Daha fazla bilgi için bkz. https://docs.nuget.org/docs/reference/command-line-r 1 - version 2 - directory path where the package was downloaded + + Downloads a NuGet package to a local folder without requiring a project file. + Downloads a NuGet package to a local folder without requiring a project file. + + Package '{0}' ({1}) failed to download. Package '{0}' ({1}) failed to download. @@ -950,11 +955,6 @@ Daha fazla bilgi için bkz. https://docs.nuget.org/docs/reference/command-line-r 1 package version 2 package source - - Downloads a NuGet package to a local folder without requiring a project file. - Downloads a NuGet package to a local folder without requiring a project file. - - Update referenced packages in a project or solution. Bir projede veya çözümde başvurulan paketleri güncelleştirin. diff --git a/src/NuGet.Core/NuGet.CommandLine.XPlat/xlf/Strings.zh-Hans.xlf b/src/NuGet.Core/NuGet.CommandLine.XPlat/xlf/Strings.zh-Hans.xlf index 735609c4b84..b62044171b7 100644 --- a/src/NuGet.Core/NuGet.CommandLine.XPlat/xlf/Strings.zh-Hans.xlf +++ b/src/NuGet.Core/NuGet.CommandLine.XPlat/xlf/Strings.zh-Hans.xlf @@ -891,7 +891,7 @@ For more information, visit https://docs.nuget.org/docs/reference/command-line-r {0}: App full name {1}: App version - + Allows downloading from HTTP (non-HTTPS) package sources. Allows downloading from HTTP (non-HTTPS) package sources. @@ -903,6 +903,11 @@ For more information, visit https://docs.nuget.org/docs/reference/command-line-r 1 - version 2 - directory path where the package was downloaded + + Downloads a NuGet package to a local folder without requiring a project file. + Downloads a NuGet package to a local folder without requiring a project file. + + Package '{0}' ({1}) failed to download. Package '{0}' ({1}) failed to download. @@ -949,11 +954,6 @@ For more information, visit https://docs.nuget.org/docs/reference/command-line-r 1 package version 2 package source - - Downloads a NuGet package to a local folder without requiring a project file. - Downloads a NuGet package to a local folder without requiring a project file. - - Update referenced packages in a project or solution. 更新项目或解决方案中引用的包。 diff --git a/src/NuGet.Core/NuGet.CommandLine.XPlat/xlf/Strings.zh-Hant.xlf b/src/NuGet.Core/NuGet.CommandLine.XPlat/xlf/Strings.zh-Hant.xlf index efbc728dd39..5dd4fe8bc5c 100644 --- a/src/NuGet.Core/NuGet.CommandLine.XPlat/xlf/Strings.zh-Hant.xlf +++ b/src/NuGet.Core/NuGet.CommandLine.XPlat/xlf/Strings.zh-Hant.xlf @@ -891,7 +891,7 @@ For more information, visit https://docs.nuget.org/docs/reference/command-line-r {0}: App full name {1}: App version - + Allows downloading from HTTP (non-HTTPS) package sources. Allows downloading from HTTP (non-HTTPS) package sources. @@ -903,6 +903,11 @@ For more information, visit https://docs.nuget.org/docs/reference/command-line-r 1 - version 2 - directory path where the package was downloaded + + Downloads a NuGet package to a local folder without requiring a project file. + Downloads a NuGet package to a local folder without requiring a project file. + + Package '{0}' ({1}) failed to download. Package '{0}' ({1}) failed to download. @@ -949,11 +954,6 @@ For more information, visit https://docs.nuget.org/docs/reference/command-line-r 1 package version 2 package source - - Downloads a NuGet package to a local folder without requiring a project file. - Downloads a NuGet package to a local folder without requiring a project file. - - Update referenced packages in a project or solution. 更新專案或解決方案中參照的套件。 diff --git a/test/NuGet.Core.FuncTests/NuGet.XPlat.FuncTest/Package/Download/PackageDownloadRunnerTests.cs b/test/NuGet.Core.FuncTests/NuGet.XPlat.FuncTest/Package/Download/PackageDownloadRunnerTests.cs index 1f31c0d4e39..06bcf4fd911 100644 --- a/test/NuGet.Core.FuncTests/NuGet.XPlat.FuncTest/Package/Download/PackageDownloadRunnerTests.cs +++ b/test/NuGet.Core.FuncTests/NuGet.XPlat.FuncTest/Package/Download/PackageDownloadRunnerTests.cs @@ -1,8 +1,6 @@ // 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. -namespace NuGet.CommandLine.Xplat.Tests; - using System; using System.Collections.Generic; using System.IO; @@ -20,11 +18,13 @@ namespace NuGet.CommandLine.Xplat.Tests; using NuGet.Versioning; using Xunit; +namespace NuGet.CommandLine.Xplat.Tests; + public class PackageDownloadRunnerTests { public static IEnumerable PackageTestData() { - // Basic stable explicit version + // Basic stable explicit versions yield return new object[] { new List<(string, string)> @@ -32,11 +32,19 @@ public static IEnumerable PackageTestData() ("Contoso.Core", "1.0.0"), ("Contoso.Core", "1.1.0"), ("Contoso.Core", "2.0.0-beta") - }, - "Contoso.Core", // downloadId argument - "1.1.0", // downloadVersion argument + }, // source packages + new List<(string, string)> + { + ("Contoso.Core", "1.1.0"), + ("Contoso.Core", "1.0.0"), + }, // argument packages false, // enablePrerelease - ("Contoso.Core", "1.1.0") // expected + "myOutput", // output directory subpath + new List<(string, string)> + { + ("Contoso.Core", "1.1.0"), + ("Contoso.Core", "1.0.0") + } // expected }; // Mixed casing on the ID in the *download* argument @@ -45,11 +53,17 @@ public static IEnumerable PackageTestData() new List<(string, string)> { ("Contoso.Core", "1.1.0") - }, - "contoso.core", // downloadId argument - "1.1.0", // downloadVersion argument - false, // enablePrerelease - ("Contoso.Core", "1.1.0") // expected + }, // source packages + new List<(string, string)> + { + ("contoso.core", "1.1.0") + }, // download argument + false, // enablePrerelease + "", // output directory subpath + new List<(string, string)> + { + ("Contoso.Core", "1.1.0") + }, // expected }; // prerelease with IncludePrerelease == true @@ -59,11 +73,17 @@ public static IEnumerable PackageTestData() { ("Contoso.Preview", "1.3.0"), ("Contoso.Preview", "2.0.0-beta.2") - }, - "Contoso.Preview", // downloadId argument - null, // downloadVersion argument - true, // enablePrerelease - ("Contoso.Preview", "2.0.0-beta.2") // expected + }, // source packages + new List<(string, string)> + { + ("Contoso.Preview", null), + }, // download argument + true, // enablePrerelease + "AnotherSubPath", // output directory subpath + new List<(string, string)> + { + ("Contoso.Preview", "2.0.0-beta.2") + }, // expected }; // chose stable with IncludePrerelease == false @@ -74,28 +94,32 @@ public static IEnumerable PackageTestData() ("Contoso.Preview", "1.3.2"), ("Contoso.Preview", "1.3.0"), ("Contoso.Preview", "2.0.0-beta.2") - }, - "Contoso.Preview", // downloadId argument - null, // downloadVersion argument - false, // enablePrerelease - ("Contoso.Preview", "1.3.2") // expected + }, // source packages + new List<(string, string)> + { + ("Contoso.Preview", null) + }, // download argument + false, // enablePrerelease + "SubPath", // output directory subpath + new List <(string, string) > { + ("Contoso.Preview", "1.3.2") + } // expected }; } - [Theory] [MemberData(nameof(PackageTestData))] public async Task RunAsync_ExplicitVersionFromLocalFolderSource_SucceedsAsync( IReadOnlyList<(string, string)> sourcePackages, - string downloadId, - string downloadVersion, + IReadOnlyList<(string, string)> argumentPackages, bool enablePrerelease, - (string, string) expectedPackage) + string outputDirectorySubPath, + IReadOnlyList<(string, string)> expectedPackages) { // Arrange using var context = new SimpleTestPathContext(); var sourceDir = context.PackageSource; - var outputDir = context.WorkingDirectory; + var outputDir = Path.Combine(context.WorkingDirectory, outputDirectorySubPath); foreach (var (id, version) in sourcePackages) { @@ -104,15 +128,24 @@ public async Task RunAsync_ExplicitVersionFromLocalFolderSource_SucceedsAsync( var logger = new Mock(MockBehavior.Loose); var settings = new Mock(MockBehavior.Loose); + List packages = []; + + foreach (var (id, version) in argumentPackages) + { + packages.Add(new PackageWithNuGetVersion + { + Id = id, + NuGetVersion = version == null ? null : NuGetVersion.Parse(version) + }); + } var args = new PackageDownloadArgs() { - Packages = [new PackageWithNuGetVersion { Id = downloadId, NuGetVersion = downloadVersion == null ? null : NuGetVersion.Parse(downloadVersion) }], + Packages = packages, OutputDirectory = outputDir, - IncludePrerelease = enablePrerelease + IncludePrerelease = enablePrerelease, }; - // Act var result = await PackageDownloadRunner.RunAsync( args, @@ -122,11 +155,14 @@ public async Task RunAsync_ExplicitVersionFromLocalFolderSource_SucceedsAsync( CancellationToken.None); // Assert - result.Should().Be(PackageDownloadRunner.ExitCodeSuccess); - var installDir = Path.Combine(outputDir, expectedPackage.Item1.ToLowerInvariant(), expectedPackage.Item2); - Directory.Exists(installDir).Should().BeTrue(); - Directory.EnumerateFiles(installDir, "*.nupkg").Any().Should().BeTrue(); - File.Exists(Path.Combine(installDir, $"{expectedPackage.Item1.ToLowerInvariant()}.{expectedPackage.Item2}.nupkg")).Should().BeTrue(); + foreach (var (expectedId, expectedVersion) in expectedPackages) + { + result.Should().Be(PackageDownloadRunner.ExitCodeSuccess); + var installDir = Path.Combine(outputDir, expectedId.ToLowerInvariant(), expectedVersion); + Directory.Exists(installDir).Should().BeTrue(); + Directory.EnumerateFiles(installDir, "*.nupkg").Any().Should().BeTrue(); + File.Exists(Path.Combine(installDir, $"{expectedId.ToLowerInvariant()}.{expectedVersion}.nupkg")).Should().BeTrue(); + } } [Fact] From 149339e250ede7d9964af18804e9888e63570b1f Mon Sep 17 00:00:00 2001 From: Nigusu Yenework Date: Mon, 27 Oct 2025 13:43:31 -0700 Subject: [PATCH 12/15] more test --- .../Download/PackageDownloadRunnerTests.cs | 57 +++++++++++++++---- 1 file changed, 46 insertions(+), 11 deletions(-) diff --git a/test/NuGet.Core.FuncTests/NuGet.XPlat.FuncTest/Package/Download/PackageDownloadRunnerTests.cs b/test/NuGet.Core.FuncTests/NuGet.XPlat.FuncTest/Package/Download/PackageDownloadRunnerTests.cs index 06bcf4fd911..464904985ac 100644 --- a/test/NuGet.Core.FuncTests/NuGet.XPlat.FuncTest/Package/Download/PackageDownloadRunnerTests.cs +++ b/test/NuGet.Core.FuncTests/NuGet.XPlat.FuncTest/Package/Download/PackageDownloadRunnerTests.cs @@ -204,17 +204,48 @@ public async Task RunAsync_NoVersion_PicksHighestAcrossMultipleSources() File.Exists(Path.Combine(chosen, $"{id.ToLowerInvariant()}.1.2.0.nupkg")).Should().BeTrue(); } - [Fact] - public async Task RunAsync_ExplicitVersionAlreadyInstalled_ShortCircuitsAndSucceeds() + public static IEnumerable ShortCircuitsPackageData => + [ + [ + new[] { + new PackageWithNuGetVersion() + { + Id = "Contoso.Utils", + NuGetVersion = NuGetVersion.Parse("3.4.5") + } + } + ], + [ + new[] { + new PackageWithNuGetVersion() + { + Id = "Contoso.Utils", + NuGetVersion = NuGetVersion.Parse("3.4.5") + }, + new PackageWithNuGetVersion() + { + Id = "Contoso.Core", + NuGetVersion = NuGetVersion.Parse("3.0.5") + } + } + ], + ]; + + [Theory] + [MemberData(nameof(ShortCircuitsPackageData))] + internal async Task RunAsync_ExplicitVersionAlreadyInstalled_ShortCircuitsAndSucceeds(PackageWithNuGetVersion[] packages) { // Arrange using var context = new SimpleTestPathContext(); var sourceDir = context.PackageSource; var outputDir = context.WorkingDirectory; - var id = "Contoso.Utils"; - var v = "3.4.5"; - await SimpleTestPackageUtility.CreateFullPackageAsync(sourceDir, id, v); + List packagesToInstall = []; + + foreach (var package in packages) + { + await SimpleTestPackageUtility.CreateFullPackageAsync(sourceDir, package.Id, package.NuGetVersion.OriginalVersion); + } var logger = new Mock(MockBehavior.Loose); var settings = new Mock(MockBehavior.Loose); @@ -222,7 +253,7 @@ public async Task RunAsync_ExplicitVersionAlreadyInstalled_ShortCircuitsAndSucce // First run: install explicit version var args1 = new PackageDownloadArgs() { - Packages = [new PackageWithNuGetVersion { Id = id, NuGetVersion = NuGetVersion.Parse(v) }], + Packages = packages, LogLevel = LogLevel.Verbose, OutputDirectory = outputDir, }; @@ -238,7 +269,7 @@ [new PackageSource(sourceDir)], // Second run: should short-circuit because already installed var args2 = new PackageDownloadArgs() { - Packages = [new PackageWithNuGetVersion { Id = id, NuGetVersion = NuGetVersion.Parse(v) }], + Packages = packages, OutputDirectory = outputDir, }; @@ -251,10 +282,14 @@ [new PackageSource(sourceDir)], CancellationToken.None); // Assert - second.Should().Be(PackageDownloadRunner.ExitCodeSuccess); - var installDir = Path.Combine(outputDir, id.ToLowerInvariant(), v); - Directory.Exists(installDir).Should().BeTrue(); - File.Exists(Path.Combine(installDir, $"{id.ToLowerInvariant()}.{v}.nupkg")).Should().BeTrue(); + foreach (var package in packages) + { + second.Should().Be(PackageDownloadRunner.ExitCodeSuccess); + var installDir = Path.Combine(outputDir, package.Id.ToLowerInvariant(), package.NuGetVersion.OriginalVersion); + Directory.Exists(installDir).Should().BeTrue(); + File.Exists(Path.Combine(installDir, $"{package.Id.ToLowerInvariant()}.{package.NuGetVersion.OriginalVersion}.nupkg")).Should().BeTrue(); + } + logger.Verify(l => l.LogMinimal(It.Is(s => s.Contains("Skipping", StringComparison.OrdinalIgnoreCase))), Times.AtLeastOnce); } From 203f7fa85b122dba02ab1297c8ae60cee510f6be Mon Sep 17 00:00:00 2001 From: Nigusu Yenework Date: Mon, 27 Oct 2025 14:08:10 -0700 Subject: [PATCH 13/15] more test --- .../Download/PackageDownloadRunnerTests.cs | 112 +++++++++++++----- 1 file changed, 82 insertions(+), 30 deletions(-) diff --git a/test/NuGet.Core.FuncTests/NuGet.XPlat.FuncTest/Package/Download/PackageDownloadRunnerTests.cs b/test/NuGet.Core.FuncTests/NuGet.XPlat.FuncTest/Package/Download/PackageDownloadRunnerTests.cs index 464904985ac..feb6bdfd96e 100644 --- a/test/NuGet.Core.FuncTests/NuGet.XPlat.FuncTest/Package/Download/PackageDownloadRunnerTests.cs +++ b/test/NuGet.Core.FuncTests/NuGet.XPlat.FuncTest/Package/Download/PackageDownloadRunnerTests.cs @@ -206,34 +206,86 @@ public async Task RunAsync_NoVersion_PicksHighestAcrossMultipleSources() public static IEnumerable ShortCircuitsPackageData => [ - [ - new[] { - new PackageWithNuGetVersion() - { - Id = "Contoso.Utils", - NuGetVersion = NuGetVersion.Parse("3.4.5") - } + // single package already installed + [ + new [] + { + ("Contoso.Utils", "3.4.5") // source packages + }, + new[] + { + new PackageWithNuGetVersion // argument packages + { + Id = "Contoso.Utils", + NuGetVersion = NuGetVersion.Parse("3.4.5") } - ], - [ - new[] { - new PackageWithNuGetVersion() - { - Id = "Contoso.Utils", - NuGetVersion = NuGetVersion.Parse("3.4.5") - }, - new PackageWithNuGetVersion() - { - Id = "Contoso.Core", - NuGetVersion = NuGetVersion.Parse("3.0.5") - } + }, + new [] + { + ("Contoso.Utils", "3.4.5") // expected packages + } + ], + + // multiple packages already installed + [ + new [] + { + ("Contoso.Utils", "3.4.5"), // source packages + ("Contoso.Core", "3.0.5") + }, + new [] + { + new PackageWithNuGetVersion // argument packages + { + Id = "Contoso.Utils", + NuGetVersion = NuGetVersion.Parse("3.4.5") + }, + new PackageWithNuGetVersion + { + Id = "Contoso.Core", + NuGetVersion = NuGetVersion.Parse("3.0.5") } - ], - ]; + }, + new [] + { + ("Contoso.Utils", "3.4.5"), // expected packages + ("Contoso.Core", "3.0.5") + } + ], + + // no version specified, but latest version already installed + [ + new [] + { + ("Contoso.Utils", "3.4.5"), // source packages + ("Contoso.Core", "3.0.5") + }, + new [] + { + new PackageWithNuGetVersion // argument packages + { + Id = "Contoso.Utils", + NuGetVersion = null + }, + new PackageWithNuGetVersion + { + Id = "Contoso.Core", + NuGetVersion = null + } + }, + new [] + { + ("Contoso.Utils", "3.4.5"), // expected packages + ("Contoso.Core", "3.0.5") + } + ]]; [Theory] [MemberData(nameof(ShortCircuitsPackageData))] - internal async Task RunAsync_ExplicitVersionAlreadyInstalled_ShortCircuitsAndSucceeds(PackageWithNuGetVersion[] packages) + internal async Task RunAsync_VersionAlreadyInstalled_ShortCircuitsAndSucceeds( + IReadOnlyList<(string, string)> sourcePackages, + PackageWithNuGetVersion[] packageDownloadArgs, + IReadOnlyList<(string, string)> expectedPackages) { // Arrange using var context = new SimpleTestPathContext(); @@ -242,9 +294,9 @@ internal async Task RunAsync_ExplicitVersionAlreadyInstalled_ShortCircuitsAndSuc List packagesToInstall = []; - foreach (var package in packages) + foreach (var (id, version) in sourcePackages) { - await SimpleTestPackageUtility.CreateFullPackageAsync(sourceDir, package.Id, package.NuGetVersion.OriginalVersion); + await SimpleTestPackageUtility.CreateFullPackageAsync(sourceDir, id, version); } var logger = new Mock(MockBehavior.Loose); @@ -253,7 +305,7 @@ internal async Task RunAsync_ExplicitVersionAlreadyInstalled_ShortCircuitsAndSuc // First run: install explicit version var args1 = new PackageDownloadArgs() { - Packages = packages, + Packages = packageDownloadArgs, LogLevel = LogLevel.Verbose, OutputDirectory = outputDir, }; @@ -269,7 +321,7 @@ [new PackageSource(sourceDir)], // Second run: should short-circuit because already installed var args2 = new PackageDownloadArgs() { - Packages = packages, + Packages = packageDownloadArgs, OutputDirectory = outputDir, }; @@ -282,12 +334,12 @@ [new PackageSource(sourceDir)], CancellationToken.None); // Assert - foreach (var package in packages) + foreach (var (id, version) in expectedPackages) { second.Should().Be(PackageDownloadRunner.ExitCodeSuccess); - var installDir = Path.Combine(outputDir, package.Id.ToLowerInvariant(), package.NuGetVersion.OriginalVersion); + var installDir = Path.Combine(outputDir, id, version); Directory.Exists(installDir).Should().BeTrue(); - File.Exists(Path.Combine(installDir, $"{package.Id.ToLowerInvariant()}.{package.NuGetVersion.OriginalVersion}.nupkg")).Should().BeTrue(); + File.Exists(Path.Combine(installDir, $"{id.ToLowerInvariant()}.{version}.nupkg")).Should().BeTrue(); } logger.Verify(l => l.LogMinimal(It.Is(s => s.Contains("Skipping", StringComparison.OrdinalIgnoreCase))), Times.AtLeastOnce); From 0b9925da1edb39a5f84676bc62f567180773e88c Mon Sep 17 00:00:00 2001 From: Nigusu Yenework Date: Mon, 27 Oct 2025 14:57:45 -0700 Subject: [PATCH 14/15] fix --- .../Package/Download/PackageDownloadRunnerTests.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test/NuGet.Core.FuncTests/NuGet.XPlat.FuncTest/Package/Download/PackageDownloadRunnerTests.cs b/test/NuGet.Core.FuncTests/NuGet.XPlat.FuncTest/Package/Download/PackageDownloadRunnerTests.cs index feb6bdfd96e..a09597f4cab 100644 --- a/test/NuGet.Core.FuncTests/NuGet.XPlat.FuncTest/Package/Download/PackageDownloadRunnerTests.cs +++ b/test/NuGet.Core.FuncTests/NuGet.XPlat.FuncTest/Package/Download/PackageDownloadRunnerTests.cs @@ -337,7 +337,7 @@ [new PackageSource(sourceDir)], foreach (var (id, version) in expectedPackages) { second.Should().Be(PackageDownloadRunner.ExitCodeSuccess); - var installDir = Path.Combine(outputDir, id, version); + var installDir = Path.Combine(outputDir, id.ToLowerInvariant(), version); Directory.Exists(installDir).Should().BeTrue(); File.Exists(Path.Combine(installDir, $"{id.ToLowerInvariant()}.{version}.nupkg")).Should().BeTrue(); } From 14332c6173597d9ad7c71292f6c091cf03042d0f Mon Sep 17 00:00:00 2001 From: Nigusu Yenework Date: Mon, 27 Oct 2025 15:33:20 -0700 Subject: [PATCH 15/15] different ids --- .../Download/PackageDownloadRunnerTests.cs | 23 +++++++++++++++++++ 1 file changed, 23 insertions(+) diff --git a/test/NuGet.Core.FuncTests/NuGet.XPlat.FuncTest/Package/Download/PackageDownloadRunnerTests.cs b/test/NuGet.Core.FuncTests/NuGet.XPlat.FuncTest/Package/Download/PackageDownloadRunnerTests.cs index a09597f4cab..c8ceeb8d334 100644 --- a/test/NuGet.Core.FuncTests/NuGet.XPlat.FuncTest/Package/Download/PackageDownloadRunnerTests.cs +++ b/test/NuGet.Core.FuncTests/NuGet.XPlat.FuncTest/Package/Download/PackageDownloadRunnerTests.cs @@ -47,6 +47,29 @@ public static IEnumerable PackageTestData() } // expected }; + // Basic stable explicit versions with different ids + yield return new object[] + { + new List<(string, string)> + { + ("Contoso.Core", "1.0.0"), + ("Contoso.Core.Utils", "1.1.0"), + ("Contoso.Core", "2.0.0-beta") + }, // source packages + new List<(string, string)> + { + ("Contoso.Core.Utils", "1.1.0"), + ("Contoso.Core", "1.0.0"), + }, // argument packages + false, // enablePrerelease + "myOutput", // output directory subpath + new List<(string, string)> + { + ("Contoso.Core.Utils", "1.1.0"), + ("Contoso.Core", "1.0.0") + } // expected + }; + // Mixed casing on the ID in the *download* argument yield return new object[] {