-
Notifications
You must be signed in to change notification settings - Fork 1.1k
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
[Issue #37237 #19650] Dotnet Cli Feature: dotnet tool update --all
Updating all tools with one command
#38996
[Issue #37237 #19650] Dotnet Cli Feature: dotnet tool update --all
Updating all tools with one command
#38996
Conversation
dotnet tool update --all
dotnet tool update --all
Would it be less ugly if you changed the arity of PackageIdArgument to ArgumentArity.ZeroOrOne and added a validator (CliArgument.Validators or CliCommand.Validators)? I imagine the validator could then flag an error if neither PackageIdArgument nor |
I was talking about this with @JL03-Yue on Friday I think - the way we thought about approaching it was to model |
Thanks! That's certainly a very doable way: let me try this and update this PR |
Huh… this seems like it could affect CompletionItem.Kind and syntax highlighting if a Language Server Protocol for System.CommandLine is ever implemented. |
@Markliniubility Thank you so much for your contribution. For the subcommand for |
@JL03-Yue Thank you for your help all the way through! I couldn't do this without your help and you have been so knowledgable! I have made the changes into subcommands and now it follows the intended behaviors. I have also added come shared options into this new command such that users can specify things for I will finish unit-test writing by the end of this weekend. Is there any other testings we need besides unit testing? |
Done. One caveat is that user must use |
dotnet tool update --all
dotnet tool update --all
Updating all tools with one command
Would it allow |
Thank you for your input! You have been an dotnet and System.Commandline experts and I really appreciate this piece of advise. However, I don't think this is a good method as this overcomplicates a thing that can be solved easily by renaming. Many options here are used across many commands in dotnet cli. Massively marking them as |
all is already the name of a NuGet package; apparently not a .NET tool package, though. (It has no dependencies either, unlike the NPM "everything" package.) |
@JL03-Yue Thanks for updated change. I have added the unit testings for the local and global install with two packages. |
Thank you very much for your contribution. This is very impressive and we will take actions on it shortly. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Thank you very much for the contribution! The work looks great and it passes manual tests with some small adjustments. Overall it looks amazing and I'm very grateful for this new feature.
[Fact] | ||
public void GivenAnExistedLowerVersionInstallationItCanUpdateAllThePackageVersion() | ||
{ | ||
CreateInstallCommand($"-g {_packageId} --version {LowerPackageVersion}").Execute(); | ||
CreateInstallCommand($"-g {_packageId2} --version {LowerPackageVersion}", _packageId2.ToString()).Execute(); | ||
|
||
CreateUpdateCommand($"--all -g -v:d").Execute(); | ||
|
||
_store.EnumeratePackageVersions(_packageId).Single().Version.ToFullString().Should() | ||
.Be(HigherPackageVersion); | ||
_store.EnumeratePackageVersions(_packageId2).Single().Version.ToFullString().Should() | ||
.Be(HigherPackageVersion); | ||
} | ||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Thanks for the clear and comprehensive tests in global update @Markliniubility.
public void WhenRunWithUpdateAllItShouldUpdateFromManifestFile() | ||
{ | ||
_toolRestoreCommand.Execute(); | ||
new ToolRestoreCommand( | ||
Parser.Instance.Parse($"dotnet tool update {_packageIdB.ToString()} --local"), | ||
_toolPackageDownloaderMock, | ||
_toolManifestFinder, | ||
_localToolsResolverCache, | ||
_fileSystem, | ||
_reporter | ||
).Execute(); | ||
|
||
_mockFeed.Packages[0].Version = _packageNewVersionA.ToNormalizedString(); | ||
_mockFeed.Packages[1].Version = _packageNewVersionA.ToNormalizedString(); | ||
|
||
_toolUpdateAllLocalCommand.Execute().Should().Be(0); | ||
|
||
AssertUpdateSuccess(packageIdExpected: _packageIdA.ToString()); | ||
AssertUpdateSuccess(packageIdExpected: _packageIdB.ToString()); | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Thanks for the clear and comprehensive tests in local install 😃
|
||
public static readonly CliOption<VerbosityOptions> VerbosityOption = ToolInstallCommandParser.VerbosityOption; | ||
public static readonly CliOption<bool> UpdateAllOption = ToolAppliedOption.UpdateAllOption; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Thanks for the consolidation of UpdateAllOption
in ToolAppliedOption
. I personally think it's a great idea since the -all
option might applies to other command as well.
src/Cli/dotnet/commands/dotnet-tool/install/ToolInstallLocalCommand.cs
Outdated
Show resolved
Hide resolved
src/Cli/dotnet/commands/dotnet-tool/install/ToolInstallLocalCommand.cs
Outdated
Show resolved
Hide resolved
src/Cli/dotnet/commands/dotnet-tool/install/ToolInstallLocalCommand.cs
Outdated
Show resolved
Hide resolved
src/Cli/dotnet/commands/dotnet-tool/install/ToolInstallGlobalOrToolPathCommand.cs
Show resolved
Hide resolved
…mmand.cs Co-authored-by: JL03-Yue <[email protected]>
…mmand.cs Co-authored-by: JL03-Yue <[email protected]>
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Thanks a bunch for this! I have a bit of feedback but this looks close to ready.
@@ -47,18 +50,22 @@ internal class ToolInstallGlobalOrToolPathCommand : CommandBase | |||
private IEnumerable<string> _forwardRestoreArguments; | |||
private readonly bool _allowRollForward; | |||
private readonly bool _allowPackageDowngrade; | |||
private readonly bool _all; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Naming nit: I think this would be cliearer as _updateAll
.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Naming nit: I think this would be cliearer as
_updateAll
.
Done
|
||
private readonly string _explicitManifestFile; | ||
private readonly bool _createManifestIfNeeded; | ||
private readonly bool _allowRollForward; | ||
private readonly bool _all; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Same nit as earlier, I think this would be clearer named _updateAll
.
} | ||
else | ||
{ | ||
return ExecuteInstallCommand((PackageId) _packageId); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
What happens if you don't pass in either a package ID or the --all
option? We should generate an error, but I didn't notice where we're checking for it, so in that case I think we might get a NullReferenceException or similar here with this cast.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Thanks for the note!! Added two tests cases on this in ToolUpdateCommandTests.cs
WhenRunWithoutAllOrPackageIdShowErrorMessage
WhenRunWithBothAllAndPackageIdShowErrorMessage
var toolDownloadedPackage = _toolLocalPackageInstaller.Install(manifestFile); | ||
if (!packageId.Equals(_packageId)) | ||
{ | ||
_toolLocalPackageInstaller = new ToolInstallLocalInstaller(_parseResult, packageId, _toolPackageDownloader); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Rather than recreating the ToolInstallLocalInstaller if the package ID doesn't match, could we instead have the ToolInstallLocalInstaller.Install
command take the package ID as an argument?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Unfortunately, this may be a bug for ToolInstallLocalInstaller imo, without newing a new ToolInstallLocalInstaller, the old ToolInstallLocalInstaller will always install the old package even though you passed in a new package ID in ToolInstallLocalInstaller.Install
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'm suggesting we could change ToolInstallLocallInstaller.Install
so that it uses a parameter passed in instead of the _packageId
field, similar to the way ToolInstallGlobalOrToolPathCommand.ExecuteInstallCommand
now works.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'm suggesting we could change
ToolInstallLocallInstaller.Install
so that it uses a parameter passed in instead of the_packageId
field, similar to the wayToolInstallGlobalOrToolPathCommand.ExecuteInstallCommand
now works.
Sorry for the misread. Done!
@@ -22,12 +22,13 @@ internal class ToolUpdateGlobalOrToolPathCommand : CommandBase | |||
{ | |||
private readonly CreateShellShimRepository _createShellShimRepository; | |||
private readonly CreateToolPackageStoresAndDownloaderAndUninstaller _createToolPackageStoreDownloaderUninstaller; | |||
private readonly ToolInstallGlobalOrToolPathCommand _toolInstallGlobalOrToolPathCommand; | |||
private readonly Lazy<ToolInstallGlobalOrToolPathCommand> _toolInstallGlobalOrToolPathCommand; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
What's the reason for changing this to be lazily initialized?
I see that ToolUpdateLocalCommand uses a similar pattern, but I'm not sure the reason for that. It doesn't seem to be necessary, but maybe I'm missing something, or maybe there used to be a reason but the code changed since then.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Thanks for noticing this! I checked the git blame for the Local counterpart. The commit was in 2019 and there was no signficiant reasons for writing it into Lazy
as the code was initially written into Lazy
rather than changed to Lazy
.
Therefore, I removed the Lazy
initialization. Thanks again!
src/Tests/dotnet.Tests/CommandTests/ToolUpdateLocalCommandTests.cs
Outdated
Show resolved
Hide resolved
…s.cs Co-authored-by: Daniel Plaisted <[email protected]>
@@ -50,7 +50,7 @@ internal class ToolInstallGlobalOrToolPathCommand : CommandBase | |||
private IEnumerable<string> _forwardRestoreArguments; | |||
private readonly bool _allowRollForward; | |||
private readonly bool _allowPackageDowngrade; | |||
private readonly bool _all; | |||
private readonly bool _updateAll; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Done
@JL03-Yue @dsplaisted Hi! Thank you all for reviewing and helping on this PR. Is there anything else needed for this PR prior to LGTM & approval for merging? |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Looking good, a few more comments, plus:
What happens if you run dotnet tool install --all
? The update and install command share code and basically work the same, but this feels like maybe it should be an error. We should decide what we want the behavior to be and add a test to cover it.
@@ -19,18 +19,20 @@ internal class ToolInstallLocalInstaller | |||
|
|||
private readonly IToolPackageStore _toolPackageStore; | |||
private readonly IToolPackageDownloader _toolPackageDownloader; | |||
private readonly PackageId _packageId; | |||
private readonly PackageId? _packageId; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think we can remove the _packageId
field from this class entirely, now that we are passing it as a parameter to the Install
method instead.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Done.
src/Cli/dotnet/commands/dotnet-tool/install/ToolInstallLocalInstaller.cs
Outdated
Show resolved
Hide resolved
…staller.cs Co-authored-by: Daniel Plaisted <[email protected]>
@dsplaisted. There is no change for running |
@Markliniubility Thanks a lot for your work on this! |
#10130 #37237 #19650
Cr. @JL03-Yue who wrote the design and lots of proof-of-concept code
This is a prototype for dotnet tool update. The feature is usable however there are several concerns to be resolved.
update
The
update
command has a required argumentpackageId
(i.e.dotnet tool update --all
is syntaxically not allow by the Cli Parser). There can be ways to hack out the requirement (which is used in this PR) but the hack makes exception handling very ugly, and should not be a long term solution.Possible solution: we can have a new subcommand like
dotnet tool update-all
which is seperate fromupdate
This prototype is calling ToolUpdateCommand multiple times by manually constructing a new ParseResult for each tool in
dotnet tool list
. Constructing new ParseResult involves with some minor string parsing and can be potentially slow; however,update --all
is not a latency sensitive feature. I think we can have this simple version first as a slow feature is better than no feature.Moreover, the bottleneck of performance is download/internet speed rather than the parsing of some really short strings. Performance improvement requires a lot of refactoring and can take a very long time (e.g. extract some static install and uninstall functions from all these non-static install and uninstall functions).
Since this is a prototype open to discussion, I just manually test it without formal written tests
Demo