From dd5688efd61cdd6d51644b40390e91134ba93053 Mon Sep 17 00:00:00 2001 From: David Sekula Date: Wed, 8 May 2024 13:59:06 +0100 Subject: [PATCH] 183 StateManagement breaks existing non-interactive build with disabled secrets (#184) * Cleanup state handling for different input modes Ensures that state management works in none interactive mode, and also add the disable state option to the BaseCommand options (doh! - missed that one). Fixes #183 * Save the state file still, as thats where secrets now live. Just dont load all state when --disable-state is true. If no secrets - dont save state if its disabled * Skip if secrets null --- src/Aspirate.Commands/Commands/BaseCommand.cs | 1 + .../Implementations/StateService.cs | 74 ++++++++++++++----- 2 files changed, 55 insertions(+), 20 deletions(-) diff --git a/src/Aspirate.Commands/Commands/BaseCommand.cs b/src/Aspirate.Commands/Commands/BaseCommand.cs index e211be8..db49529 100644 --- a/src/Aspirate.Commands/Commands/BaseCommand.cs +++ b/src/Aspirate.Commands/Commands/BaseCommand.cs @@ -13,6 +13,7 @@ protected BaseCommand(string name, string description) { AddOption(NonInteractiveOption.Instance); AddOption(DisableSecretsOption.Instance); + AddOption(DisableStateOption.Instance); Handler = CommandHandler.Create(ConstructCommand); } diff --git a/src/Aspirate.Services/Implementations/StateService.cs b/src/Aspirate.Services/Implementations/StateService.cs index 2a881ff..1c5ce9b 100644 --- a/src/Aspirate.Services/Implementations/StateService.cs +++ b/src/Aspirate.Services/Implementations/StateService.cs @@ -15,7 +15,7 @@ public class StateService(IFileSystem fs, IAnsiConsole logger, ISecretProvider s public async Task SaveState(StateManagementOptions options) { - if (options.DisableState == true) + if (options.DisableState == true && (options.State.SecretState is null || options.State.SecretState.Secrets.Count == 0)) { return; } @@ -30,51 +30,85 @@ public async Task RestoreState(StateManagementOptions options) { logger.WriteRuler("[purple]Handling Aspirate State[/]"); - if (options.DisableState == true) + if (ShouldCancelAsStateFileDoesNotExist(out var stateFile)) { - logger.MarkupLine("State has been [red]disabled[/] for this run."); return; } - var stateFile = fs.Path.Combine(fs.Directory.GetCurrentDirectory(), AspirateLiterals.StateFileName); + if (await IsNonInteractiveMode(options, stateFile)) + { + return; + } - if (options.NonInteractive == true) + if (options.DisableState == true) { - await RestoreState(options, stateFile, true); - logger.MarkupLine($"[green]({EmojiLiterals.CheckMark}) Done: [/] State loaded successfully from [blue]{stateFile}[/]"); + await OnlyRestoreSecrets(options, stateFile); return; } - if (!fs.File.Exists(stateFile)) + if (await ShouldUseAllPreviousState(options, stateFile)) { return; } + await OnlyRestoreSecrets(options, stateFile); + } + + private Task IsNonInteractiveMode(StateManagementOptions options, string stateFile) => + options switch + { + { NonInteractive: true, DisableState: true } => OnlyRestoreSecrets(options, stateFile), + { NonInteractive: true, DisableState: false or null } => RestoreAllState(options, stateFile), + _ => Task.FromResult(false) + }; + + private bool ShouldCancelAsStateFileDoesNotExist(out string stateFile) + { + stateFile = fs.Path.Combine(fs.Directory.GetCurrentDirectory(), AspirateLiterals.StateFileName); + + return !fs.File.Exists(stateFile); + } + + private async Task ShouldUseAllPreviousState(StateManagementOptions options, string stateFile) + { logger.MarkupLine($"[bold]Loading state from [blue]{stateFile}[/].[/]"); var shouldUseAllPreviousState = logger.Confirm("Would you like to use all previous state values, and [blue]skip[/] re-prompting where possible ?"); - if (shouldUseAllPreviousState) - { - logger.MarkupLine("[bold]Using all previous state values, and skipping re-prompting.[/]"); - await RestoreState(options, stateFile, true); - options.State.UseAllPreviousStateValues = true; - } - else + if (!shouldUseAllPreviousState) { - logger.MarkupLine("[bold]Re-prompting for all state values not specified on command line.[/]"); - await RestoreState(options, stateFile, false); - options.State.UseAllPreviousStateValues = false; + return false; } - logger.MarkupLine($"[green]({EmojiLiterals.CheckMark}) Done: [/] State loaded successfully from [blue]{stateFile}[/]"); + await RestoreAllState(options, stateFile); + return true; + } + + private async Task RestoreAllState(StateManagementOptions options, string stateFile) + { + await RestoreState(options, stateFile, true); + LogAllStateReloaded(stateFile); + return true; + } + + private async Task OnlyRestoreSecrets(StateManagementOptions options, string stateFile) + { + await RestoreState(options, stateFile, false); + LogDisabledStateMessage(); + return true; } private async Task RestoreState(StateManagementOptions options, string stateFile, bool shouldUseAllPreviousStateValues) { var stateAsJson = await fs.File.ReadAllTextAsync(stateFile); var previousState = JsonSerializer.Deserialize(stateAsJson, _jsonSerializerOptions); - options.State.ReplaceCurrentStateWithPreviousState(previousState, shouldUseAllPreviousStateValues); + options.State.UseAllPreviousStateValues = shouldUseAllPreviousStateValues; } + + private void LogDisabledStateMessage() => + logger.MarkupLine($"[green]({EmojiLiterals.CheckMark}) Done: [/] State has been disabled for this run. Only secrets will be populated."); + + private void LogAllStateReloaded(string stateFile) => + logger.MarkupLine($"[green]({EmojiLiterals.CheckMark}) Done: [/] State loaded successfully from [blue]{stateFile}[/]. Will run without re-prompting for values."); }