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 @@
+