Skip to content

Commit eaf74a0

Browse files
Copilotmitchdenny
andcommitted
Move SDK installation logic and change path from runtimes to sdks
- Move GetNuGetConfigPathsAsync call from CheckAsync to InstallAsync - Initialize NuGet configuration once after SDK installation completes - Change path from $HOME/.aspire/runtimes/dotnet to $HOME/.aspire/sdks/dotnet - Rename RuntimesDirectory to SdksDirectory throughout codebase - Update GetRuntimesDirectory() to GetSdksDirectory() - Update all test files to use sdks directory - Update CacheCommand to clear sdks directory instead of runtimes This addresses two feedback items: 1. NuGet initialization now happens once during installation (not every check) 2. SDK path changed to more appropriate "sdks" directory name Co-authored-by: mitchdenny <[email protected]>
1 parent e23dc73 commit eaf74a0

File tree

8 files changed

+72
-72
lines changed

8 files changed

+72
-72
lines changed

src/Aspire.Cli/CliExecutionContext.cs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -5,12 +5,12 @@
55

66
namespace Aspire.Cli;
77

8-
internal sealed class CliExecutionContext(DirectoryInfo workingDirectory, DirectoryInfo hivesDirectory, DirectoryInfo cacheDirectory, DirectoryInfo runtimesDirectory, bool debugMode = false)
8+
internal sealed class CliExecutionContext(DirectoryInfo workingDirectory, DirectoryInfo hivesDirectory, DirectoryInfo cacheDirectory, DirectoryInfo sdksDirectory, bool debugMode = false)
99
{
1010
public DirectoryInfo WorkingDirectory { get; } = workingDirectory;
1111
public DirectoryInfo HivesDirectory { get; } = hivesDirectory;
1212
public DirectoryInfo CacheDirectory { get; } = cacheDirectory;
13-
public DirectoryInfo RuntimesDirectory { get; } = runtimesDirectory;
13+
public DirectoryInfo SdksDirectory { get; } = sdksDirectory;
1414
public bool DebugMode { get; } = debugMode;
1515

1616
private BaseCommand? _command;

src/Aspire.Cli/Commands/CacheCommand.cs

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -78,11 +78,11 @@ protected override Task<int> ExecuteAsync(ParseResult parseResult, CancellationT
7878
}
7979
}
8080

