private async Task <IProject> GetProjectAsync(IUpgradeContext context, CancellationToken token) { const string SelectProjectQuestion = "Here is the recommended order to upgrade. Select enter to follow this list, or input the project you want to start with."; if (_orderedProjects is null) { throw new UpgradeException("Project selection step must be initialized before it is applied (null _orderedProjects)"); } // No need for an IAsyncEnumerable here since the commands shouldn't be displayed until // all are available anyhow. var commands = new List <ProjectCommand>(); foreach (var project in _orderedProjects) { commands.Add(await CreateProjectCommandAsync(project).ConfigureAwait(false)); } var result = await _input.ChooseAsync(SelectProjectQuestion, commands, token).ConfigureAwait(false); return(result.Project); async Task <ProjectCommand> CreateProjectCommandAsync(IProject project) { var projectCompleted = await IsCompletedAsync(context, project, token).ConfigureAwait(false); var checks = await RunChecksAsync(project, token).ConfigureAwait(false); return(new ProjectCommand(project, projectCompleted, checks)); } }
private async Task <string?> ChooseBackupPath(IUpgradeContext context, CancellationToken token) { var customPath = default(string); var commands = new[] { UpgradeCommand.Create($"Use default path [{_backupPath}]"), UpgradeCommand.Create("Enter custom path", async(ctx, token) => { customPath = await _userInput.AskUserAsync("Please enter a custom path for backups:").ConfigureAwait(false); return(!string.IsNullOrEmpty(customPath)); }) }; while (!token.IsCancellationRequested) { var result = await _userInput.ChooseAsync("Please choose a backup path", commands, token).ConfigureAwait(false); if (await result.ExecuteAsync(context, token).ConfigureAwait(false)) { // customPath may be set in the lambda above. #pragma warning disable CA1508 // Avoid dead conditional code return(customPath ?? _backupPath); #pragma warning restore CA1508 // Avoid dead conditional code } } return(null); }
private async Task RunStepAsync(IUpgradeContext context, UpgradeStep step, CancellationToken token) { token.ThrowIfCancellationRequested(); await _io.Output.WriteLineAsync(); var commands = _commandProvider.GetCommands(step, context); var command = await _input.ChooseAsync("Choose a command:", commands, token); // TODO : It might be nice to allow commands to show more details by having a 'status' property // that can be shown here. Also, commands currently only return bools but, in the future, // if they return more complex objects, custom handlers could be used to respond to the different // commands' return values. if (!await ExecuteAndTimeCommand(context, step, command, token)) { Console.ForegroundColor = ConsoleColor.Yellow; await _io.Output.WriteAsync($"Command ({command.CommandText}) did not succeed"); Console.ResetColor(); } else if (!await _input.WaitToProceedAsync(token)) { _logger.LogWarning("Upgrade process was canceled. Quitting...."); } token.ThrowIfCancellationRequested(); }
public async Task RunAsync(CancellationToken token) { using var context = await _contextFactory.CreateContext(token); await _stateManager.LoadStateAsync(context, token); try { // Cache current steps here as defense-in-depth against the possibility // of a bug (or very weird upgrade step behavior) causing the current step // to reset state after being initialized by GetNextStepAsync var steps = await _upgrader.InitializeAsync(context, token); var step = await _upgrader.GetNextStepAsync(context, token); while (step is not null) { while (!step.IsDone) { token.ThrowIfCancellationRequested(); ShowUpgradeSteps(steps, context, step); _io.Output.WriteLine(); var commands = _commandProvider.GetCommands(step); var command = await _input.ChooseAsync("Choose a command:", commands, token); // TODO : It might be nice to allow commands to show more details by having a 'status' property // that can be shown here. Also, commands currently only return bools but, in the future, // if they return more complex objects, custom handlers could be used to respond to the different // commands' return values. if (!await command.ExecuteAsync(context, token)) { Console.ForegroundColor = ConsoleColor.Yellow; _io.Output.WriteLine($"Command ({command.CommandText}) did not succeed"); Console.ResetColor(); } else if (await _input.WaitToProceedAsync(token)) { ConsoleUtils.Clear(); } else { _logger.LogWarning("Upgrade process was canceled. Quitting...."); return; } } step = await _upgrader.GetNextStepAsync(context, token); } _logger.LogInformation("Upgrade has completed. Please review any changes."); } finally { // Do not pass the same token as it may have been canceled and we still need to persist this. await _stateManager.SaveStateAsync(context, default); } }
private async ValueTask <IProject> GetEntrypointAsync(IUpgradeContext context, CancellationToken token) { const string EntrypointQuestion = "Please select the project you run. We will then analyze the dependencies and identify the recommended order to upgrade projects."; var allProjects = context.Projects.OrderBy(p => p.GetRoslynProject().Name).Select(ProjectCommand.Create).ToList(); var result = await _userInput.ChooseAsync(EntrypointQuestion, allProjects, token).ConfigureAwait(false); return(result.Project); }
public override async Task <bool> ExecuteAsync(IUpgradeContext context, CancellationToken token) { var target = await _userInput.ChooseAsync("Choose log target:", CreateFromEnum <LogTarget>(), token); var result = await _userInput.ChooseAsync("Choose your log level:", CreateFromEnum <LogLevel>(), token); switch (target.Value) { case LogTarget.Console: _logSettings.SetConsoleLevel(result.Value); return(true); case LogTarget.File: _logSettings.SetFileLevel(result.Value); return(true); default: return(false); } }
private async Task <IProject> GetProject(IUpgradeContext context, Func <IUpgradeContext, IProject, bool> isProjectCompleted, CancellationToken token) { const string SelectProjectQuestion = "Here is the recommended order to upgrade. Select enter to follow this list, or input the project you want to start with."; if (context.EntryPoint is null) { throw new InvalidOperationException("Entrypoint must be set before using this step"); } var ordered = context.EntryPoint.PostOrderTraversal(p => p.ProjectReferences).Select(CreateProjectCommand); var result = await _input.ChooseAsync(SelectProjectQuestion, ordered, token).ConfigureAwait(false); return(result.Project); ProjectCommand CreateProjectCommand(IProject project) { return(new ProjectCommand(project, isProjectCompleted(context, project))); } }