From 8958827893c2a41c87e7bf79efe76d171754ae50 Mon Sep 17 00:00:00 2001 From: haileymck <111816896+haileymck@users.noreply.github.com> Date: Fri, 10 Oct 2025 15:39:10 -0700 Subject: [PATCH 1/2] execute az cli commands async --- .../CliHelpers/AzCliRunner.cs | 19 +++---- .../AspNet/AspNetCommandService.cs | 9 +++- .../AspNet/Commands/AspNetOptions.cs | 27 +++++----- .../AspNet/Helpers/AzCliHelper.cs | 53 ++++++++----------- .../AspNet/Helpers/AzureAccountInformation.cs | 6 +++ .../UpdateAppAuthorizationStep.cs | 10 ++-- .../Aspire/AspireCommandService.cs | 6 +++ .../Command/ICommandService.cs | 2 + .../dotnet-scaffold/Program.cs | 4 +- 9 files changed, 72 insertions(+), 64 deletions(-) create mode 100644 src/dotnet-scaffolding/dotnet-scaffold/AspNet/Helpers/AzureAccountInformation.cs diff --git a/src/dotnet-scaffolding/Microsoft.DotNet.Scaffolding.Internal/CliHelpers/AzCliRunner.cs b/src/dotnet-scaffolding/Microsoft.DotNet.Scaffolding.Internal/CliHelpers/AzCliRunner.cs index 85e6461ba..fe6b28935 100644 --- a/src/dotnet-scaffolding/Microsoft.DotNet.Scaffolding.Internal/CliHelpers/AzCliRunner.cs +++ b/src/dotnet-scaffolding/Microsoft.DotNet.Scaffolding.Internal/CliHelpers/AzCliRunner.cs @@ -16,7 +16,7 @@ public static AzCliRunner Create(string? commandName = null) return new AzCliRunner(commandName); } - public int RunAzCli(string arguments, out string? stdOut, out string? stdErr) + public async Task<(int ExitCode, string? StdOut, string? StdErr)> RunAzCliAsync(string arguments) { using var outStream = new ProcessOutputStreamReader(); using var errStream = new ProcessOutputStreamReader(); @@ -42,13 +42,11 @@ public int RunAzCli(string arguments, out string? stdOut, out string? stdErr) } catch (Exception ex) { - stdOut = string.Empty; - stdErr = ex.Message; - return -1; + return (-1, string.Empty, ex.Message); } - var taskOut = outStream.BeginRead(process.StandardOutput); - var taskErr = errStream.BeginRead(process.StandardError); + Task taskOut = outStream.BeginRead(process.StandardOutput); + Task taskErr = errStream.BeginRead(process.StandardError); // Wait for process to exit with a timeout (e.g., 30 seconds) const int timeoutMilliseconds = 30000; @@ -66,13 +64,12 @@ public int RunAzCli(string arguments, out string? stdOut, out string? stdErr) } } - taskOut.Wait(); - taskErr.Wait(); + await Task.WhenAll(taskOut, taskErr); - stdOut = outStream.CapturedOutput?.Trim(); - stdErr = errStream.CapturedOutput; + string? stdOut = outStream.CapturedOutput?.Trim(); + string? stdErr = errStream.CapturedOutput; - return process.ExitCode; + return (process.ExitCode, stdOut, stdErr); } internal ProcessStartInfo _psi; diff --git a/src/dotnet-scaffolding/dotnet-scaffold/AspNet/AspNetCommandService.cs b/src/dotnet-scaffolding/dotnet-scaffold/AspNet/AspNetCommandService.cs index a10435d53..84221679e 100644 --- a/src/dotnet-scaffolding/dotnet-scaffold/AspNet/AspNetCommandService.cs +++ b/src/dotnet-scaffolding/dotnet-scaffold/AspNet/AspNetCommandService.cs @@ -44,7 +44,7 @@ public Type[] GetScaffoldSteps() ]; } - public void AddScaffolderCommands() + public async Task AddScaffolderCommandsAsync() { _builder.AddScaffolder(ScaffolderCatagory.AspNet, AspnetStrings.Blazor.Empty) .WithDisplayName(AspnetStrings.Blazor.EmptyDisplayName) @@ -309,7 +309,7 @@ public void AddScaffolderCommands() .WithIdentityTextTemplatingStep() .WithIdentityCodeChangeStep(); - AspNetOptions options = new(); + AspNetOptions options = await AspNetOptions.CreateAsync(); if (options.AreAzCliCommandsSuccessful()) { @@ -340,5 +340,10 @@ public void AddScaffolderCommands() .WithEntraIdTextTemplatingStep(); } } + + public void AddScaffolderCommands() + { + //ignore + } } } diff --git a/src/dotnet-scaffolding/dotnet-scaffold/AspNet/Commands/AspNetOptions.cs b/src/dotnet-scaffolding/dotnet-scaffold/AspNet/Commands/AspNetOptions.cs index a9ddba5b6..c2ce898f5 100644 --- a/src/dotnet-scaffolding/dotnet-scaffold/AspNet/Commands/AspNetOptions.cs +++ b/src/dotnet-scaffolding/dotnet-scaffold/AspNet/Commands/AspNetOptions.cs @@ -161,23 +161,24 @@ internal class AspNetOptions PickerType = InteractivePickerType.YesNo }; - private readonly bool _areAzCliCommandsSuccessful; - private readonly List _usernames = []; - private readonly List _tenants = []; - private readonly List _appIds = []; private ScaffolderOption? _username = null; private ScaffolderOption? _tenantId = null; private ScaffolderOption? _applicationId = null; + private AzureInformation? _azureInformation; - public AspNetOptions() + + private AspNetOptions(AzureInformation? azureInformation) + { + _azureInformation = azureInformation; + } + + public static async Task CreateAsync() { - _areAzCliCommandsSuccessful = AzCliHelper.GetAzureInformation(out List usernames, out List tenants, out List appIds); - _usernames = usernames; - _tenants = tenants; - _appIds = appIds; + AzureInformation? azureInfo = await AzCliHelper.GetAzureInformationAsync(); + return new AspNetOptions(azureInfo); } - public bool AreAzCliCommandsSuccessful() => _areAzCliCommandsSuccessful; + public bool AreAzCliCommandsSuccessful() => _azureInformation is not null; public ScaffolderOption Username => _username ??= new() { @@ -186,7 +187,7 @@ public AspNetOptions() Description = AspnetStrings.Options.Username.Description, Required = true, PickerType = InteractivePickerType.CustomPicker, - CustomPickerValues = _usernames + CustomPickerValues = _azureInformation!.Usernames }; @@ -197,7 +198,7 @@ public AspNetOptions() Description = AspnetStrings.Options.TenantId.Description, Required = true, PickerType = InteractivePickerType.CustomPicker, - CustomPickerValues = _tenants + CustomPickerValues = _azureInformation!.Tenants }; public static ScaffolderOption Application => new() @@ -216,6 +217,6 @@ public AspNetOptions() Description = AspnetStrings.Options.SelectApplication.Description, Required = false, PickerType = InteractivePickerType.CustomPicker, - CustomPickerValues = _appIds + CustomPickerValues = _azureInformation!.AppIds }; } diff --git a/src/dotnet-scaffolding/dotnet-scaffold/AspNet/Helpers/AzCliHelper.cs b/src/dotnet-scaffolding/dotnet-scaffold/AspNet/Helpers/AzCliHelper.cs index 8deca82d8..776aa4f89 100644 --- a/src/dotnet-scaffolding/dotnet-scaffold/AspNet/Helpers/AzCliHelper.cs +++ b/src/dotnet-scaffolding/dotnet-scaffold/AspNet/Helpers/AzCliHelper.cs @@ -13,60 +13,52 @@ internal class AzCliHelper /// /// Gets Azure usernames, tenant IDs, and application IDs using the Azure CLI. /// - /// the user IDs - /// the tenant IDs - /// the app IDs - /// if successful, return true - public static bool GetAzureInformation(out List usernames, out List tenants, out List appIds) + /// non null AzureInformation if the commands run successfully + public static async Task GetAzureInformationAsync() { // Create a runner to execute the 'az account list' command with json output format var runner = AzCliRunner.Create(); - - if (EnsureUserIsLoggedIn(runner, out string? output) && !string.IsNullOrEmpty(output)) + (bool isUserLoggedIn, string? output) = await EnsureUserIsLoggedInAsync(runner); + if (isUserLoggedIn && !string.IsNullOrEmpty(output)) { - if (GetAzureUsernamesAndTenatIds(runner, output, out usernames, out tenants)) + if (GetAzureUsernamesAndTenatIds(runner, output, out List usernames, out List tenants)) { - if (GetAzureAppIds(runner, out appIds)) + (bool areAppIdSuccessful, List appIds) = await GetAzureAppIdsAsync(runner); + if (areAppIdSuccessful) { - return true; + return new AzureInformation(usernames, tenants, appIds); } } } - usernames = []; - tenants = []; - appIds = []; - return false; + return null; } /// /// Ensures the user is logged into Azure CLI. If not logged in, it will prompt for login. /// /// the az cli runner - /// the CLI output if available - /// if successful, return true - private static bool EnsureUserIsLoggedIn(AzCliRunner runner, out string? output) + /// if successful, return true and the output if applicable + private static async Task<(bool success, string? output)> EnsureUserIsLoggedInAsync(AzCliRunner runner) { try { - int exitCode = runner.RunAzCli("account list --output json", out var stdOut, out var stdErr); + (int exitCode, string? stdOut, string? stdErr) = await runner.RunAzCliAsync("account list --output json"); if (stdOut is not null) { var result = StringUtil.ConvertStringToArray(stdOut); if (result.Length is 0) { - exitCode = runner.RunAzCli("login", out stdOut, out stdErr); + (exitCode, stdOut, stdErr) = await runner.RunAzCliAsync("login"); } } - output = stdOut; - return exitCode == 0 && string.IsNullOrEmpty(stdErr); + return (exitCode == 0 && string.IsNullOrEmpty(stdErr), stdOut); } catch (Exception ex) { - output = null; AnsiConsole.WriteLine($"Error checking Azure login status: {ex.Message}"); - return false; + return (false, null); } } @@ -132,15 +124,13 @@ private static bool GetAzureUsernamesAndTenatIds(AzCliRunner runner, string outp /// Gets Azure application IDs using the Azure CLI. /// /// the az cli runner - /// the appIds - /// if successful, returns true - private static bool GetAzureAppIds(AzCliRunner runner, out List appIds) + /// if successful, returns true with the appIds if retrieved + private static async Task<(bool, List appIds)> GetAzureAppIdsAsync(AzCliRunner runner) { try { - - appIds = []; - var exitCode = runner.RunAzCli("ad app list --output json", out string? stdOut, out string? stdErr); + List appIds = []; + (int exitCode, string? stdOut, string? stdErr) = await runner.RunAzCliAsync("ad app list --output json"); if (exitCode == 0 && !string.IsNullOrEmpty(stdOut)) { @@ -166,7 +156,7 @@ private static bool GetAzureAppIds(AzCliRunner runner, out List appIds) } } } - return true; + return (true, appIds); } } @@ -177,10 +167,9 @@ private static bool GetAzureAppIds(AzCliRunner runner, out List appIds) } catch (Exception ex) { - appIds = []; // Handle any exceptions, like az CLI not being installed AnsiConsole.WriteLine($"Error getting Azure apps: {ex.Message}"); } - return false; + return (false, []); } } diff --git a/src/dotnet-scaffolding/dotnet-scaffold/AspNet/Helpers/AzureAccountInformation.cs b/src/dotnet-scaffolding/dotnet-scaffold/AspNet/Helpers/AzureAccountInformation.cs new file mode 100644 index 000000000..88f752377 --- /dev/null +++ b/src/dotnet-scaffolding/dotnet-scaffold/AspNet/Helpers/AzureAccountInformation.cs @@ -0,0 +1,6 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +namespace Microsoft.DotNet.Tools.Scaffold.AspNet.Helpers; + +internal record AzureInformation(List Usernames, List Tenants, List AppIds); diff --git a/src/dotnet-scaffolding/dotnet-scaffold/AspNet/ScaffoldSteps/UpdateAppAuthorizationStep.cs b/src/dotnet-scaffolding/dotnet-scaffold/AspNet/ScaffoldSteps/UpdateAppAuthorizationStep.cs index 50126143b..28b70bcc9 100644 --- a/src/dotnet-scaffolding/dotnet-scaffold/AspNet/ScaffoldSteps/UpdateAppAuthorizationStep.cs +++ b/src/dotnet-scaffolding/dotnet-scaffold/AspNet/ScaffoldSteps/UpdateAppAuthorizationStep.cs @@ -28,11 +28,11 @@ public UpdateAppAuthorizationStep(ILogger logger, IF _telemetryService = telemetryService; } - public override Task ExecuteAsync(ScaffolderContext context, CancellationToken cancellationToken = default) + public override async Task ExecuteAsync(ScaffolderContext context, CancellationToken cancellationToken = default) { if (string.IsNullOrEmpty(ClientId)) { - return Task.FromResult(false); + return false; } if (AutoConfigureLocalUrls) @@ -59,16 +59,16 @@ public override Task ExecuteAsync(ScaffolderContext context, CancellationT command += $" --public-client-redirect-uris {spaUrisJson}"; } - var exitCode = runner.RunAzCli(command, out var stdOut, out var stdErr); + (int exitCode, string? stdOut, string? stdErr) = await runner.RunAzCliAsync(command); if (exitCode != 0 || !string.IsNullOrEmpty(stdErr)) { _logger.LogError($"Failed to update app registration: {stdErr}"); - return Task.FromResult(false); + return false; } _logger.LogInformation($"Updated App registration with ID token configuration and redirect URIs"); - return Task.FromResult(true); + return true; } private void ConfigureLocalRedirectUris(string projectPath) diff --git a/src/dotnet-scaffolding/dotnet-scaffold/Aspire/AspireCommandService.cs b/src/dotnet-scaffolding/dotnet-scaffold/Aspire/AspireCommandService.cs index e2744e0a5..5533e0dd9 100644 --- a/src/dotnet-scaffolding/dotnet-scaffold/Aspire/AspireCommandService.cs +++ b/src/dotnet-scaffolding/dotnet-scaffold/Aspire/AspireCommandService.cs @@ -74,4 +74,10 @@ public void AddScaffolderCommands() .WithStorageAddPackageSteps() .WithStorageCodeModificationSteps(); } + + public Task AddScaffolderCommandsAsync() + { + //ignore + throw new NotImplementedException(); + } } diff --git a/src/dotnet-scaffolding/dotnet-scaffold/Command/ICommandService.cs b/src/dotnet-scaffolding/dotnet-scaffold/Command/ICommandService.cs index e67d52d8c..08e322e97 100644 --- a/src/dotnet-scaffolding/dotnet-scaffold/Command/ICommandService.cs +++ b/src/dotnet-scaffolding/dotnet-scaffold/Command/ICommandService.cs @@ -7,6 +7,8 @@ internal interface ICommandService { void AddScaffolderCommands(); + Task AddScaffolderCommandsAsync(); + Type[] GetScaffoldSteps(); } } diff --git a/src/dotnet-scaffolding/dotnet-scaffold/Program.cs b/src/dotnet-scaffolding/dotnet-scaffold/Program.cs index 9daa58848..7e5675e3e 100644 --- a/src/dotnet-scaffolding/dotnet-scaffold/Program.cs +++ b/src/dotnet-scaffolding/dotnet-scaffold/Program.cs @@ -23,11 +23,13 @@ Option nonInteractiveOption = nonInteractiveScaffoldOption.ToCliOption(); AspireCommandService aspireCommandService = new(builder); + +//aspire command adding does not need to be async aspireCommandService.AddScaffolderCommands(); ConfigureCommandSteps(builder.Services, aspireCommandService); AspNetCommandService aspNetCommandService = new(builder); -aspNetCommandService.AddScaffolderCommands(); +await aspNetCommandService.AddScaffolderCommandsAsync(); ConfigureCommandSteps(builder.Services, aspNetCommandService); IScaffoldRunner runner = builder.Build(); From 762496248cf817b4f2ef1acdf6637e8baa4c3f37 Mon Sep 17 00:00:00 2001 From: haileymck <111816896+haileymck@users.noreply.github.com> Date: Fri, 10 Oct 2025 16:29:41 -0700 Subject: [PATCH 2/2] pass cancellation token --- .../CliHelpers/AzCliRunner.cs | 21 +++++++++++++++---- .../AspNet/AspNetCommandService.cs | 4 ++-- .../AspNet/Commands/AspNetOptions.cs | 4 ++-- .../AspNet/Helpers/AzCliHelper.cs | 18 +++++++++------- .../UpdateAppAuthorizationStep.cs | 2 +- .../Aspire/AspireCommandService.cs | 2 +- .../Command/ICommandService.cs | 2 +- .../dotnet-scaffold/Program.cs | 8 ++++++- 8 files changed, 41 insertions(+), 20 deletions(-) diff --git a/src/dotnet-scaffolding/Microsoft.DotNet.Scaffolding.Internal/CliHelpers/AzCliRunner.cs b/src/dotnet-scaffolding/Microsoft.DotNet.Scaffolding.Internal/CliHelpers/AzCliRunner.cs index fe6b28935..b3103173d 100644 --- a/src/dotnet-scaffolding/Microsoft.DotNet.Scaffolding.Internal/CliHelpers/AzCliRunner.cs +++ b/src/dotnet-scaffolding/Microsoft.DotNet.Scaffolding.Internal/CliHelpers/AzCliRunner.cs @@ -16,7 +16,7 @@ public static AzCliRunner Create(string? commandName = null) return new AzCliRunner(commandName); } - public async Task<(int ExitCode, string? StdOut, string? StdErr)> RunAzCliAsync(string arguments) + public async Task<(int ExitCode, string? StdOut, string? StdErr)> RunAzCliAsync(string arguments, CancellationToken cancellationToken) { using var outStream = new ProcessOutputStreamReader(); using var errStream = new ProcessOutputStreamReader(); @@ -48,11 +48,24 @@ public static AzCliRunner Create(string? commandName = null) Task taskOut = outStream.BeginRead(process.StandardOutput); Task taskErr = errStream.BeginRead(process.StandardError); - // Wait for process to exit with a timeout (e.g., 30 seconds) + // Wait for process to exit with a timeout (e.g., 30 seconds) or cancellation const int timeoutMilliseconds = 30000; - bool exited = process.WaitForExit(timeoutMilliseconds); + Task waitForExitTask = Task.Run(() => process.WaitForExit(timeoutMilliseconds), cancellationToken); + try + { + await Task.WhenAny(waitForExitTask, Task.Delay(Timeout.Infinite, cancellationToken)); + } + catch (OperationCanceledException) + { + try + { + process.Kill(entireProcessTree: true); + } + catch { } + throw; + } - if (!exited) + if (!waitForExitTask.Result) { try { diff --git a/src/dotnet-scaffolding/dotnet-scaffold/AspNet/AspNetCommandService.cs b/src/dotnet-scaffolding/dotnet-scaffold/AspNet/AspNetCommandService.cs index 84221679e..eda9df909 100644 --- a/src/dotnet-scaffolding/dotnet-scaffold/AspNet/AspNetCommandService.cs +++ b/src/dotnet-scaffolding/dotnet-scaffold/AspNet/AspNetCommandService.cs @@ -44,7 +44,7 @@ public Type[] GetScaffoldSteps() ]; } - public async Task AddScaffolderCommandsAsync() + public async Task AddScaffolderCommandsAsync(CancellationToken cancellationToken) { _builder.AddScaffolder(ScaffolderCatagory.AspNet, AspnetStrings.Blazor.Empty) .WithDisplayName(AspnetStrings.Blazor.EmptyDisplayName) @@ -309,7 +309,7 @@ public async Task AddScaffolderCommandsAsync() .WithIdentityTextTemplatingStep() .WithIdentityCodeChangeStep(); - AspNetOptions options = await AspNetOptions.CreateAsync(); + AspNetOptions options = await AspNetOptions.CreateAsync(cancellationToken); if (options.AreAzCliCommandsSuccessful()) { diff --git a/src/dotnet-scaffolding/dotnet-scaffold/AspNet/Commands/AspNetOptions.cs b/src/dotnet-scaffolding/dotnet-scaffold/AspNet/Commands/AspNetOptions.cs index c2ce898f5..b086398c1 100644 --- a/src/dotnet-scaffolding/dotnet-scaffold/AspNet/Commands/AspNetOptions.cs +++ b/src/dotnet-scaffolding/dotnet-scaffold/AspNet/Commands/AspNetOptions.cs @@ -172,9 +172,9 @@ private AspNetOptions(AzureInformation? azureInformation) _azureInformation = azureInformation; } - public static async Task CreateAsync() + public static async Task CreateAsync(CancellationToken cancellationToken) { - AzureInformation? azureInfo = await AzCliHelper.GetAzureInformationAsync(); + AzureInformation? azureInfo = await AzCliHelper.GetAzureInformationAsync(cancellationToken); return new AspNetOptions(azureInfo); } diff --git a/src/dotnet-scaffolding/dotnet-scaffold/AspNet/Helpers/AzCliHelper.cs b/src/dotnet-scaffolding/dotnet-scaffold/AspNet/Helpers/AzCliHelper.cs index 776aa4f89..93cc31a71 100644 --- a/src/dotnet-scaffolding/dotnet-scaffold/AspNet/Helpers/AzCliHelper.cs +++ b/src/dotnet-scaffolding/dotnet-scaffold/AspNet/Helpers/AzCliHelper.cs @@ -14,16 +14,16 @@ internal class AzCliHelper /// Gets Azure usernames, tenant IDs, and application IDs using the Azure CLI. /// /// non null AzureInformation if the commands run successfully - public static async Task GetAzureInformationAsync() + public static async Task GetAzureInformationAsync(CancellationToken cancellationToken) { // Create a runner to execute the 'az account list' command with json output format var runner = AzCliRunner.Create(); - (bool isUserLoggedIn, string? output) = await EnsureUserIsLoggedInAsync(runner); + (bool isUserLoggedIn, string? output) = await EnsureUserIsLoggedInAsync(runner, cancellationToken); if (isUserLoggedIn && !string.IsNullOrEmpty(output)) { if (GetAzureUsernamesAndTenatIds(runner, output, out List usernames, out List tenants)) { - (bool areAppIdSuccessful, List appIds) = await GetAzureAppIdsAsync(runner); + (bool areAppIdSuccessful, List appIds) = await GetAzureAppIdsAsync(runner, cancellationToken); if (areAppIdSuccessful) { return new AzureInformation(usernames, tenants, appIds); @@ -38,19 +38,20 @@ internal class AzCliHelper /// Ensures the user is logged into Azure CLI. If not logged in, it will prompt for login. /// /// the az cli runner + /// the cancellation token /// if successful, return true and the output if applicable - private static async Task<(bool success, string? output)> EnsureUserIsLoggedInAsync(AzCliRunner runner) + private static async Task<(bool success, string? output)> EnsureUserIsLoggedInAsync(AzCliRunner runner, CancellationToken cancellationToken) { try { - (int exitCode, string? stdOut, string? stdErr) = await runner.RunAzCliAsync("account list --output json"); + (int exitCode, string? stdOut, string? stdErr) = await runner.RunAzCliAsync("account list --output json", cancellationToken); if (stdOut is not null) { var result = StringUtil.ConvertStringToArray(stdOut); if (result.Length is 0) { - (exitCode, stdOut, stdErr) = await runner.RunAzCliAsync("login"); + (exitCode, stdOut, stdErr) = await runner.RunAzCliAsync("login", cancellationToken); } } return (exitCode == 0 && string.IsNullOrEmpty(stdErr), stdOut); @@ -124,13 +125,14 @@ private static bool GetAzureUsernamesAndTenatIds(AzCliRunner runner, string outp /// Gets Azure application IDs using the Azure CLI. /// /// the az cli runner + /// the cancellation token /// if successful, returns true with the appIds if retrieved - private static async Task<(bool, List appIds)> GetAzureAppIdsAsync(AzCliRunner runner) + private static async Task<(bool, List appIds)> GetAzureAppIdsAsync(AzCliRunner runner, CancellationToken cancellationToken) { try { List appIds = []; - (int exitCode, string? stdOut, string? stdErr) = await runner.RunAzCliAsync("ad app list --output json"); + (int exitCode, string? stdOut, string? stdErr) = await runner.RunAzCliAsync("ad app list --output json", cancellationToken); if (exitCode == 0 && !string.IsNullOrEmpty(stdOut)) { diff --git a/src/dotnet-scaffolding/dotnet-scaffold/AspNet/ScaffoldSteps/UpdateAppAuthorizationStep.cs b/src/dotnet-scaffolding/dotnet-scaffold/AspNet/ScaffoldSteps/UpdateAppAuthorizationStep.cs index 28b70bcc9..b32192ef8 100644 --- a/src/dotnet-scaffolding/dotnet-scaffold/AspNet/ScaffoldSteps/UpdateAppAuthorizationStep.cs +++ b/src/dotnet-scaffolding/dotnet-scaffold/AspNet/ScaffoldSteps/UpdateAppAuthorizationStep.cs @@ -59,7 +59,7 @@ public override async Task ExecuteAsync(ScaffolderContext context, Cancell command += $" --public-client-redirect-uris {spaUrisJson}"; } - (int exitCode, string? stdOut, string? stdErr) = await runner.RunAzCliAsync(command); + (int exitCode, string? stdOut, string? stdErr) = await runner.RunAzCliAsync(command, cancellationToken); if (exitCode != 0 || !string.IsNullOrEmpty(stdErr)) { _logger.LogError($"Failed to update app registration: {stdErr}"); diff --git a/src/dotnet-scaffolding/dotnet-scaffold/Aspire/AspireCommandService.cs b/src/dotnet-scaffolding/dotnet-scaffold/Aspire/AspireCommandService.cs index 5533e0dd9..cb91bb529 100644 --- a/src/dotnet-scaffolding/dotnet-scaffold/Aspire/AspireCommandService.cs +++ b/src/dotnet-scaffolding/dotnet-scaffold/Aspire/AspireCommandService.cs @@ -75,7 +75,7 @@ public void AddScaffolderCommands() .WithStorageCodeModificationSteps(); } - public Task AddScaffolderCommandsAsync() + public Task AddScaffolderCommandsAsync(CancellationToken cancellationToken) { //ignore throw new NotImplementedException(); diff --git a/src/dotnet-scaffolding/dotnet-scaffold/Command/ICommandService.cs b/src/dotnet-scaffolding/dotnet-scaffold/Command/ICommandService.cs index 08e322e97..42cc6a80f 100644 --- a/src/dotnet-scaffolding/dotnet-scaffold/Command/ICommandService.cs +++ b/src/dotnet-scaffolding/dotnet-scaffold/Command/ICommandService.cs @@ -7,7 +7,7 @@ internal interface ICommandService { void AddScaffolderCommands(); - Task AddScaffolderCommandsAsync(); + Task AddScaffolderCommandsAsync(CancellationToken cancellationToken); Type[] GetScaffoldSteps(); } diff --git a/src/dotnet-scaffolding/dotnet-scaffold/Program.cs b/src/dotnet-scaffolding/dotnet-scaffold/Program.cs index 7e5675e3e..d1ef27ca1 100644 --- a/src/dotnet-scaffolding/dotnet-scaffold/Program.cs +++ b/src/dotnet-scaffolding/dotnet-scaffold/Program.cs @@ -15,6 +15,12 @@ IScaffoldRunnerBuilder builder = Host.CreateScaffoldBuilder(); +using var cts = new CancellationTokenSource(); +Console.CancelKeyPress += (sender, e) => { + e.Cancel = true; + cts.Cancel(); +}; + ConfigureServices(builder.Services); ConfigureSharedSteps(builder.Services); @@ -29,7 +35,7 @@ ConfigureCommandSteps(builder.Services, aspireCommandService); AspNetCommandService aspNetCommandService = new(builder); -await aspNetCommandService.AddScaffolderCommandsAsync(); +await aspNetCommandService.AddScaffolderCommandsAsync(cts.Token); ConfigureCommandSteps(builder.Services, aspNetCommandService); IScaffoldRunner runner = builder.Build();