81-
// Also clear the runtimes directory
82-
var runtimesDirectory = ExecutionContext.RuntimesDirectory;
83-
if (runtimesDirectory.Exists)
81+
// Also clear the sdks directory
82+
var sdksDirectory = ExecutionContext.SdksDirectory;
83+
if (sdksDirectory.Exists)
8484
{
85-
foreach (var file in runtimesDirectory.GetFiles("*", SearchOption.AllDirectories))
85+
foreach (var file in sdksDirectory.GetFiles("*", SearchOption.AllDirectories))
8686
{
8787
try
8888
{
@@ -96,7 +96,7 @@ protected override Task<int> ExecuteAsync(ParseResult parseResult, CancellationT
9696
}
9797

9898
// Delete subdirectories
99-
foreach (var directory in runtimesDirectory.GetDirectories())
99+
foreach (var directory in sdksDirectory.GetDirectories())
100100
{
101101
try
102102
{

src/Aspire.Cli/DotNet/DotNetCliRunner.cs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1202,8 +1202,8 @@ private void ConfigurePrivateSdkEnvironment(ProcessStartInfo startInfo)
12021202
// Get the effective minimum SDK version to determine which private SDK to use
12031203
var sdkInstaller = serviceProvider.GetRequiredService<IDotNetSdkInstaller>();
12041204
var sdkVersion = sdkInstaller.GetEffectiveMinimumSdkVersion();
1205-
var runtimesDirectory = executionContext.RuntimesDirectory.FullName;
1206-
var sdkInstallPath = Path.Combine(runtimesDirectory, "dotnet", sdkVersion);
1205+
var sdksDirectory = executionContext.SdksDirectory.FullName;
1206+
var sdkInstallPath = Path.Combine(sdksDirectory, "dotnet", sdkVersion);
12071207
var dotnetExecutablePath = Path.Combine(
12081208
sdkInstallPath,
12091209
RuntimeInformation.IsOSPlatform(OSPlatform.Windows) ? "dotnet.exe" : "dotnet"

src/Aspire.Cli/DotNet/DotNetSdkInstaller.cs

Lines changed: 39 additions & 39 deletions
Original file line numberDiff line numberDiff line change
@@ -36,44 +36,19 @@ internal sealed class DotNetSdkInstaller(IFeatures features, IConfiguration conf
3636
bool.TryParse(alwaysInstallSdk, out var alwaysInstall) &&
3737
alwaysInstall;
3838

39-
// First check if we already have the SDK installed in our private runtimes directory
40-
// If we do, verify it's working properly by calling dotnet nuget config paths
41-
// This is important on Windows where NuGet needs to create initial config on first use
39+
// First check if we already have the SDK installed in our private sdks directory
4240
if (!forceInstall)
4341
{
44-
var runtimesDirectory = GetRuntimesDirectory();
45-
var sdkInstallPath = Path.Combine(runtimesDirectory, "dotnet", minimumVersion);
42+
var sdksDirectory = GetSdksDirectory();
43+
var sdkInstallPath = Path.Combine(sdksDirectory, "dotnet", minimumVersion);
4644
var dotnetExecutable = RuntimeInformation.IsOSPlatform(OSPlatform.Windows)
4745
? Path.Combine(sdkInstallPath, "dotnet.exe")
4846
: Path.Combine(sdkInstallPath, "dotnet");
4947

5048
if (File.Exists(dotnetExecutable))
5149
{
52-
logger.LogDebug("Found private SDK installation at {Path}, verifying it works", sdkInstallPath);
53-
54-
try
55-
{
56-
// Call GetNuGetConfigPathsAsync to ensure NuGet is properly initialized
57-
var options = new DotNetCliRunnerInvocationOptions();
58-
var (exitCode, _) = await dotNetCliRunner.GetNuGetConfigPathsAsync(
59-
new DirectoryInfo(Environment.CurrentDirectory),
60-
options,
61-
cancellationToken);
62-
63-
if (exitCode == 0)
64-
{
65-
logger.LogDebug("Private SDK installation verified successfully");
66-
return (true, minimumVersion, minimumVersion, false);
67-
}
68-
else
69-
{
70-
logger.LogDebug("Private SDK installation verification failed with exit code {ExitCode}", exitCode);
71-
}
72-
}
73-
catch (Exception ex)
74-
{
75-
logger.LogDebug(ex, "Failed to verify private SDK installation");
76-
}
50+
logger.LogDebug("Found private SDK installation at {Path}", sdkInstallPath);
51+
return (true, minimumVersion, minimumVersion, false);
7752
}
7853
}
7954

@@ -159,8 +134,8 @@ internal sealed class DotNetSdkInstaller(IFeatures features, IConfiguration conf
159134
public async Task InstallAsync(CancellationToken cancellationToken = default)
160135
{
161136
var sdkVersion = GetEffectiveMinimumSdkVersion();
162-
var runtimesDirectory = GetRuntimesDirectory();
163-
var sdkInstallPath = Path.Combine(runtimesDirectory, "dotnet", sdkVersion);
137+
var sdksDirectory = GetSdksDirectory();
138+
var sdkInstallPath = Path.Combine(sdksDirectory, "dotnet", sdkVersion);
164139

165140
// Check if SDK is already installed in the private location
166141
if (Directory.Exists(sdkInstallPath))
@@ -169,14 +144,14 @@ public async Task InstallAsync(CancellationToken cancellationToken = default)
169144
return;
170145
}
171146

172-
// Create the runtimes directory if it doesn't exist
173-
Directory.CreateDirectory(runtimesDirectory);
147+
// Create the sdks directory if it doesn't exist
148+
Directory.CreateDirectory(sdksDirectory);
174149

175150
// Determine which install script to use based on the platform
176151
var (scriptUrl, scriptFileName, scriptRunner) = GetInstallScriptInfo();
177152

178153
// Download the install script
179-
var scriptPath = Path.Combine(runtimesDirectory, scriptFileName);
154+
var scriptPath = Path.Combine(sdksDirectory, scriptFileName);
180155
using (var httpClient = new HttpClient())
181156
{
182157
httpClient.Timeout = TimeSpan.FromMinutes(5);
@@ -271,6 +246,31 @@ public async Task InstallAsync(CancellationToken cancellationToken = default)
271246
{
272247
// Ignore cleanup errors
273248
}
249+
250+
// After installation, call dotnet nuget config paths to initialize NuGet
251+
// This is important on Windows where NuGet needs to create initial config on first use
252+
logger.LogDebug("Initializing NuGet configuration for private SDK installation");
253+
try
254+
{
255+
var options = new DotNetCliRunnerInvocationOptions();
256+
var (exitCode, _) = await dotNetCliRunner.GetNuGetConfigPathsAsync(
257+
new DirectoryInfo(Environment.CurrentDirectory),
258+
options,
259+
cancellationToken);
260+
261+
if (exitCode == 0)
262+
{
263+
logger.LogDebug("NuGet configuration initialized successfully");
264+
}
265+
else
266+
{
267+
logger.LogDebug("NuGet configuration initialization returned exit code {ExitCode}", exitCode);
268+
}
269+
}
270+
catch (Exception ex)
271+
{
272+
logger.LogDebug(ex, "Failed to initialize NuGet configuration, continuing anyway");
273+
}
274274
}
275275

276276
/// <summary>
@@ -290,12 +290,12 @@ private static string GetCurrentArchitecture()
290290
}
291291

292292
/// <summary>
293-
/// Gets the directory where .NET runtimes are stored.
293+
/// Gets the directory where .NET SDKs are stored.
294294
/// </summary>
295-
/// <returns>The full path to the runtimes directory.</returns>
296-
private string GetRuntimesDirectory()
295+
/// <returns>The full path to the sdks directory.</returns>
296+
private string GetSdksDirectory()
297297
{
298-
return executionContext.RuntimesDirectory.FullName;
298+
return executionContext.SdksDirectory.FullName;
299299
}
300300

301301
/// <summary>

src/Aspire.Cli/Program.cs

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -170,20 +170,20 @@ private static DirectoryInfo GetHivesDirectory()
170170
return new DirectoryInfo(hivesDirectory);
171171
}
172172

173-
private static DirectoryInfo GetRuntimesDirectory()
173+
private static DirectoryInfo GetSdksDirectory()
174174
{
175175
var homeDirectory = GetUsersAspirePath();
176-
var runtimesPath = Path.Combine(homeDirectory, "runtimes");
177-
return new DirectoryInfo(runtimesPath);
176+
var sdksPath = Path.Combine(homeDirectory, "sdks");
177+
return new DirectoryInfo(sdksPath);
178178
}
179179

180180
private static CliExecutionContext BuildCliExecutionContext(bool debugMode)
181181
{
182182
var workingDirectory = new DirectoryInfo(Environment.CurrentDirectory);
183183
var hivesDirectory = GetHivesDirectory();
184184
var cacheDirectory = GetCacheDirectory();
185-
var runtimesDirectory = GetRuntimesDirectory();
186-
return new CliExecutionContext(workingDirectory, hivesDirectory, cacheDirectory, runtimesDirectory, debugMode);
185+
var sdksDirectory = GetSdksDirectory();
186+
return new CliExecutionContext(workingDirectory, hivesDirectory, cacheDirectory, sdksDirectory, debugMode);
187187
}
188188

189189
private static DirectoryInfo GetCacheDirectory()

tests/Aspire.Cli.Tests/Commands/RunCommandTests.cs

Lines changed: 7 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -779,7 +779,7 @@ public async Task DotNetCliRunner_RunAsync_WhenWatchIsTrue_IncludesNonInteractiv
779779
var options = new DotNetCliRunnerInvocationOptions();
780780

781781
var executionContext = new CliExecutionContext(
782-
workingDirectory: workspace.WorkspaceRoot, hivesDirectory: workspace.WorkspaceRoot.CreateSubdirectory(".aspire").CreateSubdirectory("hives"), cacheDirectory: workspace.WorkspaceRoot.CreateSubdirectory(".aspire").CreateSubdirectory("cache"), runtimesDirectory: new DirectoryInfo(Path.Combine(Path.GetTempPath(), "aspire-test-runtimes"))
782+
workingDirectory: workspace.WorkspaceRoot, hivesDirectory: workspace.WorkspaceRoot.CreateSubdirectory(".aspire").CreateSubdirectory("hives"), cacheDirectory: workspace.WorkspaceRoot.CreateSubdirectory(".aspire").CreateSubdirectory("cache"), sdksDirectory: new DirectoryInfo(Path.Combine(Path.GetTempPath(), "aspire-test-sdks"))
783783
);
784784

785785
var runner = new AssertingDotNetCliRunner(
@@ -833,7 +833,7 @@ public async Task DotNetCliRunner_RunAsync_WhenWatchIsFalse_DoesNotIncludeNonInt
833833
var options = new DotNetCliRunnerInvocationOptions();
834834

835835
var executionContext = new CliExecutionContext(
836-
workingDirectory: workspace.WorkspaceRoot, hivesDirectory: workspace.WorkspaceRoot.CreateSubdirectory(".aspire").CreateSubdirectory("hives"), cacheDirectory: workspace.WorkspaceRoot.CreateSubdirectory(".aspire").CreateSubdirectory("cache"), runtimesDirectory: new DirectoryInfo(Path.Combine(Path.GetTempPath(), "aspire-test-runtimes"))
836+
workingDirectory: workspace.WorkspaceRoot, hivesDirectory: workspace.WorkspaceRoot.CreateSubdirectory(".aspire").CreateSubdirectory("hives"), cacheDirectory: workspace.WorkspaceRoot.CreateSubdirectory(".aspire").CreateSubdirectory("cache"), sdksDirectory: new DirectoryInfo(Path.Combine(Path.GetTempPath(), "aspire-test-sdks"))
837837
);
838838

839839
var runner = new AssertingDotNetCliRunner(
@@ -883,7 +883,7 @@ public async Task DotNetCliRunner_RunAsync_WhenWatchIsTrueAndDebugIsTrue_Include
883883
var options = new DotNetCliRunnerInvocationOptions { Debug = true };
884884

885885
var executionContext = new CliExecutionContext(
886-
workingDirectory: workspace.WorkspaceRoot, hivesDirectory: workspace.WorkspaceRoot.CreateSubdirectory(".aspire").CreateSubdirectory("hives"), cacheDirectory: workspace.WorkspaceRoot.CreateSubdirectory(".aspire").CreateSubdirectory("cache"), runtimesDirectory: new DirectoryInfo(Path.Combine(Path.GetTempPath(), "aspire-test-runtimes"))
886+
workingDirectory: workspace.WorkspaceRoot, hivesDirectory: workspace.WorkspaceRoot.CreateSubdirectory(".aspire").CreateSubdirectory("hives"), cacheDirectory: workspace.WorkspaceRoot.CreateSubdirectory(".aspire").CreateSubdirectory("cache"), sdksDirectory: new DirectoryInfo(Path.Combine(Path.GetTempPath(), "aspire-test-sdks"))
887887
);
888888

889889
var runner = new AssertingDotNetCliRunner(
@@ -937,7 +937,7 @@ public async Task DotNetCliRunner_RunAsync_WhenWatchIsTrueAndDebugIsFalse_DoesNo
937937
var options = new DotNetCliRunnerInvocationOptions { Debug = false };
938938

939939
var executionContext = new CliExecutionContext(
940-
workingDirectory: workspace.WorkspaceRoot, hivesDirectory: workspace.WorkspaceRoot.CreateSubdirectory(".aspire").CreateSubdirectory("hives"), cacheDirectory: workspace.WorkspaceRoot.CreateSubdirectory(".aspire").CreateSubdirectory("cache"), runtimesDirectory: new DirectoryInfo(Path.Combine(Path.GetTempPath(), "aspire-test-runtimes"))
940+
workingDirectory: workspace.WorkspaceRoot, hivesDirectory: workspace.WorkspaceRoot.CreateSubdirectory(".aspire").CreateSubdirectory("hives"), cacheDirectory: workspace.WorkspaceRoot.CreateSubdirectory(".aspire").CreateSubdirectory("cache"), sdksDirectory: new DirectoryInfo(Path.Combine(Path.GetTempPath(), "aspire-test-sdks"))
941941
);
942942

943943
var runner = new AssertingDotNetCliRunner(
@@ -986,7 +986,7 @@ public async Task DotNetCliRunner_RunAsync_WhenWatchIsFalseAndDebugIsTrue_DoesNo
986986
var options = new DotNetCliRunnerInvocationOptions { Debug = true };
987987

988988
var executionContext = new CliExecutionContext(
989-
workingDirectory: workspace.WorkspaceRoot, hivesDirectory: workspace.WorkspaceRoot.CreateSubdirectory(".aspire").CreateSubdirectory("hives"), cacheDirectory: workspace.WorkspaceRoot.CreateSubdirectory(".aspire").CreateSubdirectory("cache"), runtimesDirectory: new DirectoryInfo(Path.Combine(Path.GetTempPath(), "aspire-test-runtimes"))
989+
workingDirectory: workspace.WorkspaceRoot, hivesDirectory: workspace.WorkspaceRoot.CreateSubdirectory(".aspire").CreateSubdirectory("hives"), cacheDirectory: workspace.WorkspaceRoot.CreateSubdirectory(".aspire").CreateSubdirectory("cache"), sdksDirectory: new DirectoryInfo(Path.Combine(Path.GetTempPath(), "aspire-test-sdks"))
990990
);
991991

992992
var runner = new AssertingDotNetCliRunner(
@@ -1036,7 +1036,7 @@ public async Task DotNetCliRunner_RunAsync_WhenWatchIsTrue_SetsSuppressLaunchBro
10361036
var options = new DotNetCliRunnerInvocationOptions();
10371037

10381038
var executionContext = new CliExecutionContext(
1039-
workingDirectory: workspace.WorkspaceRoot, hivesDirectory: workspace.WorkspaceRoot.CreateSubdirectory(".aspire").CreateSubdirectory("hives"), cacheDirectory: workspace.WorkspaceRoot.CreateSubdirectory(".aspire").CreateSubdirectory("cache"), runtimesDirectory: new DirectoryInfo(Path.Combine(Path.GetTempPath(), "aspire-test-runtimes"))
1039+
workingDirectory: workspace.WorkspaceRoot, hivesDirectory: workspace.WorkspaceRoot.CreateSubdirectory(".aspire").CreateSubdirectory("hives"), cacheDirectory: workspace.WorkspaceRoot.CreateSubdirectory(".aspire").CreateSubdirectory("cache"), sdksDirectory: new DirectoryInfo(Path.Combine(Path.GetTempPath(), "aspire-test-sdks"))
10401040
);
10411041

10421042
var runner = new AssertingDotNetCliRunner(
@@ -1086,7 +1086,7 @@ public async Task DotNetCliRunner_RunAsync_WhenWatchIsFalse_DoesNotSetSuppressLa
10861086
var options = new DotNetCliRunnerInvocationOptions();
10871087

10881088
var executionContext = new CliExecutionContext(
1089-
workingDirectory: workspace.WorkspaceRoot, hivesDirectory: workspace.WorkspaceRoot.CreateSubdirectory(".aspire").CreateSubdirectory("hives"), cacheDirectory: workspace.WorkspaceRoot.CreateSubdirectory(".aspire").CreateSubdirectory("cache"), runtimesDirectory: new DirectoryInfo(Path.Combine(Path.GetTempPath(), "aspire-test-runtimes"))
1089+
workingDirectory: workspace.WorkspaceRoot, hivesDirectory: workspace.WorkspaceRoot.CreateSubdirectory(".aspire").CreateSubdirectory("hives"), cacheDirectory: workspace.WorkspaceRoot.CreateSubdirectory(".aspire").CreateSubdirectory("cache"), sdksDirectory: new DirectoryInfo(Path.Combine(Path.GetTempPath(), "aspire-test-sdks"))
10901090
);
10911091

10921092
var runner = new AssertingDotNetCliRunner(

tests/Aspire.Cli.Tests/DotNetSdkInstallerTests.cs

Lines changed: 11 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -21,9 +21,9 @@ private static CliExecutionContext CreateTestExecutionContext()
2121
var workingDirectory = new DirectoryInfo(tempPath);
2222
var hivesDirectory = new DirectoryInfo(Path.Combine(tempPath, "hives"));
2323
var cacheDirectory = new DirectoryInfo(Path.Combine(tempPath, "cache"));
24-
var runtimesDirectory = new DirectoryInfo(Path.Combine(tempPath, "runtimes"));
24+
var sdksDirectory = new DirectoryInfo(Path.Combine(tempPath, "sdks"));
2525

26-
return new CliExecutionContext(workingDirectory, hivesDirectory, cacheDirectory, runtimesDirectory, debugMode: false);
26+
return new CliExecutionContext(workingDirectory, hivesDirectory, cacheDirectory, sdksDirectory, debugMode: false);
2727
}
2828

2929
private static ILogger<DotNetSdkInstaller> CreateTestLogger()
@@ -107,17 +107,17 @@ public async Task CheckAsync_WithInvalidMinimumVersion_ReturnsFalse()
107107
}
108108

109109
[Fact]
110-
public async Task InstallAsync_CreatesRuntimesDirectory()
110+
public async Task InstallAsync_CreatesSdksDirectory()
111111
{
112112
var features = new TestFeatures()
113113
.SetFeature(KnownFeatures.MinimumSdkCheckEnabled, true);
114114
var context = CreateTestExecutionContext();
115115
var installer = new DotNetSdkInstaller(features, CreateEmptyConfiguration(), context, CreateTestDotNetCliRunner(), CreateTestLogger());
116116

117-
// Get the runtimes directory path
118-
var runtimesDirectory = context.RuntimesDirectory.FullName;
117+
// Get the sdks directory path
118+
var sdksDirectory = context.SdksDirectory.FullName;
119119
var sdkVersion = installer.GetEffectiveMinimumSdkVersion();
120-
var sdkInstallPath = Path.Combine(runtimesDirectory, "dotnet", sdkVersion);
120+
var sdkInstallPath = Path.Combine(sdksDirectory, "dotnet", sdkVersion);
121121

122122
// Clean up if it exists from a previous test
123123
if (Directory.Exists(sdkInstallPath))
@@ -140,18 +140,18 @@ public async Task InstallAsync_CreatesRuntimesDirectory()
140140
}
141141

142142
[Fact]
143-
public void GetRuntimesDirectory_ReturnsValidPath()
143+
public void GetSdksDirectory_ReturnsValidPath()
144144
{
145145
var context = CreateTestExecutionContext();
146146

147-
// Verify the runtimes directory from the execution context
148-
var runtimesDirectory = context.RuntimesDirectory.FullName;
147+
// Verify the sdks directory from the execution context
148+
var sdksDirectory = context.SdksDirectory.FullName;
149149

150150
// Verify the path contains the expected components
151-
Assert.Contains("runtimes", runtimesDirectory);
151+
Assert.Contains("sdks", sdksDirectory);
152152

153153
// Verify it's a valid path format
154-
Assert.False(string.IsNullOrWhiteSpace(runtimesDirectory));
154+
Assert.False(string.IsNullOrWhiteSpace(sdksDirectory));
155155
}
156156

157157
[Fact]

0 commit comments

Comments
 (0)