diff --git a/src/Cli/dotnet/Commands/CliCommandStrings.resx b/src/Cli/dotnet/Commands/CliCommandStrings.resx index d18c552b8f74..7c44013e6890 100644 --- a/src/Cli/dotnet/Commands/CliCommandStrings.resx +++ b/src/Cli/dotnet/Commands/CliCommandStrings.resx @@ -483,6 +483,13 @@ This is equivalent to deleting project.assets.json. Specifies the version of machine-readable output. Requires the '--format json' option. + + Restore failed. Run `dotnet restore` for more details on the issue. + {Locked="dotnet restore"} + + + Do not restore before running the command. + PACKAGE_NAME diff --git a/src/Cli/dotnet/Commands/Hidden/List/Package/ListPackageCommandParser.cs b/src/Cli/dotnet/Commands/Hidden/List/Package/ListPackageCommandParser.cs index faa7cc354274..c491827a12cd 100644 --- a/src/Cli/dotnet/Commands/Hidden/List/Package/ListPackageCommandParser.cs +++ b/src/Cli/dotnet/Commands/Hidden/List/Package/ListPackageCommandParser.cs @@ -33,6 +33,7 @@ private static CliCommand ConstructCommand() command.Options.Add(PackageListCommandParser.InteractiveOption); command.Options.Add(PackageListCommandParser.FormatOption); command.Options.Add(PackageListCommandParser.OutputVersionOption); + command.Options.Add(PackageListCommandParser.NoRestore); command.SetAction((parseResult) => new PackageListCommand(parseResult).Execute()); diff --git a/src/Cli/dotnet/Commands/Package/List/PackageListCommand.cs b/src/Cli/dotnet/Commands/Package/List/PackageListCommand.cs index 3f8dae9aee19..04bbeffc7493 100644 --- a/src/Cli/dotnet/Commands/Package/List/PackageListCommand.cs +++ b/src/Cli/dotnet/Commands/Package/List/PackageListCommand.cs @@ -6,6 +6,8 @@ using Microsoft.DotNet.Cli.Commands.NuGet; using Microsoft.DotNet.Cli.Extensions; using Microsoft.DotNet.Cli.Utils; +using System.Globalization; +using Microsoft.DotNet.Cli.Commands.MSBuild; namespace Microsoft.DotNet.Cli.Commands.Package.List; @@ -25,7 +27,71 @@ private static string GetAbsolutePath(string currentDirectory, string relativePa public override int Execute() { - return NuGetCommand.Run(TransformArgs()); + string projectFile = GetProjectOrSolution(); + bool noRestore = _parseResult.HasOption(PackageListCommandParser.NoRestore); + int restoreExitCode = 0; + + if (!noRestore) + { + ReportOutputFormat formatOption = _parseResult.GetValue((CliOption)PackageListCommandParser.FormatOption); + bool interactive = _parseResult.GetValue((CliOption)PackageListCommandParser.InteractiveOption); + restoreExitCode = RunRestore(projectFile, formatOption, interactive); + } + + return restoreExitCode == 0 + ? NuGetCommand.Run(TransformArgs(projectFile)) + : restoreExitCode; + } + + private int RunRestore(string projectOrSolution, ReportOutputFormat formatOption, bool interactive) + { + List args = ["-target:Restore", projectOrSolution]; + + if (formatOption == ReportOutputFormat.json) + { + args.Add("-noConsoleLogger"); + } + else + { + args.Add("-consoleLoggerParameters:NoSummary"); + args.Add("-verbosity:minimal"); + } + + args.Add($"-interactive:{interactive.ToString().ToLower()}"); + + MSBuildForwardingApp restoringCommand = new MSBuildForwardingApp(argsToForward: args); + + int exitCode = 0; + + try + { + exitCode = restoringCommand.Execute(); + } + catch (Exception) + { + exitCode = 1; + } + + if (exitCode != 0) + { + if (formatOption == ReportOutputFormat.json) + { + string jsonError = $$""" +{ + "version": 1, + "problems": [ + { + "text": "{{String.Format(CultureInfo.CurrentCulture, CliCommandStrings.Error_restore)}}", + "level": "error" + } + ] +} +"""; + Console.WriteLine(jsonError); + } + } + + return exitCode; } internal static void EnforceOptionRules(ParseResult parseResult) @@ -40,13 +106,13 @@ internal static void EnforceOptionRules(ParseResult parseResult) } } - private string[] TransformArgs() + private string[] TransformArgs(string projectOrSolution) { var args = new List { "package", "list", - GetProjectOrSolution() + projectOrSolution }; args.AddRange(_parseResult.OptionValuesToBeForwarded(PackageListCommandParser.GetCommand())); diff --git a/src/Cli/dotnet/Commands/Package/List/PackageListCommandParser.cs b/src/Cli/dotnet/Commands/Package/List/PackageListCommandParser.cs index 6aaa53d79641..88904d6de15a 100644 --- a/src/Cli/dotnet/Commands/Package/List/PackageListCommandParser.cs +++ b/src/Cli/dotnet/Commands/Package/List/PackageListCommandParser.cs @@ -72,6 +72,12 @@ internal static class PackageListCommandParser public static readonly CliOption InteractiveOption = CommonOptions.InteractiveOption().ForwardIfEnabled("--interactive"); + public static readonly CliOption NoRestore = new CliOption("--no-restore") + { + Description = CliCommandStrings.CmdNoRestoreDescription, + Arity = ArgumentArity.Zero + }; + public static readonly CliOption VerbosityOption = new ForwardedOption("--verbosity", "-v") { Description = CliStrings.VerbosityOptionDescription, @@ -113,6 +119,7 @@ private static CliCommand ConstructCommand() command.Options.Add(InteractiveOption); command.Options.Add(FormatOption); command.Options.Add(OutputVersionOption); + command.Options.Add(NoRestore); command.Options.Add(PackageCommandParser.ProjectOption); command.SetAction((parseResult) => new PackageListCommand(parseResult).Execute()); diff --git a/src/Cli/dotnet/Commands/xlf/CliCommandStrings.cs.xlf b/src/Cli/dotnet/Commands/xlf/CliCommandStrings.cs.xlf index b40213fe208b..28c05970ec5d 100644 --- a/src/Cli/dotnet/Commands/xlf/CliCommandStrings.cs.xlf +++ b/src/Cli/dotnet/Commands/xlf/CliCommandStrings.cs.xlf @@ -561,6 +561,11 @@ This is equivalent to deleting project.assets.json. Specify a project or solution file. The current working directory does not contain a project or solution file. + + Do not restore before running the command. + Do not restore before running the command. + + No test modules found for the given test module pattern: {0} with root directory: {1} No test modules found for the given test module pattern: {0} with root directory: {1} @@ -1061,6 +1066,11 @@ Make the profile names distinct. Make the profile names distinct. + + Restore failed. Run `dotnet restore` for more details on the issue. + Restore failed. Run `dotnet restore` for more details on the issue. + {Locked="dotnet restore"} + Require that the search term exactly match the name of the package. Causes `--take` and `--skip` options to be ignored. Require that the search term exactly match the name of the package. Causes `--take` and `--skip` options to be ignored. diff --git a/src/Cli/dotnet/Commands/xlf/CliCommandStrings.de.xlf b/src/Cli/dotnet/Commands/xlf/CliCommandStrings.de.xlf index 084d78a04136..6934ad07dc21 100644 --- a/src/Cli/dotnet/Commands/xlf/CliCommandStrings.de.xlf +++ b/src/Cli/dotnet/Commands/xlf/CliCommandStrings.de.xlf @@ -561,6 +561,11 @@ This is equivalent to deleting project.assets.json. Specify a project or solution file. The current working directory does not contain a project or solution file. + + Do not restore before running the command. + Do not restore before running the command. + + No test modules found for the given test module pattern: {0} with root directory: {1} No test modules found for the given test module pattern: {0} with root directory: {1} @@ -1061,6 +1066,11 @@ Make the profile names distinct. Make the profile names distinct. + + Restore failed. Run `dotnet restore` for more details on the issue. + Restore failed. Run `dotnet restore` for more details on the issue. + {Locked="dotnet restore"} + Require that the search term exactly match the name of the package. Causes `--take` and `--skip` options to be ignored. Require that the search term exactly match the name of the package. Causes `--take` and `--skip` options to be ignored. diff --git a/src/Cli/dotnet/Commands/xlf/CliCommandStrings.es.xlf b/src/Cli/dotnet/Commands/xlf/CliCommandStrings.es.xlf index b52aee8f86dc..986040944a9a 100644 --- a/src/Cli/dotnet/Commands/xlf/CliCommandStrings.es.xlf +++ b/src/Cli/dotnet/Commands/xlf/CliCommandStrings.es.xlf @@ -561,6 +561,11 @@ This is equivalent to deleting project.assets.json. Specify a project or solution file. The current working directory does not contain a project or solution file. + + Do not restore before running the command. + Do not restore before running the command. + + No test modules found for the given test module pattern: {0} with root directory: {1} No test modules found for the given test module pattern: {0} with root directory: {1} @@ -1061,6 +1066,11 @@ Make the profile names distinct. Make the profile names distinct. + + Restore failed. Run `dotnet restore` for more details on the issue. + Restore failed. Run `dotnet restore` for more details on the issue. + {Locked="dotnet restore"} + Require that the search term exactly match the name of the package. Causes `--take` and `--skip` options to be ignored. Require that the search term exactly match the name of the package. Causes `--take` and `--skip` options to be ignored. diff --git a/src/Cli/dotnet/Commands/xlf/CliCommandStrings.fr.xlf b/src/Cli/dotnet/Commands/xlf/CliCommandStrings.fr.xlf index 15a358999121..699e27cf1172 100644 --- a/src/Cli/dotnet/Commands/xlf/CliCommandStrings.fr.xlf +++ b/src/Cli/dotnet/Commands/xlf/CliCommandStrings.fr.xlf @@ -561,6 +561,11 @@ This is equivalent to deleting project.assets.json. Specify a project or solution file. The current working directory does not contain a project or solution file. + + Do not restore before running the command. + Do not restore before running the command. + + No test modules found for the given test module pattern: {0} with root directory: {1} No test modules found for the given test module pattern: {0} with root directory: {1} @@ -1061,6 +1066,11 @@ Make the profile names distinct. Make the profile names distinct. + + Restore failed. Run `dotnet restore` for more details on the issue. + Restore failed. Run `dotnet restore` for more details on the issue. + {Locked="dotnet restore"} + Require that the search term exactly match the name of the package. Causes `--take` and `--skip` options to be ignored. Require that the search term exactly match the name of the package. Causes `--take` and `--skip` options to be ignored. diff --git a/src/Cli/dotnet/Commands/xlf/CliCommandStrings.it.xlf b/src/Cli/dotnet/Commands/xlf/CliCommandStrings.it.xlf index 89c2748bdcb5..2e5547ff20df 100644 --- a/src/Cli/dotnet/Commands/xlf/CliCommandStrings.it.xlf +++ b/src/Cli/dotnet/Commands/xlf/CliCommandStrings.it.xlf @@ -561,6 +561,11 @@ This is equivalent to deleting project.assets.json. Specify a project or solution file. The current working directory does not contain a project or solution file. + + Do not restore before running the command. + Do not restore before running the command. + + No test modules found for the given test module pattern: {0} with root directory: {1} No test modules found for the given test module pattern: {0} with root directory: {1} @@ -1061,6 +1066,11 @@ Make the profile names distinct. Make the profile names distinct. + + Restore failed. Run `dotnet restore` for more details on the issue. + Restore failed. Run `dotnet restore` for more details on the issue. + {Locked="dotnet restore"} + Require that the search term exactly match the name of the package. Causes `--take` and `--skip` options to be ignored. Require that the search term exactly match the name of the package. Causes `--take` and `--skip` options to be ignored. diff --git a/src/Cli/dotnet/Commands/xlf/CliCommandStrings.ja.xlf b/src/Cli/dotnet/Commands/xlf/CliCommandStrings.ja.xlf index 101a5ec62e38..195f49f4dbf7 100644 --- a/src/Cli/dotnet/Commands/xlf/CliCommandStrings.ja.xlf +++ b/src/Cli/dotnet/Commands/xlf/CliCommandStrings.ja.xlf @@ -561,6 +561,11 @@ This is equivalent to deleting project.assets.json. Specify a project or solution file. The current working directory does not contain a project or solution file. + + Do not restore before running the command. + Do not restore before running the command. + + No test modules found for the given test module pattern: {0} with root directory: {1} No test modules found for the given test module pattern: {0} with root directory: {1} @@ -1061,6 +1066,11 @@ Make the profile names distinct. Make the profile names distinct. + + Restore failed. Run `dotnet restore` for more details on the issue. + Restore failed. Run `dotnet restore` for more details on the issue. + {Locked="dotnet restore"} + Require that the search term exactly match the name of the package. Causes `--take` and `--skip` options to be ignored. Require that the search term exactly match the name of the package. Causes `--take` and `--skip` options to be ignored. diff --git a/src/Cli/dotnet/Commands/xlf/CliCommandStrings.ko.xlf b/src/Cli/dotnet/Commands/xlf/CliCommandStrings.ko.xlf index f9ae5c299e5b..ada8d1ee5139 100644 --- a/src/Cli/dotnet/Commands/xlf/CliCommandStrings.ko.xlf +++ b/src/Cli/dotnet/Commands/xlf/CliCommandStrings.ko.xlf @@ -561,6 +561,11 @@ This is equivalent to deleting project.assets.json. Specify a project or solution file. The current working directory does not contain a project or solution file. + + Do not restore before running the command. + Do not restore before running the command. + + No test modules found for the given test module pattern: {0} with root directory: {1} No test modules found for the given test module pattern: {0} with root directory: {1} @@ -1061,6 +1066,11 @@ Make the profile names distinct. Make the profile names distinct. + + Restore failed. Run `dotnet restore` for more details on the issue. + Restore failed. Run `dotnet restore` for more details on the issue. + {Locked="dotnet restore"} + Require that the search term exactly match the name of the package. Causes `--take` and `--skip` options to be ignored. Require that the search term exactly match the name of the package. Causes `--take` and `--skip` options to be ignored. diff --git a/src/Cli/dotnet/Commands/xlf/CliCommandStrings.pl.xlf b/src/Cli/dotnet/Commands/xlf/CliCommandStrings.pl.xlf index 33642773ddff..d6df366369c5 100644 --- a/src/Cli/dotnet/Commands/xlf/CliCommandStrings.pl.xlf +++ b/src/Cli/dotnet/Commands/xlf/CliCommandStrings.pl.xlf @@ -561,6 +561,11 @@ This is equivalent to deleting project.assets.json. Specify a project or solution file. The current working directory does not contain a project or solution file. + + Do not restore before running the command. + Do not restore before running the command. + + No test modules found for the given test module pattern: {0} with root directory: {1} No test modules found for the given test module pattern: {0} with root directory: {1} @@ -1061,6 +1066,11 @@ Make the profile names distinct. Make the profile names distinct. + + Restore failed. Run `dotnet restore` for more details on the issue. + Restore failed. Run `dotnet restore` for more details on the issue. + {Locked="dotnet restore"} + Require that the search term exactly match the name of the package. Causes `--take` and `--skip` options to be ignored. Require that the search term exactly match the name of the package. Causes `--take` and `--skip` options to be ignored. diff --git a/src/Cli/dotnet/Commands/xlf/CliCommandStrings.pt-BR.xlf b/src/Cli/dotnet/Commands/xlf/CliCommandStrings.pt-BR.xlf index 5e076eb7a4f4..d90cb2c746d3 100644 --- a/src/Cli/dotnet/Commands/xlf/CliCommandStrings.pt-BR.xlf +++ b/src/Cli/dotnet/Commands/xlf/CliCommandStrings.pt-BR.xlf @@ -561,6 +561,11 @@ This is equivalent to deleting project.assets.json. Specify a project or solution file. The current working directory does not contain a project or solution file. + + Do not restore before running the command. + Do not restore before running the command. + + No test modules found for the given test module pattern: {0} with root directory: {1} No test modules found for the given test module pattern: {0} with root directory: {1} @@ -1061,6 +1066,11 @@ Make the profile names distinct. Make the profile names distinct. + + Restore failed. Run `dotnet restore` for more details on the issue. + Restore failed. Run `dotnet restore` for more details on the issue. + {Locked="dotnet restore"} + Require that the search term exactly match the name of the package. Causes `--take` and `--skip` options to be ignored. Require that the search term exactly match the name of the package. Causes `--take` and `--skip` options to be ignored. diff --git a/src/Cli/dotnet/Commands/xlf/CliCommandStrings.ru.xlf b/src/Cli/dotnet/Commands/xlf/CliCommandStrings.ru.xlf index 79dee0e34032..5105335f3ce0 100644 --- a/src/Cli/dotnet/Commands/xlf/CliCommandStrings.ru.xlf +++ b/src/Cli/dotnet/Commands/xlf/CliCommandStrings.ru.xlf @@ -561,6 +561,11 @@ This is equivalent to deleting project.assets.json. Specify a project or solution file. The current working directory does not contain a project or solution file. + + Do not restore before running the command. + Do not restore before running the command. + + No test modules found for the given test module pattern: {0} with root directory: {1} No test modules found for the given test module pattern: {0} with root directory: {1} @@ -1061,6 +1066,11 @@ Make the profile names distinct. Make the profile names distinct. + + Restore failed. Run `dotnet restore` for more details on the issue. + Restore failed. Run `dotnet restore` for more details on the issue. + {Locked="dotnet restore"} + Require that the search term exactly match the name of the package. Causes `--take` and `--skip` options to be ignored. Require that the search term exactly match the name of the package. Causes `--take` and `--skip` options to be ignored. diff --git a/src/Cli/dotnet/Commands/xlf/CliCommandStrings.tr.xlf b/src/Cli/dotnet/Commands/xlf/CliCommandStrings.tr.xlf index b88fbaf94bbe..b4cf621b2847 100644 --- a/src/Cli/dotnet/Commands/xlf/CliCommandStrings.tr.xlf +++ b/src/Cli/dotnet/Commands/xlf/CliCommandStrings.tr.xlf @@ -561,6 +561,11 @@ This is equivalent to deleting project.assets.json. Specify a project or solution file. The current working directory does not contain a project or solution file. + + Do not restore before running the command. + Do not restore before running the command. + + No test modules found for the given test module pattern: {0} with root directory: {1} No test modules found for the given test module pattern: {0} with root directory: {1} @@ -1061,6 +1066,11 @@ Make the profile names distinct. Make the profile names distinct. + + Restore failed. Run `dotnet restore` for more details on the issue. + Restore failed. Run `dotnet restore` for more details on the issue. + {Locked="dotnet restore"} + Require that the search term exactly match the name of the package. Causes `--take` and `--skip` options to be ignored. Require that the search term exactly match the name of the package. Causes `--take` and `--skip` options to be ignored. diff --git a/src/Cli/dotnet/Commands/xlf/CliCommandStrings.zh-Hans.xlf b/src/Cli/dotnet/Commands/xlf/CliCommandStrings.zh-Hans.xlf index d5a469a80b1a..bf305c614a41 100644 --- a/src/Cli/dotnet/Commands/xlf/CliCommandStrings.zh-Hans.xlf +++ b/src/Cli/dotnet/Commands/xlf/CliCommandStrings.zh-Hans.xlf @@ -561,6 +561,11 @@ This is equivalent to deleting project.assets.json. Specify a project or solution file. The current working directory does not contain a project or solution file. + + Do not restore before running the command. + Do not restore before running the command. + + No test modules found for the given test module pattern: {0} with root directory: {1} No test modules found for the given test module pattern: {0} with root directory: {1} @@ -1061,6 +1066,11 @@ Make the profile names distinct. Make the profile names distinct. + + Restore failed. Run `dotnet restore` for more details on the issue. + Restore failed. Run `dotnet restore` for more details on the issue. + {Locked="dotnet restore"} + Require that the search term exactly match the name of the package. Causes `--take` and `--skip` options to be ignored. Require that the search term exactly match the name of the package. Causes `--take` and `--skip` options to be ignored. diff --git a/src/Cli/dotnet/Commands/xlf/CliCommandStrings.zh-Hant.xlf b/src/Cli/dotnet/Commands/xlf/CliCommandStrings.zh-Hant.xlf index a8ac9272ab9d..97dc514087ec 100644 --- a/src/Cli/dotnet/Commands/xlf/CliCommandStrings.zh-Hant.xlf +++ b/src/Cli/dotnet/Commands/xlf/CliCommandStrings.zh-Hant.xlf @@ -561,6 +561,11 @@ This is equivalent to deleting project.assets.json. Specify a project or solution file. The current working directory does not contain a project or solution file. + + Do not restore before running the command. + Do not restore before running the command. + + No test modules found for the given test module pattern: {0} with root directory: {1} No test modules found for the given test module pattern: {0} with root directory: {1} @@ -1061,6 +1066,11 @@ Make the profile names distinct. Make the profile names distinct. + + Restore failed. Run `dotnet restore` for more details on the issue. + Restore failed. Run `dotnet restore` for more details on the issue. + {Locked="dotnet restore"} + Require that the search term exactly match the name of the package. Causes `--take` and `--skip` options to be ignored. Require that the search term exactly match the name of the package. Causes `--take` and `--skip` options to be ignored. diff --git a/test/Microsoft.NET.TestFramework/Commands/PackageListCommand.cs b/test/Microsoft.NET.TestFramework/Commands/PackageListCommand.cs new file mode 100644 index 000000000000..b99407ed4072 --- /dev/null +++ b/test/Microsoft.NET.TestFramework/Commands/PackageListCommand.cs @@ -0,0 +1,35 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using Microsoft.DotNet.Cli.Utils; + +namespace Microsoft.NET.TestFramework.Commands +{ + public class PackageListCommand : DotnetCommand + { + private string? _projectName = null; + + public PackageListCommand(ITestOutputHelper log, params string[] args) : base(log, args) + { + } + + public override CommandResult Execute(IEnumerable args) + { + List newArgs = ["package", "list"]; + if (!string.IsNullOrEmpty(_projectName)) + { + newArgs.Add("--project"); + newArgs.Add(_projectName); + } + newArgs.AddRange(args); + + return base.Execute(newArgs); + } + + public PackageListCommand WithProject(string projectName) + { + _projectName = projectName; + return this; + } + } +} diff --git a/test/dotnet-completions.Tests/snapshots/bash/DotnetCliSnapshotTests.VerifyCompletions.verified.sh b/test/dotnet-completions.Tests/snapshots/bash/DotnetCliSnapshotTests.VerifyCompletions.verified.sh index 9a4a003fe1d0..44dc106b1abb 100644 --- a/test/dotnet-completions.Tests/snapshots/bash/DotnetCliSnapshotTests.VerifyCompletions.verified.sh +++ b/test/dotnet-completions.Tests/snapshots/bash/DotnetCliSnapshotTests.VerifyCompletions.verified.sh @@ -1083,7 +1083,7 @@ _testhost_package_list() { prev="${COMP_WORDS[COMP_CWORD-1]}" COMPREPLY=() - opts="--verbosity --outdated --deprecated --vulnerable --framework --include-transitive --include-prerelease --highest-patch --highest-minor --config --source --interactive --format --output-version --project --help" + opts="--verbosity --outdated --deprecated --vulnerable --framework --include-transitive --include-prerelease --highest-patch --highest-minor --config --source --interactive --format --output-version --no-restore --project --help" if [[ $COMP_CWORD == "$1" ]]; then COMPREPLY=( $(compgen -W "$opts" -- "$cur") ) @@ -2224,4 +2224,4 @@ _testhost_completions_script() { -complete -F _testhost testhost \ No newline at end of file +complete -F _testhost testhost diff --git a/test/dotnet-completions.Tests/snapshots/pwsh/DotnetCliSnapshotTests.VerifyCompletions.verified.ps1 b/test/dotnet-completions.Tests/snapshots/pwsh/DotnetCliSnapshotTests.VerifyCompletions.verified.ps1 index b8ba20263713..5433aad3fe93 100644 --- a/test/dotnet-completions.Tests/snapshots/pwsh/DotnetCliSnapshotTests.VerifyCompletions.verified.ps1 +++ b/test/dotnet-completions.Tests/snapshots/pwsh/DotnetCliSnapshotTests.VerifyCompletions.verified.ps1 @@ -636,6 +636,7 @@ Register-ArgumentCompleter -Native -CommandName 'testhost' -ScriptBlock { [CompletionResult]::new('--interactive', '--interactive', [CompletionResultType]::ParameterName, "Allows the command to stop and wait for user input or action (for example to complete authentication).") [CompletionResult]::new('--format', '--format', [CompletionResultType]::ParameterName, "Specifies the output format type for the list packages command.") [CompletionResult]::new('--output-version', '--output-version', [CompletionResultType]::ParameterName, "Specifies the version of machine-readable output. Requires the `'--format json`' option.") + [CompletionResult]::new('--no-restore', '--no-restore', [CompletionResultType]::ParameterName, "Do not restore before running the command.") [CompletionResult]::new('--project', '--project', [CompletionResultType]::ParameterName, "The project file to operate on. If a file is not specified, the command will search the current directory for one.") [CompletionResult]::new('--help', '--help', [CompletionResultType]::ParameterName, "Show command line help.") [CompletionResult]::new('--help', '-h', [CompletionResultType]::ParameterName, "Show command line help.") diff --git a/test/dotnet-completions.Tests/snapshots/zsh/DotnetCliSnapshotTests.VerifyCompletions.verified.zsh b/test/dotnet-completions.Tests/snapshots/zsh/DotnetCliSnapshotTests.VerifyCompletions.verified.zsh index 451a9b00d108..5795644bd951 100644 --- a/test/dotnet-completions.Tests/snapshots/zsh/DotnetCliSnapshotTests.VerifyCompletions.verified.zsh +++ b/test/dotnet-completions.Tests/snapshots/zsh/DotnetCliSnapshotTests.VerifyCompletions.verified.zsh @@ -629,6 +629,7 @@ _testhost() { '--interactive[Allows the command to stop and wait for user input or action (for example to complete authentication).]' \ '--format=[Specifies the output format type for the list packages command.]: :((console\:"console" json\:"json" ))' \ '--output-version=[Specifies the version of machine-readable output. Requires the '\''--format json'\'' option.]: : ' \ + '--no-restore[Do not restore before running the command.]' \ '--project=[The project file to operate on. If a file is not specified, the command will search the current directory for one.]: : ' \ '--help[Show command line help.]' \ '-h[Show command line help.]' \ diff --git a/test/dotnet-list-package.Tests/GivenDotnetListPackage.cs b/test/dotnet-list-package.Tests/GivenDotnetListPackage.cs index 4e0be90d99ca..bef15fa249b4 100644 --- a/test/dotnet-list-package.Tests/GivenDotnetListPackage.cs +++ b/test/dotnet-list-package.Tests/GivenDotnetListPackage.cs @@ -139,12 +139,30 @@ public void AssetsPathExistsButNotRestored() new ListPackageCommand(Log) .WithWorkingDirectory(projectDirectory) - .Execute() + .Execute("--no-restore") .Should() .Fail() .And.HaveStdErr(); } + [Fact] + public void RestoresAndLists() + { + var testAsset = "NewtonSoftDependentProject"; + var projectDirectory = _testAssetsManager + .CopyTestAsset(testAsset) + .WithSource() + .Path; + + new ListPackageCommand(Log) + .WithWorkingDirectory(projectDirectory) + .Execute() + .Should() + .Pass() + .And.HaveStdOut() + .And.HaveStdOutContaining("NewtonSoft.Json"); + } + [Fact] public void ItListsTransitivePackage() { @@ -325,7 +343,7 @@ public void ItListsFSharpProject() public void ItEnforcesOptionRules(bool throws, params string[] options) { var parseResult = Parser.Instance.Parse($"dotnet list package {string.Join(' ', options)}"); - Action checkRules = () => PackageListCommand.EnforceOptionRules(parseResult); + Action checkRules = () => Microsoft.DotNet.Cli.Commands.Package.List.PackageListCommand.EnforceOptionRules(parseResult); if (throws) { diff --git a/test/dotnet-pacakge-list/GivenDotnetPackageList.cs b/test/dotnet-pacakge-list/GivenDotnetPackageList.cs new file mode 100644 index 000000000000..54c06652ec0f --- /dev/null +++ b/test/dotnet-pacakge-list/GivenDotnetPackageList.cs @@ -0,0 +1,460 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +#nullable disable + +using Microsoft.DotNet.Cli.Utils; +using Microsoft.DotNet.Tools.Package.List; + +namespace Microsoft.DotNet.Cli.Commands.Package.List.Tests +{ + public class GivenDotnetPackageList : SdkTest + { + public GivenDotnetPackageList(ITestOutputHelper output) : base(output) + { + } + + [Fact] + public void ItShowsCoreOutputOnMinimalVerbosity() + { + var testAssetName = "NewtonSoftDependentProject"; + var testAsset = _testAssetsManager + .CopyTestAsset(testAssetName) + .WithSource(); + var projectDirectory = testAsset.Path; + + new RestoreCommand(testAsset) + .Execute() + .Should() + .Pass() + .And.NotHaveStdErr(); + + new PackageListCommand(Log) + .WithWorkingDirectory(projectDirectory) + .Execute("--verbosity", "quiet", "--no-restore") + .Should() + .Pass() + .And.NotHaveStdErr() + .And.HaveStdOutContaining("NewtonSoft.Json"); + } + + [Fact] + public void RequestedAndResolvedVersionsMatch() + { + var testAssetName = "TestAppSimple"; + var testAsset = _testAssetsManager + .CopyTestAsset(testAssetName) + .WithSource(); + + var projectDirectory = testAsset.Path; + + var packageName = "Newtonsoft.Json"; + var packageVersion = ToolsetInfo.GetNewtonsoftJsonPackageVersion(); + var cmd = new DotnetCommand(Log) + .WithWorkingDirectory(projectDirectory) + .Execute("add", "package", packageName, "--version", packageVersion); + cmd.Should().Pass(); + + new RestoreCommand(testAsset) + .Execute() + .Should() + .Pass() + .And.NotHaveStdErr(); + + new PackageListCommand(Log) + .WithWorkingDirectory(projectDirectory) + .Execute("--no-restore") + .Should() + .Pass() + .And.NotHaveStdErr() + .And.HaveStdOutContainingIgnoreSpaces(packageName + packageVersion + packageVersion); + } + + [Fact] + public void ItListsAutoReferencedPackages() + { + var testAssetName = "TestAppSimple"; + var testAsset = _testAssetsManager + .CopyTestAsset(testAssetName) + .WithSource() + .WithProjectChanges(ChangeTargetFrameworkTo2_1); + var projectDirectory = testAsset.Path; + + new RestoreCommand(testAsset) + .Execute() + .Should() + .Pass() + .And.NotHaveStdErr(); + + new PackageListCommand(Log) + .WithWorkingDirectory(projectDirectory) + .Execute("--no-restore") + .Should() + .Pass() + .And.NotHaveStdErr() + .And.HaveStdOutContainingIgnoreSpaces("Microsoft.NETCore.App(A)") + .And.HaveStdOutContainingIgnoreSpaces("(A):Auto-referencedpackage"); + + static void ChangeTargetFrameworkTo2_1(XDocument project) + { + project.Descendants() + .Single(e => e.Name.LocalName == "TargetFramework") + .Value = "netcoreapp2.1"; + } + } + + [Fact] + public void ItRunOnSolution() + { + var sln = "TestAppWithSlnAndSolutionFolders"; + var testAsset = _testAssetsManager + .CopyTestAsset(sln) + .WithSource(); + var projectDirectory = testAsset.Path; + + new RestoreCommand(testAsset, "App.sln") + .Execute() + .Should() + .Pass() + .And.NotHaveStdErr(); + + new PackageListCommand(Log) + .WithProject("App.sln") + .WithWorkingDirectory(projectDirectory) + .Execute("--no-restore") + .Should() + .Pass() + .And.NotHaveStdErr() + .And.HaveStdOutContainingIgnoreSpaces("NewtonSoft.Json"); + } + + [Fact] + public void AssetsPathExistsButNotRestored() + { + var testAsset = "NewtonSoftDependentProject"; + var projectDirectory = _testAssetsManager + .CopyTestAsset(testAsset) + .WithSource() + .Path; + + new PackageListCommand(Log) + .WithWorkingDirectory(projectDirectory) + .Execute("--no-restore") + .Should() + .Fail() + .And.HaveStdErr(); + } + + + [Fact] + public void RestoresAndLists() + { + var testAsset = "NewtonSoftDependentProject"; + var projectDirectory = _testAssetsManager + .CopyTestAsset(testAsset) + .WithSource() + .Path; + + new PackageListCommand(Log) + .WithWorkingDirectory(projectDirectory) + .Execute() + .Should() + .Pass() + .And.HaveStdOut() + .And.HaveStdOutContaining("NewtonSoft.Json"); + } + + [Fact] + public void ItListsTransitivePackage() + { + var testProject = new TestProject + { + Name = "NewtonSoftDependentProject", + TargetFrameworks = ToolsetInfo.CurrentTargetFramework, + IsExe = true, + SourceFiles = + { +["Program.cs"] = @" +using System; +using System.Collections; +using Newtonsoft.Json.Linq; + +class Program +{ + public static void Main(string[] args) + { + ArrayList argList = new ArrayList(args); + JObject jObject = new JObject(); + + foreach (string arg in argList) + { + jObject[arg] = arg; + } + Console.WriteLine(jObject.ToString()); + } +} +", + } + }; + + testProject.PackageReferences.Add(new TestPackageReference("NewtonSoft.Json", "9.0.1")); + + // Disable package pruning so that there are still transitive dependencies to test the command + testProject.AdditionalProperties["RestoreEnablePackagePruning"] = "false"; + + var testAsset = _testAssetsManager.CreateTestProject(testProject); + var projectDirectory = Path.Combine(testAsset.Path, testProject.Name); + + new RestoreCommand(testAsset) + .Execute() + .Should() + .Pass() + .And.NotHaveStdErr(); + + new PackageListCommand(Log) + .WithWorkingDirectory(projectDirectory) + .Execute("--no-restore") + .Should() + .Pass() + .And.NotHaveStdErr() + .And.NotHaveStdOutContaining("System.IO.FileSystem"); + + new PackageListCommand(Log) + .WithWorkingDirectory(projectDirectory) + .Execute(args: ["--include-transitive", "--no-restore"]) + .Should() + .Pass() + .And.NotHaveStdErr() + .And.HaveStdOutContaining("System.IO.FileSystem"); + } + + [Theory] + [InlineData("", "[net451]", null)] + [InlineData("", $"[{ToolsetInfo.CurrentTargetFramework}]", null)] + [InlineData($"--framework {ToolsetInfo.CurrentTargetFramework} --framework net451", "[net451]", null)] + [InlineData($"--framework {ToolsetInfo.CurrentTargetFramework} --framework net451", $"[{ToolsetInfo.CurrentTargetFramework}]", null)] + [InlineData($"--framework {ToolsetInfo.CurrentTargetFramework}", $"[{ToolsetInfo.CurrentTargetFramework}]", "[net451]")] + [InlineData("--framework net451", "[net451]", "[netcoreapp3.0]")] + [InlineData($"-f {ToolsetInfo.CurrentTargetFramework} -f net451", "[net451]", null)] + [InlineData($"-f {ToolsetInfo.CurrentTargetFramework} -f net451", $"[{ToolsetInfo.CurrentTargetFramework}]", null)] + [InlineData($"-f {ToolsetInfo.CurrentTargetFramework}", $"[{ToolsetInfo.CurrentTargetFramework}]", "[net451]")] + [InlineData("-f net451", "[net451]", "[netcoreapp3.0]")] + public void ItListsValidFrameworks(string args, string shouldInclude, string shouldntInclude) + { + var testAssetName = "MSBuildAppWithMultipleFrameworks"; + var testAsset = _testAssetsManager + .CopyTestAsset(testAssetName, identifier: args.GetHashCode().ToString() + shouldInclude) + .WithSource(); + var projectDirectory = testAsset.Path; + + new RestoreCommand(testAsset) + .Execute() + .Should() + .Pass() + .And.NotHaveStdErr(); + + if (shouldntInclude == null) + { + new PackageListCommand(Log) + .WithWorkingDirectory(projectDirectory) + .Execute(args.Split(' ', options: StringSplitOptions.RemoveEmptyEntries)) + .Should() + .Pass() + .And.NotHaveStdErr() + .And.HaveStdOutContainingIgnoreSpaces(shouldInclude.Replace(" ", "")); + } + else + { + new PackageListCommand(Log) + .WithWorkingDirectory(projectDirectory) + .Execute(args.Split(' ', options: StringSplitOptions.RemoveEmptyEntries)) + .Should() + .Pass() + .And.NotHaveStdErr() + .And.HaveStdOutContainingIgnoreSpaces(shouldInclude.Replace(" ", "")) + .And.NotHaveStdOutContaining(shouldntInclude.Replace(" ", "")); + } + + } + + [Fact] + public void ItDoesNotAcceptInvalidFramework() + { + var testAssetName = "MSBuildAppWithMultipleFrameworks"; + var testAsset = _testAssetsManager + .CopyTestAsset(testAssetName) + .WithSource(); + var projectDirectory = testAsset.Path; + + new RestoreCommand(testAsset) + .Execute() + .Should() + .Pass(); + + new PackageListCommand(Log) + .WithWorkingDirectory(projectDirectory) + .Execute("--framework", "invalid") + .Should() + .Fail(); + } + + [FullMSBuildOnlyFact] + public void ItListsFSharpProject() + { + var testAssetName = "FSharpTestAppSimple"; + var testAsset = _testAssetsManager + .CopyTestAsset(testAssetName) + .WithSource(); + var projectDirectory = testAsset.Path; + + new RestoreCommand(testAsset) + .Execute() + .Should() + .Pass() + .And.NotHaveStdErr(); + + new PackageListCommand(Log) + .WithWorkingDirectory(projectDirectory) + .Execute() + .Should() + .Pass() + .And.NotHaveStdErr(); + } + + [Theory] + [InlineData(false, "--no-restore")] + [InlineData(false, "--vulnerable")] + [InlineData(false, "--no-restore", "--include-transitive")] + [InlineData(false, "--no-restore", "--include-prerelease")] + [InlineData(false, "--no-restore", "--deprecated")] + [InlineData(false, "--no-restore", "--outdated")] + [InlineData(false, "--no-restore", "--vulnerable")] + [InlineData(false, "--vulnerable", "--include-transitive")] + [InlineData(false, "--vulnerable", "--include-prerelease")] + [InlineData(false, "--deprecated", "--highest-minor")] + [InlineData(false, "--deprecated", "--highest-patch")] + [InlineData(false, "--outdated", "--include-prerelease")] + [InlineData(false, "--outdated", "--highest-minor")] + [InlineData(false, "--outdated", "--highest-patch")] + [InlineData(false, "--config")] + [InlineData(false, "--configfile")] + [InlineData(false, "--source")] + [InlineData(false, "-s")] + [InlineData(false, "--config", "--deprecated")] + [InlineData(false, "--configfile", "--deprecated")] + [InlineData(false, "--source", "--vulnerable")] + [InlineData(false, "-s", "--vulnerable")] + [InlineData(true, "--vulnerable", "--deprecated")] + [InlineData(true, "--vulnerable", "--outdated")] + [InlineData(true, "--deprecated", "--outdated")] + public void ItEnforcesOptionRules(bool throws, params string[] options) + { + var parseResult = Parser.Instance.Parse($"dotnet package list {string.Join(' ', options)}"); + Action checkRules = () => ListPackageReferencesCommand.EnforceOptionRules(parseResult); + + if (throws) + { + Assert.Throws(checkRules); + } + else + { + checkRules(); // Test for no throw + } + } + + [UnixOnlyFact] + public void ItRunsInCurrentDirectoryWithPoundInPath() + { + // Regression test for https://github.com/dotnet/sdk/issues/19654 + var testAssetName = "TestAppSimple"; + var testAsset = _testAssetsManager + .CopyTestAsset(testAssetName, "C#") + .WithSource(); + var projectDirectory = testAsset.Path; + + new RestoreCommand(testAsset) + .Execute() + .Should() + .Pass(); + + new PackageListCommand(Log) + .WithWorkingDirectory(projectDirectory) + .Execute() + .Should() + .Pass(); + } + + [Fact] + public void ItRecognizesRelativePathsForAProject() + { + var testAssetName = "TestAppSimple"; + var testAsset = _testAssetsManager + .CopyTestAsset(testAssetName) + .WithSource(); + + var projectDirectory = testAsset.Path; + + new RestoreCommand(testAsset) + .Execute() + .Should() + .Pass(); + + new PackageListCommand(Log) + .WithProject("TestAppSimple.csproj") + .WithWorkingDirectory(projectDirectory) + .Execute() + .Should() + .Pass(); + } + + [Fact] + public void ItRecognizesRelativePathsForASolution() + { + var sln = "TestAppWithSlnAndSolutionFolders"; + var testAsset = _testAssetsManager + .CopyTestAsset(sln) + .WithSource(); + + var projectDirectory = testAsset.Path; + + new RestoreCommand(testAsset, "App.sln") + .Execute() + .Should() + .Pass(); + + new PackageListCommand(Log) + .WithProject("App.sln") + .WithWorkingDirectory(projectDirectory) + .Execute() + .Should() + .Pass(); + } + + [Fact] + public void ItRecognizesRelativePathsForASolutionFromSubFolder() + { + var sln = "TestAppWithSlnAndSolutionFolders"; + var testAsset = _testAssetsManager + .CopyTestAsset(sln) + .WithSource(); + + var projectDirectory = testAsset.Path; + + string subFolderName = "subFolder"; + var subFolderPath = Path.Combine(projectDirectory, subFolderName); + Directory.CreateDirectory(subFolderPath); + + new RestoreCommand(testAsset, "App.sln") + .Execute() + .Should() + .Pass(); + + new PackageListCommand(Log) + .WithProject("../App.sln") + .WithWorkingDirectory(subFolderPath) + .Execute() + .Should() + .Pass(); + } + } +} diff --git a/test/dotnet.Tests/dotnet.Tests.csproj b/test/dotnet.Tests/dotnet.Tests.csproj index b242b6f20c07..00427dd42924 100644 --- a/test/dotnet.Tests/dotnet.Tests.csproj +++ b/test/dotnet.Tests/dotnet.Tests.csproj @@ -39,6 +39,7 @@ +