Skip to content

Commit 2cc1c05

Browse files
authored
Do not go beyond the root when searching for a previously installed tool Fixes #40655 (#40800)
Fixes #40655 dotnet tool install currently looks for a previously installed tools manifest. It reads manifests it finds in looking for one that has the manifest it's trying to install but does not check whether that manifest 'isRoot', which means it keeps going outside the root. We don't use a tool beyond the root, which means we can't install then use a tool as would be expected.
1 parent 58d1cf7 commit 2cc1c05

File tree

9 files changed

+74
-18
lines changed

9 files changed

+74
-18
lines changed

src/Cli/Microsoft.DotNet.Cli.Utils/Command.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -26,7 +26,7 @@ public Command(Process process, bool trimtrailingNewlines = false)
2626

2727
public CommandResult Execute()
2828
{
29-
return Execute(_ => { });
29+
return Execute(null);
3030
}
3131
public CommandResult Execute(Action<Process> processStarted)
3232
{

src/Cli/dotnet/ToolManifest/ToolManifestFinder.cs

Lines changed: 16 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -249,18 +249,32 @@ public IReadOnlyList<FilePath> FindByPackageId(PackageId packageId)
249249
{
250250
var result = new List<FilePath>();
251251
bool findAnyManifest = false;
252+
DirectoryPath? rootPath = null;
252253
foreach ((FilePath possibleManifest,
253254
DirectoryPath correspondingDirectory)
254255
in EnumerateDefaultAllPossibleManifests())
255256
{
257+
if (rootPath is not null)
258+
{
259+
if (!correspondingDirectory.Value.Equals(rootPath.Value))
260+
{
261+
break;
262+
}
263+
}
264+
256265
if (_fileSystem.File.Exists(possibleManifest.Value))
257266
{
258267
findAnyManifest = true;
259-
if (_toolManifestEditor.Read(possibleManifest, correspondingDirectory).content
260-
.Any(t => t.PackageId.Equals(packageId)))
268+
(List<ToolManifestPackage> content, bool isRoot) = _toolManifestEditor.Read(possibleManifest, correspondingDirectory);
269+
if (content.Any(t => t.PackageId.Equals(packageId)))
261270
{
262271
result.Add(possibleManifest);
263272
}
273+
274+
if (isRoot)
275+
{
276+
rootPath = correspondingDirectory;
277+
}
264278
}
265279
}
266280

src/Cli/dotnet/ToolPackage/ToolPackageFactory.cs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -13,10 +13,10 @@ namespace Microsoft.DotNet.ToolPackage
1313
internal static class ToolPackageFactory
1414
{
1515
public static (IToolPackageStore, IToolPackageStoreQuery, IToolPackageDownloader) CreateToolPackageStoresAndDownloader(
16-
DirectoryPath? nonGlobalLocation = null, IEnumerable<string> additionalRestoreArguments = null)
16+
DirectoryPath? nonGlobalLocation = null, IEnumerable<string> additionalRestoreArguments = null, string runtimeJsonPathForTests = null)
1717
{
1818
ToolPackageStoreAndQuery toolPackageStore = CreateConcreteToolPackageStore(nonGlobalLocation);
19-
var toolPackageDownloader = new ToolPackageDownloader(toolPackageStore);
19+
var toolPackageDownloader = new ToolPackageDownloader(toolPackageStore, runtimeJsonPathForTests);
2020

2121
return (toolPackageStore, toolPackageStore, toolPackageDownloader);
2222
}

src/Cli/dotnet/commands/dotnet-tool/install/ToolInstallLocalCommand.cs

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -37,7 +37,8 @@ public ToolInstallLocalCommand(
3737
IToolManifestFinder toolManifestFinder = null,
3838
IToolManifestEditor toolManifestEditor = null,
3939
ILocalToolsResolverCache localToolsResolverCache = null,
40-
IReporter reporter = null
40+
IReporter reporter = null,
41+
string runtimeJsonPathForTests = null
4142
)
4243
: base(parseResult)
4344
{
@@ -54,7 +55,7 @@ public ToolInstallLocalCommand(
5455
new ToolManifestFinder(new DirectoryPath(Directory.GetCurrentDirectory()));
5556
_toolManifestEditor = toolManifestEditor ?? new ToolManifestEditor();
5657
_localToolsResolverCache = localToolsResolverCache ?? new LocalToolsResolverCache();
57-
_toolLocalPackageInstaller = new ToolInstallLocalInstaller(parseResult, toolPackageDownloader);
58+
_toolLocalPackageInstaller = new ToolInstallLocalInstaller(parseResult, toolPackageDownloader, runtimeJsonPathForTests);
5859
_toolPackageDownloader = toolPackageDownloader;
5960
_allowRollForward = parseResult.GetValue(ToolInstallCommandParser.RollForwardOption);
6061
_allowPackageDowngrade = parseResult.GetValue(ToolInstallCommandParser.AllowPackageDowngradeOption);

src/Cli/dotnet/commands/dotnet-tool/install/ToolInstallLocalInstaller.cs

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -26,7 +26,8 @@ internal class ToolInstallLocalInstaller
2626

2727
public ToolInstallLocalInstaller(
2828
ParseResult parseResult,
29-
IToolPackageDownloader toolPackageDownloader = null)
29+
IToolPackageDownloader toolPackageDownloader = null,
30+
string runtimeJsonPathForTests = null)
3031
{
3132
_parseResult = parseResult;
3233
_packageVersion = parseResult.GetValue(ToolInstallCommandParser.VersionOption);
@@ -38,7 +39,7 @@ public ToolInstallLocalInstaller(
3839
IToolPackageStoreQuery,
3940
IToolPackageDownloader downloader) toolPackageStoresAndDownloader
4041
= ToolPackageFactory.CreateToolPackageStoresAndDownloader(
41-
additionalRestoreArguments: parseResult.OptionValuesToBeForwarded(ToolInstallCommandParser.GetCommand()));
42+
additionalRestoreArguments: parseResult.OptionValuesToBeForwarded(ToolInstallCommandParser.GetCommand()), runtimeJsonPathForTests: runtimeJsonPathForTests);
4243
_toolPackageStore = toolPackageStoresAndDownloader.store;
4344
_toolPackageDownloader = toolPackageDownloader ?? toolPackageStoresAndDownloader.downloader;
4445

src/Cli/dotnet/commands/dotnet-tool/run/ToolRunCommand.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -39,7 +39,7 @@ public override int Execute()
3939
CommandName = $"dotnet-{_toolCommandName}",
4040
CommandArguments = _forwardArgument,
4141

42-
}, _allowRollForward); ;
42+
}, _allowRollForward);
4343

