Skip to content

Commit 11265f1

Browse files
authored
Add progress abstraction for installation library (#51463)
2 parents 487be24 + 2cbf233 commit 11265f1

File tree

11 files changed

+234
-71
lines changed

11 files changed

+234
-71
lines changed
Lines changed: 53 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,53 @@
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.Text;
7+
8+
namespace Microsoft.Dotnet.Installation;
9+
10+
public interface IProgressTarget
11+
{
12+
public IProgressReporter CreateProgressReporter();
13+
}
14+
15+
public interface IProgressReporter : IDisposable
16+
{
17+
public IProgressTask AddTask(string description, double maxValue);
18+
}
19+
20+
public interface IProgressTask
21+
{
22+
string Description { get; set; }
23+
double Value { get; set; }
24+
double MaxValue { get; set; }
25+
26+
27+
}
28+
29+
public class NullProgressTarget : IProgressTarget
30+
{
31+
public IProgressReporter CreateProgressReporter() => new NullProgressReporter();
32+
class NullProgressReporter : IProgressReporter
33+
{
34+
public void Dispose()
35+
{
36+
}
37+
public IProgressTask AddTask(string description, double maxValue)
38+
{
39+
return new NullProgressTask(description);
40+
}
41+
}
42+
class NullProgressTask : IProgressTask
43+
{
44+
public NullProgressTask(string description)
45+
{
46+
Description = description;
47+
}
48+
49+
public double Value { get; set; }
50+
public string Description { get; set; }
51+
public double MaxValue { get; set; }
52+
}
53+
}

src/Installer/Microsoft.Dotnet.Installation/InstallerFactory.cs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -10,9 +10,9 @@ namespace Microsoft.Dotnet.Installation;
1010

1111
public static class InstallerFactory
1212
{
13-
public static IDotnetInstaller CreateInstaller()
13+
public static IDotnetInstaller CreateInstaller(IProgressTarget progressTarget)
1414
{
15-
return new DotnetInstaller();
15+
return new DotnetInstaller(progressTarget);
1616
}
1717

1818
public static IDotnetReleaseInfoProvider CreateReleaseInfoProvider()

src/Installer/Microsoft.Dotnet.Installation/Internal/ArchiveDotnetExtractor.cs

Lines changed: 23 additions & 58 deletions
Original file line numberDiff line numberDiff line change
@@ -16,15 +16,15 @@ internal class ArchiveDotnetExtractor : IDisposable
1616
{
1717
private readonly DotnetInstallRequest _request;
1818
private readonly ReleaseVersion _resolvedVersion;
19-
private readonly bool _noProgress;
19+
private readonly IProgressTarget _progressTarget;
2020
private string scratchDownloadDirectory;
2121
private string? _archivePath;
2222

23-
public ArchiveDotnetExtractor(DotnetInstallRequest request, ReleaseVersion resolvedVersion, bool noProgress = false)
23+
public ArchiveDotnetExtractor(DotnetInstallRequest request, ReleaseVersion resolvedVersion, IProgressTarget progressTarget)
2424
{
2525
_request = request;
2626
_resolvedVersion = resolvedVersion;
27-
_noProgress = noProgress;
27+
_progressTarget = progressTarget;
2828
scratchDownloadDirectory = Directory.CreateTempSubdirectory().FullName;
2929
}
3030

@@ -34,33 +34,17 @@ public void Prepare()
3434
var archiveName = $"dotnet-{Guid.NewGuid()}";
3535
_archivePath = Path.Combine(scratchDownloadDirectory, archiveName + DnupUtilities.GetArchiveFileExtensionForPlatform());
3636

37-
if (_noProgress)
37+
using (var progressReporter = _progressTarget.CreateProgressReporter())
3838
{
39-
// When no-progress is enabled, download without progress display
40-
Console.WriteLine($"Downloading .NET SDK {_resolvedVersion}...");
41-
var downloadSuccess = releaseManifest.DownloadArchiveWithVerification(_request, _resolvedVersion, _archivePath, null);
39+
var downloadTask = progressReporter.AddTask($"Downloading .NET SDK {_resolvedVersion}", 100);
40+
var reporter = new DownloadProgressReporter(downloadTask, $"Downloading .NET SDK {_resolvedVersion}");
41+
var downloadSuccess = releaseManifest.DownloadArchiveWithVerification(_request, _resolvedVersion, _archivePath, reporter);
4242
if (!downloadSuccess)
4343
{
4444
throw new InvalidOperationException($"Failed to download .NET archive for version {_resolvedVersion}");
4545
}
46-
Console.WriteLine($"Download of .NET SDK {_resolvedVersion} complete.");
47-
}
48-
else
49-
{
50-
// Use progress display for normal operation
51-
Spectre.Console.AnsiConsole.Progress()
52-
.Start(ctx =>
53-
{
54-
var downloadTask = ctx.AddTask($"Downloading .NET SDK {_resolvedVersion}", autoStart: true);
55-
var reporter = new SpectreDownloadProgressReporter(downloadTask, $"Downloading .NET SDK {_resolvedVersion}");
56-
var downloadSuccess = releaseManifest.DownloadArchiveWithVerification(_request, _resolvedVersion, _archivePath, reporter);
57-
if (!downloadSuccess)
58-
{
59-
throw new InvalidOperationException($"Failed to download .NET archive for version {_resolvedVersion}");
60-
}
6146

62-
downloadTask.Value = 100;
63-
});
47+
downloadTask.Value = 100;
6448
}
6549
}
6650

@@ -111,45 +95,26 @@ public void Commit(IEnumerable<ReleaseVersion> existingSdkVersions)
11195
throw new InvalidOperationException("Archive not found. Make sure Prepare() was called successfully.");
11296
}
11397

114-
if (_noProgress)
98+
using (var progressReporter = _progressTarget.CreateProgressReporter())
11599
{
116-
// When no-progress is enabled, install without progress display
117-
Console.WriteLine($"Installing .NET SDK {_resolvedVersion}...");
100+
var installTask = progressReporter.AddTask($"Installing .NET SDK {_resolvedVersion}", maxValue: 100);
118101

119102
// Extract archive directly to target directory with special handling for muxer
120-
var extractResult = ExtractArchiveDirectlyToTarget(_archivePath, _request.InstallRoot.Path!, existingSdkVersions, null);
103+
var extractResult = ExtractArchiveDirectlyToTarget(_archivePath, _request.InstallRoot.Path!, existingSdkVersions, installTask);
121104
if (extractResult is not null)
122105
{
123106
throw new InvalidOperationException($"Failed to install SDK: {extractResult}");
124107
}
125108

126-
Console.WriteLine($"Installation of .NET SDK {_resolvedVersion} complete.");
127-
}
128-
else
129-
{
130-
// Use progress display for normal operation
131-
Spectre.Console.AnsiConsole.Progress()
132-
.Start(ctx =>
133-
{
134-
var installTask = ctx.AddTask($"Installing .NET SDK {_resolvedVersion}", autoStart: true);
135-
136-
// Extract archive directly to target directory with special handling for muxer
137-
var extractResult = ExtractArchiveDirectlyToTarget(_archivePath, _request.InstallRoot.Path!, existingSdkVersions, installTask);
138-
if (extractResult is not null)
139-
{
140-
throw new InvalidOperationException($"Failed to install SDK: {extractResult}");
141-
}
142-
143-
installTask.Value = installTask.MaxValue;
144-
});
109+
installTask.Value = installTask.MaxValue;
145110
}
146111
}
147112

