Skip to content

Commit 321fd59

Browse files
committed
Prompt for tool exec only if tool hasn't been downloaded before
1 parent fac59a8 commit 321fd59

36 files changed

+175
-115
lines changed

src/Cli/dotnet/CliStrings.resx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -317,7 +317,7 @@
317317
<value>More than one command is defined for the tool.</value>
318318
</data>
319319
<data name="ToolSettingsUnsupportedRunner" xml:space="preserve">
320-
<value>Command '{0}' uses unsupported runner '{1}'."</value>
320+
<value>Tool '{0}' uses unsupported runner '{1}'."</value>
321321
</data>
322322
<data name="ToolUnsupportedRuntimeIdentifier" xml:space="preserve">
323323
<value>The tool does not support the current architecture or operating system ({0}). Supported runtimes: {1}</value>

src/Cli/dotnet/CommandFactory/CommandResolution/LocalToolsCommandResolver.cs

Lines changed: 3 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33

44
#nullable disable
55

6+
using Microsoft.DotNet.Cli.Commands.Tool;
67
using Microsoft.DotNet.Cli.ToolManifest;
78
using Microsoft.DotNet.Cli.ToolPackage;
89
using Microsoft.DotNet.Cli.Utils;
@@ -91,31 +92,8 @@ private CommandSpec GetPackageCommandSpecUsingMuxer(CommandResolverArguments arg
9192
toolCommandName.ToString()));
9293
}
9394

94-
if (toolCommand.Runner == "dotnet")
95-
{
96-
if (toolManifestPackage.RollForward || allowRollForward)
97-
{
98-
arguments.CommandArguments = ["--allow-roll-forward", .. arguments.CommandArguments];
99-
}
100-
101-
return MuxerCommandSpecMaker.CreatePackageCommandSpecUsingMuxer(
102-
toolCommand.Executable.Value,
103-
arguments.CommandArguments);
104-
}
105-
else if (toolCommand.Runner == "executable")
106-
{
107-
var escapedArgs = ArgumentEscaper.EscapeAndConcatenateArgArrayForProcessStart(
108-
arguments.CommandArguments);
109-
110-
return new CommandSpec(
111-
toolCommand.Executable.Value,
112-
escapedArgs);
113-
}
114-
else
115-
{
116-
throw new GracefulException(string.Format(CliStrings.ToolSettingsUnsupportedRunner,
117-
toolCommand.Name, toolCommand.Runner));
118-
}
95+
return ToolCommandSpecCreator.CreateToolCommandSpec(toolCommand.Name.Value, toolCommand.Executable.Value, toolCommand.Runner,
96+
toolManifestPackage.RollForward || allowRollForward, arguments.CommandArguments);
11997
}
12098
else
12199
{

src/Cli/dotnet/Commands/CliCommandStrings.resx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2486,7 +2486,7 @@ To display a value, specify the corresponding command-line option without provid
24862486
<value>Missing package ID</value>
24872487
</data>
24882488
<data name="ToolRunFromSourceUserConfirmationPrompt" xml:space="preserve">
2489-
<value>Tool not found on the system. Do you want to run it from source? [y/n]</value>
2489+
<value>Tool package {0}@{1} will be downloaded from source {2}. Proceed? [y/n]</value>
24902490
</data>
24912491
<data name="ToolRunFromSourceUserConfirmationFailed" xml:space="preserve">
24922492
<value>Run from source approval denied by the user</value>

src/Cli/dotnet/Commands/Tool/Execute/ToolExecuteCommand.cs

Lines changed: 44 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@
1616

1717
using Microsoft.Extensions.EnvironmentAbstractions;
1818
using NuGet.Common;
19+
using NuGet.Configuration;
1920
using NuGet.Packaging.Core;
2021
using NuGet.Versioning;
2122

@@ -87,40 +88,54 @@ public override int Execute()
8788
}
8889
}
8990

91+
var packageLocation = new PackageLocation(
92+
nugetConfig: _configFile != null ? new(_configFile) : null,
93+
sourceFeedOverrides: _sources,
94+
additionalFeeds: _addSource);
9095