4444
if (commandspec == null)
4545
{

test/Microsoft.NET.Build.Containers.IntegrationTests/ToolsetUtils.cs

Lines changed: 1 addition & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -11,13 +11,7 @@ internal static class ToolsetUtils
1111
/// <returns></returns>
1212
internal static string GetRuntimeGraphFilePath()
1313
{
14-
string dotnetRoot = TestContext.Current.ToolsetUnderTest.DotNetRoot;
15-
16-
DirectoryInfo sdksDir = new(Path.Combine(dotnetRoot, "sdk"));
17-
18-
var lastWrittenSdk = sdksDir.EnumerateDirectories().OrderByDescending(di => di.LastWriteTime).First();
19-
20-
return lastWrittenSdk.GetFiles("RuntimeIdentifierGraph.json").Single().FullName;
14+
return TestContext.GetRuntimeGraphFilePath();
2115
}
2216

2317
internal static IManifestPicker RidGraphManifestPicker { get; } = new RidGraphManifestPicker(GetRuntimeGraphFilePath());

test/Microsoft.NET.TestFramework/TestContext.cs

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -51,6 +51,17 @@ public static TestContext Current
5151

5252
public const string LatestRuntimePatchForNetCoreApp2_0 = "2.0.9";
5353

54+
public static string GetRuntimeGraphFilePath()
55+
{
56+
string dotnetRoot = TestContext.Current.ToolsetUnderTest.DotNetRoot;
57+
58+
DirectoryInfo sdksDir = new(Path.Combine(dotnetRoot, "sdk"));
59+
60+
var lastWrittenSdk = sdksDir.EnumerateDirectories().OrderByDescending(di => di.LastWriteTime).First();
61+
62+
return lastWrittenSdk.GetFiles("RuntimeIdentifierGraph.json").Single().FullName;
63+
}
64+
5465
public void AddTestEnvironmentVariables(IDictionary<string, string> environment)
5566
{
5667
environment["DOTNET_MULTILEVEL_LOOKUP"] = "0";

test/dotnet.Tests/CommandTests/ToolInstallCommandTests.cs

Lines changed: 36 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,17 +1,25 @@
11
// Licensed to the .NET Foundation under one or more agreements.
22
// The .NET Foundation licenses this file to you under the MIT license.
33

4+
using System.IO;
5+
using System.Linq;
6+
using System.Reflection;
47
using Microsoft.DotNet.Cli.Utils;
58
using Microsoft.DotNet.Tools.Tool.Install;
9+
using Microsoft.DotNet.Tools.Tool.Run;
610
using LocalizableStrings = Microsoft.DotNet.Tools.Tool.Install.LocalizableStrings;
711
using Parser = Microsoft.DotNet.Cli.Parser;
812

913
namespace Microsoft.DotNet.Tests.Commands.Tool
1014
{
11-
public class ToolInstallCommandTests
15+
public class ToolInstallCommandTests : SdkTest
1216
{
1317
private const string PackageId = "global.tool.console.demo";
1418

19+
public ToolInstallCommandTests(ITestOutputHelper log) : base(log)
20+
{
21+
}
22+
1523
[Fact]
1624
public void WhenRunWithBothGlobalAndToolPathShowErrorMessage()
1725
{
@@ -28,6 +36,33 @@ public void WhenRunWithBothGlobalAndToolPathShowErrorMessage()
2836
"--global --tool-path"));
2937
}
3038

39+
[Fact]
40+
public void WhenRunWithRoot()
41+
{
42+
Directory.CreateDirectory("/tmp/folder/sub");
43+
var directory = Directory.GetCurrentDirectory();
44+
var ridGraphPath = TestContext.GetRuntimeGraphFilePath();
45+
try
46+
{
47+
Directory.SetCurrentDirectory("/tmp/folder");
48+
49+
new DotnetNewCommand(Log, "tool-manifest").WithCustomHive("/tmp/folder").WithWorkingDirectory("/tmp/folder").Execute().Should().Pass();
50+
var parseResult = Parser.Instance.Parse("tool install dotnetsay");
51+
new ToolInstallLocalCommand(parseResult, runtimeJsonPathForTests: ridGraphPath).Execute().Should().Be(0);
52+
53+
Directory.SetCurrentDirectory("/tmp/folder/sub");
54+
new DotnetNewCommand(Log, "tool-manifest").WithCustomHive("/tmp/folder/sub").WithWorkingDirectory("/tmp/folder/sub").Execute().Should().Pass();
55+
parseResult = Parser.Instance.Parse("tool install dotnetsay");
56+
new ToolInstallLocalCommand(parseResult, runtimeJsonPathForTests: ridGraphPath).Execute().Should().Be(0);
57+
58+
new ToolRunCommand(Parser.Instance.Parse($"tool run dotnetsay")).Execute().Should().Be(0);
59+
}
60+
finally
61+
{
62+
Directory.SetCurrentDirectory(directory);
63+
}
64+
}
65+
3166
[Fact]
3267
public void WhenRunWithBothGlobalAndLocalShowErrorMessage()
3368
{

0 commit comments

Comments
 (0)