148113
/**
149114
* Extracts the archive directly to the target directory with special handling for muxer.
150115
* Combines extraction and installation into a single operation.
151116
*/
152-
private string? ExtractArchiveDirectlyToTarget(string archivePath, string targetDir, IEnumerable<ReleaseVersion> existingSdkVersions, Spectre.Console.ProgressTask? installTask)
117+
private string? ExtractArchiveDirectlyToTarget(string archivePath, string targetDir, IEnumerable<ReleaseVersion> existingSdkVersions, IProgressTask? installTask)
153118
{
154119
try
155120
{
@@ -194,7 +159,7 @@ private MuxerHandlingConfig ConfigureMuxerHandling(IEnumerable<ReleaseVersion> e
194159
/**
195160
* Extracts a tar or tar.gz archive to the target directory.
196161
*/
197-
private string? ExtractTarArchive(string archivePath, string targetDir, MuxerHandlingConfig muxerConfig, Spectre.Console.ProgressTask? installTask)
162+
private string? ExtractTarArchive(string archivePath, string targetDir, MuxerHandlingConfig muxerConfig, IProgressTask? installTask)
198163
{
199164
string decompressedPath = DecompressTarGzIfNeeded(archivePath, out bool needsDecompression);
200165

@@ -265,7 +230,7 @@ private long CountTarEntries(string tarPath)
265230
/**
266231
* Extracts the contents of a tar file to the target directory.
267232
*/
268-
private void ExtractTarContents(string tarPath, string targetDir, MuxerHandlingConfig muxerConfig, Spectre.Console.ProgressTask? installTask)
233+
private void ExtractTarContents(string tarPath, string targetDir, MuxerHandlingConfig muxerConfig, IProgressTask? installTask)
269234
{
270235
using var tarStream = File.OpenRead(tarPath);
271236
var tarReader = new TarReader(tarStream);
@@ -282,20 +247,20 @@ private void ExtractTarContents(string tarPath, string targetDir, MuxerHandlingC
282247
// Create directory if it doesn't exist
283248
var dirPath = Path.Combine(targetDir, entry.Name);
284249
Directory.CreateDirectory(dirPath);
285-
installTask?.Increment(1);
250+
installTask?.Value += 1;
286251
}
287252
else
288253
{
289254
// Skip other entry types
290-
installTask?.Increment(1);
255+
installTask?.Value += 1;
291256
}
292257
}
293258
}
294259

295260
/**
296261
* Extracts a single file entry from a tar archive.
297262
*/
298-
private void ExtractTarFileEntry(TarEntry entry, string targetDir, MuxerHandlingConfig muxerConfig, Spectre.Console.ProgressTask? installTask)
263+
private void ExtractTarFileEntry(TarEntry entry, string targetDir, MuxerHandlingConfig muxerConfig, IProgressTask? installTask)
299264
{
300265
var fileName = Path.GetFileName(entry.Name);
301266
var destPath = Path.Combine(targetDir, entry.Name);
@@ -314,7 +279,7 @@ private void ExtractTarFileEntry(TarEntry entry, string targetDir, MuxerHandling
314279
entry.DataStream?.CopyTo(outStream);
315280
}
316281

317-
installTask?.Increment(1);
282+
installTask?.Value += 1;
318283
}
319284

320285
/**
@@ -346,7 +311,7 @@ private void HandleMuxerUpdateFromTar(TarEntry entry, string muxerTargetPath)
346311
/**
347312
* Extracts a zip archive to the target directory.
348313
*/
349-
private string? ExtractZipArchive(string archivePath, string targetDir, MuxerHandlingConfig muxerConfig, Spectre.Console.ProgressTask? installTask)
314+
private string? ExtractZipArchive(string archivePath, string targetDir, MuxerHandlingConfig muxerConfig, IProgressTask? installTask)
350315
{
351316
long totalFiles = CountZipEntries(archivePath);
352317

@@ -373,7 +338,7 @@ private long CountZipEntries(string zipPath)
373338
/**
374339
* Extracts a single entry from a zip archive.
375340
*/
376-
private void ExtractZipEntry(ZipArchiveEntry entry, string targetDir, MuxerHandlingConfig muxerConfig, Spectre.Console.ProgressTask? installTask)
341+
private void ExtractZipEntry(ZipArchiveEntry entry, string targetDir, MuxerHandlingConfig muxerConfig, IProgressTask? installTask)
377342
{
378343
var fileName = Path.GetFileName(entry.FullName);
379344
var destPath = Path.Combine(targetDir, entry.FullName);
@@ -382,7 +347,7 @@ private void ExtractZipEntry(ZipArchiveEntry entry, string targetDir, MuxerHandl
382347
if (string.IsNullOrEmpty(fileName))
383348
{
384349
Directory.CreateDirectory(destPath);
385-
installTask?.Increment(1);
350+
installTask?.Value += 1;
386351
return;
387352
}
388353

@@ -400,7 +365,7 @@ private void ExtractZipEntry(ZipArchiveEntry entry, string targetDir, MuxerHandl
400365
entry.ExtractToFile(destPath, overwrite: true);
401366
}
402367

403-
installTask?.Increment(1);
368+
installTask?.Value += 1;
404369
}
405370

406371
/**

src/Installer/Microsoft.Dotnet.Installation/Internal/DotnetInstaller.cs

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -5,17 +5,23 @@
55
using System.Collections.Generic;
66
using System.Text;
77
using Microsoft.Deployment.DotNet.Releases;
8-
using Spectre.Console;
98

109
namespace Microsoft.Dotnet.Installation.Internal
1110
{
1211
internal class DotnetInstaller : IDotnetInstaller
1312
{
13+
IProgressTarget _progressTarget;
14+
15+
public DotnetInstaller(IProgressTarget progressTarget)
16+
{
17+
_progressTarget = progressTarget;
18+
}
19+
1420
public void Install(DotnetInstallRoot dotnetRoot, InstallComponent component, ReleaseVersion version)
1521
{
1622
var installRequest = new DotnetInstallRequest(dotnetRoot, new UpdateChannel(version.ToString()), component, new InstallRequestOptions());
1723

18-
using ArchiveDotnetExtractor installer = new(installRequest, version, noProgress: true);
24+
using ArchiveDotnetExtractor installer = new(installRequest, version, _progressTarget);
1925
installer.Prepare();
2026
installer.Commit();
2127
}
Lines changed: 3 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,16 +1,15 @@
11
using System;
2-
using Spectre.Console;
32
using Microsoft.Dotnet.Installation;
43

54
namespace Microsoft.Dotnet.Installation.Internal
65
{
7-
public class SpectreDownloadProgressReporter : IProgress<DownloadProgress>
6+
public class DownloadProgressReporter : IProgress<DownloadProgress>
87
{
9-
private readonly ProgressTask _task;
8+
private readonly IProgressTask _task;
109
private readonly string _description;
1110
private long? _totalBytes;
1211

13-
public SpectreDownloadProgressReporter(ProgressTask task, string description)
12+
public DownloadProgressReporter(IProgressTask task, string description)
1413
{
1514
_task = task;
1615
_description = description;

src/Installer/Microsoft.Dotnet.Installation/Microsoft.Dotnet.Installation.csproj

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -19,8 +19,6 @@
1919

2020
<ItemGroup>
2121
<PackageReference Include="Microsoft.Deployment.DotNet.Releases" />
22-
<!-- TODO: Create logging / progress abstraction and remove this dependency from the library -->
23-
<PackageReference Include="Spectre.Console" />
2422
</ItemGroup>
2523

2624
</Project>

src/Installer/dnup/Commands/Sdk/Install/SdkInstallCommand.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -209,7 +209,7 @@ public override int Execute()
209209

210210
// TODO: Implement transaction / rollback?
211211

212-
SpectreAnsiConsole.MarkupInterpolated($"Installing .NET SDK [blue]{resolvedVersion}[/] to [blue]{resolvedInstallPath}[/]...");
212+
SpectreAnsiConsole.MarkupLineInterpolated($"Installing .NET SDK [blue]{resolvedVersion}[/] to [blue]{resolvedInstallPath}[/]...");
213213

214214
DotnetInstall? mainInstall;
215215

src/Installer/dnup/InstallerOrchestratorSingleton.cs

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -51,7 +51,9 @@ private InstallerOrchestratorSingleton()
5151
}
5252
}
5353

54-
using ArchiveDotnetExtractor installer = new(installRequest, versionToInstall, noProgress);
54+
IProgressTarget progressTarget = noProgress ? new NonUpdatingProgressTarget() : new SpectreProgressTarget();
55+
56+
using ArchiveDotnetExtractor installer = new(installRequest, versionToInstall, progressTarget);
5557
installer.Prepare();
5658

5759
// Extract and commit the install to the directory

0 commit comments

Comments
 (0)