91-
if (!UserAgreedToRunFromSource())
92-
{
93-
throw new GracefulException(CliCommandStrings.ToolRunFromSourceUserConfirmationFailed, isUserError: true);
94-
}
96+
var restoreActionConfig = new RestoreActionConfig(
97+
IgnoreFailedSources: _ignoreFailedSources,
98+
Interactive: _interactive);
9599

96-
if (_allowRollForward)
97-
{
98-
_forwardArguments.Append("--allow-roll-forward");
99-
}
100+
(var bestVersion, var packageSource) = _toolPackageDownloader.GetNuGetVersion(packageLocation, packageId, _verbosity, versionRange, restoreActionConfig);
100101

102+
IToolPackage toolPackage;
101103

102-
string tempDirectory = NuGetEnvironment.GetFolderPath(NuGetFolderPath.Temp);
104+
// TODO: Add framework argument
105+
if (!_toolPackageDownloader.TryGetDownloadedTool(packageId, bestVersion, targetFramework: null, out toolPackage))
106+
{
107+
if (!UserAgreedToRunFromSource(packageId, bestVersion, packageSource))
108+
{
109+
// TODO: Refactor this to print a better message, and probably a different message depending on whether the user selected no
110+
// or whether interactive mode was off
111+
throw new GracefulException(CliCommandStrings.ToolRunFromSourceUserConfirmationFailed, isUserError: true);
112+
}
103113

104-
IToolPackage toolPackage = _toolPackageDownloader.InstallPackage(
105-
new PackageLocation(
114+
// We've already determined which source we will use and displayed that in a confirmation message to the user.
115+
// So set the package location here to override the source feeds to just the source we already resolved to.
116+
// This does mean that we won't work with feeds that have a primary package but where the RID-specific packages are on
117+
// other feeds, but this is probably OK.
118+
var downloadPackageLocation = new PackageLocation(
106119
nugetConfig: _configFile != null ? new(_configFile) : null,
107-
sourceFeedOverrides: _sources,
108-
additionalFeeds: _addSource),
109-
packageId: packageId,
110-
verbosity: _verbosity,
111-
versionRange: versionRange,
112-
isGlobalToolRollForward: _allowRollForward, // Needed to update .runtimeconfig.json
113-
restoreActionConfig: new(
114-
IgnoreFailedSources: _ignoreFailedSources,
115-
Interactive: _interactive));
120+
sourceFeedOverrides: [packageSource.Source],
121+
additionalFeeds: _addSource);
122+
123+
toolPackage = _toolPackageDownloader.InstallPackage(
124+
downloadPackageLocation,
125+
packageId: packageId,
126+
verbosity: _verbosity,
127+
versionRange: new VersionRange(bestVersion, true, bestVersion, true),
128+
isGlobalToolRollForward: false,
129+
restoreActionConfig: restoreActionConfig);
130+
}
116131

117-
CommandSpec commandSpec = MuxerCommandSpecMaker.CreatePackageCommandSpecUsingMuxer(toolPackage.Command.Executable.ToString(), _forwardArguments);
132+
var commandSpec = ToolCommandSpecCreator.CreateToolCommandSpec(toolPackage.Command.Name.Value, toolPackage.Command.Executable.Value, toolPackage.Command.Runner, _allowRollForward, _forwardArguments);
118133
var command = CommandFactoryUsingResolver.Create(commandSpec);
119134
var result = command.Execute();
120135
return result.ExitCode;
121136
}
122137

