Skip to content

Add dotnet package update command #49287

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 3 commits into from
Jun 11, 2025
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 4 additions & 1 deletion src/Cli/dotnet/Parser.cs
Original file line number Diff line number Diff line change
Expand Up @@ -119,7 +119,7 @@ public static class Parser
// Argument
public static readonly Argument<string> DotnetSubCommand = new("subcommand") { Arity = ArgumentArity.ZeroOrOne, Hidden = true };

private static Command ConfigureCommandLine(Command rootCommand)
private static Command ConfigureCommandLine(RootCommand rootCommand)
{
for (int i = rootCommand.Options.Count - 1; i >= 0; i--)
{
Expand Down Expand Up @@ -156,6 +156,9 @@ private static Command ConfigureCommandLine(Command rootCommand)
// Add argument
rootCommand.Arguments.Add(DotnetSubCommand);

// NuGet implements several commands in its own repo. Add them to the .NET SDK via the provided API.
NuGet.CommandLine.XPlat.NuGetCommands.Add(rootCommand);

rootCommand.SetAction(parseResult =>
{
if (parseResult.GetValue(DiagOption) && parseResult.Tokens.Count == 1)
Expand Down
47 changes: 47 additions & 0 deletions test/dotnet.Tests/CommandTests/NuGet/GivenANuGetCommand.cs
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,8 @@
using Microsoft.DotNet.Cli;
using Microsoft.DotNet.Cli.Commands.NuGet;
using Moq;
using Newtonsoft.Json.Linq;
using NuGet.Versioning;

namespace Microsoft.DotNet.Tools.Run.Tests
{
Expand Down Expand Up @@ -133,5 +135,50 @@ public void ItHasAWhySubcommand()
.And.NotHaveStdErr()
.And.HaveStdOutContaining("has the following dependency");
}

[Fact]
public void ItCanUpdatePackages()
{
// Arrange
var testAssetName = "TestAppSimple";
var testAsset = _testAssetsManager
.CopyTestAsset(testAssetName)
.WithSource();
var projectDirectory = testAsset.Path;

NuGetConfigWriter.Write(projectDirectory, TestContext.Current.TestPackages);

new DotnetCommand(Log, "package", "add", "dotnet-hello@1.0.0")
.WithWorkingDirectory(projectDirectory)
.Execute()
.Should()
.Pass()
.And.NotHaveStdErr();

// Act
var commandResult = new DotnetCommand(Log, "package", "update", "dotnet-hello")
.WithWorkingDirectory(projectDirectory)
.Execute()
.Should()
.Pass()
.And.NotHaveStdErr();

// Assert
var listPackageCommandResult = new DotnetCommand(Log, "package", "list", "--format", "json")
.WithWorkingDirectory(projectDirectory)
.Execute();
listPackageCommandResult.Should()
.Pass()
.And.NotHaveStdErr();

var updatedPackageVersionString = JObject.Parse(listPackageCommandResult.StdOut)
.SelectToken("$.projects[0].frameworks[0].topLevelPackages[?(@.id == 'dotnet-hello')].requestedVersion")
.ToString();

var v1 = NuGetVersion.Parse("1.0.0");
var updatedVersion = NuGetVersion.Parse(updatedPackageVersionString);

updatedVersion.Should().BeGreaterThan(v1);
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -1006,7 +1006,7 @@ _testhost_package() {
prev="${COMP_WORDS[COMP_CWORD-1]}"
COMPREPLY=()

opts="search add list remove --help"
opts="search add list remove update --help"

if [[ $COMP_CWORD == "$1" ]]; then
COMPREPLY=( $(compgen -W "$opts" -- "$cur") )
Expand Down Expand Up @@ -1034,6 +1034,11 @@ _testhost_package() {
return
;;

(update)
_testhost_package_update $(($1+1))
return
;;

esac

COMPREPLY=( $(compgen -W "$opts" -- "$cur") )
Expand Down Expand Up @@ -1126,6 +1131,23 @@ _testhost_package_remove() {
}


_testhost_package_update() {

cur="${COMP_WORDS[COMP_CWORD]}"
prev="${COMP_WORDS[COMP_CWORD-1]}"
COMPREPLY=()

opts="--project --help"

if [[ $COMP_CWORD == "$1" ]]; then
COMPREPLY=( $(compgen -W "$opts" -- "$cur") )
return
fi

COMPREPLY=( $(compgen -W "$opts" -- "$cur") )
}


_testhost_project() {

cur="${COMP_WORDS[COMP_CWORD]}"
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -573,6 +573,7 @@ Register-ArgumentCompleter -Native -CommandName 'testhost' -ScriptBlock {
[CompletionResult]::new('add', 'add', [CompletionResultType]::ParameterValue, "Add a NuGet package reference to the project.")
[CompletionResult]::new('list', 'list', [CompletionResultType]::ParameterValue, "List all package references of the project or solution.")
[CompletionResult]::new('remove', 'remove', [CompletionResultType]::ParameterValue, "Remove a NuGet package reference from the project.")
[CompletionResult]::new('update', 'update', [CompletionResultType]::ParameterValue, "Update referenced packages in a project or solution.")
)
$completions += $staticCompletions
break
Expand Down Expand Up @@ -656,6 +657,15 @@ Register-ArgumentCompleter -Native -CommandName 'testhost' -ScriptBlock {
$completions += $staticCompletions
break
}
'testhost;package;update' {
$staticCompletions = @(
[CompletionResult]::new('--project', '--project', [CompletionResultType]::ParameterName, "Path to a project or solution file, or a directory.")
[CompletionResult]::new('--help', '--help', [CompletionResultType]::ParameterName, "Show command line help.")
[CompletionResult]::new('--help', '-h', [CompletionResultType]::ParameterName, "Show command line help.")
)
$completions += $staticCompletions
break
}
'testhost;project' {
$staticCompletions = @(
[CompletionResult]::new('--help', '--help', [CompletionResultType]::ParameterName, "Show command line help.")
Expand Down
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Azure DevOps' test view on the CI build isn't showing the failed test, but looking at the Helix console log, I see this, but only on Windows, so I think the test is passing on Linux and Mac:

Received: DotnetCliSnapshotTests.VerifyCompletions.received.zsh
Verified: DotnetCliSnapshotTests.VerifyCompletions.verified.zsh
Compare Result:
0846                         '-r=[The target runtime to run for.]:RUNTIME_IDENTIFIER:->dotnet_dynamic_complete' \
[xUnit.net 00:02:55.86]          -                         '--project=[The path to the project file to run (defaults to the current directory if there is only one project).]: : ' \
[xUnit.net 00:02:55.86]          -                         '--launch-profile=[The name of the launch profile (if any) to use when launching the application.]: : ' \
[xUnit.net 00:02:55.86]          -                         '-lp=[The name of the launch profile (if any) to use when launching the application.]: : ' \
[xUnit.net 00:02:55.86]          +                         '--project=[The path to the project file to run (defaults to the current directory if there is only one project).]:project: ' \
[xUnit.net 00:02:55.86]          +                         '--launch-profile=[The name of the launch profile (if any) to use when launching the application.]:launch-profile: ' \
[xUnit.net 00:02:55.86]          +                         '-lp=[The name of the launch profile (if any) to use when launching the application.]:launch-profile: ' \
[xUnit.net 00:02:55.86]       0850                         '--no-launch-profile[Do not attempt to use launchSettings.json to configure the application.]' \

Reforatting the diff:

'-r=[The target runtime to run for.]:RUNTIME_IDENTIFIER:->dotnet_dynamic_complete' \
-                         '--project=[The path to the project file to run (defaults to the current directory if there is only one project).]: : ' \
-                         '--launch-profile=[The name of the launch profile (if any) to use when launching the application.]: : ' \
-                         '-lp=[The name of the launch profile (if any) to use when launching the application.]: : ' \
+                         '--project=[The path to the project file to run (defaults to the current directory if there is only one project).]:project: ' \
+                         '--launch-profile=[The name of the launch profile (if any) to use when launching the application.]:launch-profile: ' \
+                         '-lp=[The name of the launch profile (if any) to use when launching the application.]:launch-profile: ' \
'--no-launch-profile[Do not attempt to use launchSettings.json to configure the application.]' \

However, we can see that this pull request is not modifying lines 846 to 850.

Additionally, when I run the test locally, I can't reproduce the failure, but this test keeps failing on CI, even after multiple retries.

Any ideas, @baronfel ?

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

we've got a known issue for this specifically - several of us have hit this same issue. I have no rationale for why this happens.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Sorry, I didn't think to search for a known issue first:

Original file line number Diff line number Diff line change
Expand Up @@ -646,6 +646,14 @@ _testhost() {
'*::PACKAGE_NAME -- The package reference to remove.: ' \
&& ret=0
;;
(update)
_arguments "${_arguments_options[@]}" : \
'--project=[Path to a project or solution file, or a directory.]: : ' \
'--help[Show command line help.]' \
'-h[Show command line help.]' \
'*::packages: ' \
&& ret=0
;;
esac
;;
esac
Expand Down Expand Up @@ -1640,6 +1648,7 @@ _testhost__package_commands() {
'add:Add a NuGet package reference to the project.' \
'list:List all package references of the project or solution.' \
'remove:Remove a NuGet package reference from the project.' \
'update:Update referenced packages in a project or solution.' \
)
_describe -t commands 'testhost package commands' commands "$@"
}
Expand Down Expand Up @@ -1668,6 +1677,12 @@ _testhost__package__remove_commands() {
_describe -t commands 'testhost package remove commands' commands "$@"
}

(( $+functions[_testhost__package__update_commands] )) ||
_testhost__package__update_commands() {
local commands; commands=()
_describe -t commands 'testhost package update commands' commands "$@"
}

(( $+functions[_testhost__project_commands] )) ||
_testhost__project_commands() {
local commands; commands=(
Expand Down
Loading