123-
private bool UserAgreedToRunFromSource()
138+
private bool UserAgreedToRunFromSource(PackageId packageId, NuGetVersion version, PackageSource source)
124139
{
125140
if (_yes)
126141
{
@@ -132,19 +147,22 @@ private bool UserAgreedToRunFromSource()
132147
return false;
133148
}
134149

135-
// TODO: Use a better way to ask for user input
136-
Console.Write(CliCommandStrings.ToolRunFromSourceUserConfirmationPrompt);
150+
// TODO: Use a better way to ask for user input
151+
// TODO: How to localize y/n and interpret keys correctly? Does Spectre.Console handle this?
152+
string promptMessage = string.Format(CliCommandStrings.ToolRunFromSourceUserConfirmationPrompt, packageId, version.ToString(), source.Source);
153+
154+
Console.Write(promptMessage);
137155
bool userAccepted = Console.ReadKey().Key == ConsoleKey.Y;
138156

139157
if (_verbosity >= VerbosityOptions.detailed)
140158
{
141159
Console.WriteLine();
142-
Console.WriteLine(new String('-', CliCommandStrings.ToolRunFromSourceUserConfirmationPrompt.Length));
160+
Console.WriteLine(new String('-', promptMessage.Length));
143161
}
144162
else
145163
{
146164
// Clear the line
147-
Console.Write("\r" + new string(' ', CliCommandStrings.ToolRunFromSourceUserConfirmationPrompt.Length + 1) + "\r");
165+
Console.Write("\r" + new string(' ', promptMessage.Length + 1) + "\r");
148166
}
149167

150168
return userAccepted;

src/Cli/dotnet/Commands/Tool/Execute/ToolExecuteCommandParser.cs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -58,6 +58,8 @@ private static Command ConstructCommand()
5858
command.Options.Add(YesOption);
5959
command.Options.Add(VerbosityOption);
6060

61+
// TODO: Framework?
62+
6163
command.SetAction((parseResult) => new ToolExecuteCommand(parseResult).Execute());
6264

6365
return command;

src/Cli/dotnet/Commands/Tool/Install/ParseResultExtension.cs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -36,6 +36,8 @@ public static VersionRange GetVersionRange(this ParseResult parseResult)
3636

3737
if (prerelease)
3838
{
39+
// TODO: This is probably broken if you try to specify --prerelease together with a version. Should that be allowed?
40+
// It seems like it would make sense to specify a version of something like 2.* and want to allow prerelease versions.
3941
packageVersion = "*-*";
4042
}
4143

Lines changed: 47 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,47 @@
1+
// Licensed to the .NET Foundation under one or more agreements.
2+
// The .NET Foundation licenses this file to you under the MIT license.
3+
4+
using System;
5+
using System.Collections.Generic;
6+
using System.CommandLine;
7+
using System.Text;
8+
using Microsoft.DotNet.Cli.CommandFactory;
9+
using Microsoft.DotNet.Cli.CommandFactory.CommandResolution;
10+
using Microsoft.DotNet.Cli.ToolManifest;
11+
using Microsoft.DotNet.Cli.ToolPackage;
12+
using Microsoft.DotNet.Cli.Utils;
13+
14+
namespace Microsoft.DotNet.Cli.Commands.Tool
15+
{
16+
internal class ToolCommandSpecCreator
17+
{
18+
public static CommandSpec CreateToolCommandSpec(string toolName, string toolExecutable, string toolRunner, bool allowRollForward, IEnumerable<string> commandArguments)
19+
{
20+
if (toolRunner == "dotnet")
21+
{
22+
if (allowRollForward)
23+
{
24+
commandArguments = ["--allow-roll-forward", .. commandArguments];
25+
}
26+
27+
return MuxerCommandSpecMaker.CreatePackageCommandSpecUsingMuxer(
28+
toolExecutable,
29+
commandArguments);
30+
}
31+
else if (toolRunner == "executable")
32+
{
33+
var escapedArgs = ArgumentEscaper.EscapeAndConcatenateArgArrayForProcessStart(
34+
commandArguments);
35+
36+
return new CommandSpec(
37+
toolExecutable,
38+
escapedArgs);
39+
}
40+
else
41+
{
42+
throw new GracefulException(string.Format(CliStrings.ToolSettingsUnsupportedRunner,
43+
toolName, toolRunner));
44+
}
45+
}
46+
}
47+
}

src/Cli/dotnet/Commands/xlf/CliCommandStrings.cs.xlf

Lines changed: 2 additions & 2 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

src/Cli/dotnet/Commands/xlf/CliCommandStrings.de.xlf

Lines changed: 2 additions & 2 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

src/Cli/dotnet/Commands/xlf/CliCommandStrings.es.xlf

Lines changed: 2 additions & 2 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

0 commit comments

Comments
 (0)