public override async Task <int> ExecuteAsync(CommandContext context, DoctorSettings settings) { Console.Title = ToolName; AnsiConsole.MarkupLine($"[underline bold green]{Icon.Ambulance} {ToolName} {Icon.Recommend}[/]"); AnsiConsole.Render(new Rule()); AnsiConsole.MarkupLine("This tool will attempt to evaluate your .NET MAUI development environment."); AnsiConsole.MarkupLine("If problems are detected, this tool may offer the option to try and fix them for you, or suggest a way to fix them yourself."); AnsiConsole.WriteLine(); AnsiConsole.MarkupLine("Thanks for choosing .NET MAUI!"); AnsiConsole.WriteLine(); if (!settings.NonInteractive) { AnsiConsole.Markup("Press any key to start..."); Console.ReadKey(); AnsiConsole.WriteLine(); } AnsiConsole.Render(new Rule()); var clinic = new Clinic(); var cts = new System.Threading.CancellationTokenSource(); var checkupStatus = new Dictionary <string, Doctoring.Status>(); var patientHistory = new PatientHistory(); var diagnoses = new List <Diagonosis>(); var consoleStatus = AnsiConsole.Status(); AnsiConsole.Markup($"[bold blue]{Icon.Thinking} Synchronizing configuration...[/]"); var chart = await Manifest.Chart.FromFileOrUrl(settings.Manifest); var toolVersion = chart?.Doctor?.ToolVersion; var fileVersion = NuGetVersion.Parse(FileVersionInfo.GetVersionInfo(this.GetType().Assembly.Location).FileVersion); if (string.IsNullOrEmpty(toolVersion) || !NuGetVersion.TryParse(toolVersion, out var toolVer) || fileVersion < toolVer) { Console.WriteLine(); AnsiConsole.MarkupLine($"[bold red]{Icon.Error} Updating to version {toolVersion} or newer is required:[/]"); AnsiConsole.MarkupLine($"[red]Update with the following:[/]"); var installCmdVer = string.IsNullOrEmpty(toolVersion) ? "" : $" --version {toolVersion}"; AnsiConsole.Markup($" dotnet tool install --global {ToolPackageId}{installCmdVer}"); return(-1); } AnsiConsole.MarkupLine(" ok"); AnsiConsole.Markup($"[bold blue]{Icon.Thinking} Scheduling appointments...[/]"); if (chart.Doctor.OpenJdk != null) { clinic.OfferService(new OpenJdkCheckup(chart.Doctor.OpenJdk.MinimumVersion, chart.Doctor.OpenJdk.ExactVersion)); } if (chart.Doctor.Android != null) { clinic.OfferService(new AndroidSdkManagerCheckup()); clinic.OfferService(new AndroidSdkPackagesCheckup(chart.Doctor.Android.Packages.ToArray())); clinic.OfferService(new AndroidSdkLicensesCheckup()); } if (chart.Doctor.XCode != null) { clinic.OfferService(new XCodeCheckup(chart.Doctor.XCode.MinimumVersion, chart.Doctor.XCode.ExactVersion)); } if (chart.Doctor.VSMac != null && !string.IsNullOrEmpty(chart.Doctor.VSMac.MinimumVersion)) { clinic.OfferService(new VisualStudioMacCheckup(chart.Doctor.VSMac.MinimumVersion, chart.Doctor.VSMac.ExactVersion)); } if (chart.Doctor.VSWin != null && !string.IsNullOrEmpty(chart.Doctor.VSWin.MinimumVersion)) { clinic.OfferService(new VisualStudioWindowsCheckup(chart.Doctor.VSWin.MinimumVersion, chart.Doctor.VSWin.ExactVersion)); } if (chart.Doctor.DotNet?.Sdks?.Any() ?? false) { clinic.OfferService(new DotNetCheckup(chart.Doctor.DotNet.Sdks.ToArray())); foreach (var sdk in chart.Doctor.DotNet.Sdks) { clinic.OfferService(new DotNetWorkloadsCheckup(sdk.Version, sdk.Workloads.ToArray(), sdk.PackageSources.ToArray())); // Always run the packs checkup even if manifest is empty, since the workloads may resolve some required packs dynamically that aren't from the manifest clinic.OfferService(new DotNetPacksCheckup(sdk.Version, sdk.Packs?.ToArray() ?? Array.Empty <Manifest.NuGetPackage>(), sdk.PackageSources.ToArray())); } } var checkups = clinic.ScheduleCheckups(); AnsiConsole.MarkupLine(" ok"); foreach (var checkup in checkups) { var skipCheckup = false; // Make sure our dependencies succeeded first if (checkup.Dependencies?.Any() ?? false) { foreach (var dep in checkup.Dependencies) { if (!checkupStatus.TryGetValue(dep.CheckupId, out var depStatus) || depStatus != Doctoring.Status.Ok) { skipCheckup = dep.IsRequired; break; } } } if (skipCheckup) { checkupStatus.Add(checkup.Id, Doctoring.Status.Error); AnsiConsole.WriteLine(); AnsiConsole.MarkupLine($"[bold red]{Icon.Error} Skipped: " + checkup.Title + "[/]"); continue; } checkup.OnStatusUpdated += (s, e) => { var msg = ""; if (e.Status == Doctoring.Status.Error) { msg = $"[red]{Icon.Error} {e.Message}[/]"; } else if (e.Status == Doctoring.Status.Warning) { msg = $"[red]{Icon.Error} {e.Message}[/]"; } else if (e.Status == Doctoring.Status.Ok) { msg = $"[green]{Icon.Success} {e.Message}[/]"; } else { msg = $"{Icon.ListItem} {e.Message}"; } AnsiConsole.MarkupLine(" " + msg); }; AnsiConsole.WriteLine(); AnsiConsole.MarkupLine($"[bold]{Icon.Checking} " + checkup.Title + " Checkup[/]..."); Console.Title = checkup.Title; Diagonosis diagnosis = null; try { diagnosis = await checkup.Examine(patientHistory); } catch (Exception ex) { diagnosis = new Diagonosis(Doctoring.Status.Error, checkup, ex.Message); } diagnoses.Add(diagnosis); // Cache the status for dependencies checkupStatus.Add(checkup.Id, diagnosis.Status); if (diagnosis.Status == Doctoring.Status.Ok) { continue; } var statusEmoji = diagnosis.Status == Doctoring.Status.Error ? Icon.Error : Icon.Warning; var statusColor = diagnosis.Status == Doctoring.Status.Error ? "red" : "orange3"; var msg = !string.IsNullOrEmpty(diagnosis.Message) ? " - " + diagnosis.Message : string.Empty; //AnsiConsole.MarkupLine($"[{statusColor}]{statusEmoji} {checkup.Title}{msg}[/]"); if (diagnosis.HasPrescription) { AnsiConsole.MarkupLine($" [bold blue]{Icon.Recommend} Recommendation:[/] {diagnosis.Prescription.Name}"); if (!string.IsNullOrEmpty(diagnosis.Prescription.Description)) { AnsiConsole.MarkupLine(" " + diagnosis.Prescription.Description); } // See if we should fix // needs to have a remedy available to even bother asking/trying var doFix = diagnosis.Prescription.HasRemedy && ( // --fix + --non-interactive == auto fix, no prompt (settings.NonInteractive && settings.Fix) // interactive (default) + prompt/confirm they want to fix || (!settings.NonInteractive && AnsiConsole.Confirm($" [bold]{Icon.Bell} Attempt to fix?[/]")) ); if (doFix) { var isAdmin = Util.IsAdmin(); foreach (var remedy in diagnosis.Prescription.Remedies) { if (!remedy.HasPrivilegesToRun(isAdmin, Util.Platform)) { AnsiConsole.Markup("Fix requires running with adminstrator privileges. Try opening a terminal as administrator and running maui-doctor again."); continue; } try { remedy.OnStatusUpdated += (s, e) => { AnsiConsole.MarkupLine(" " + e.Message); }; AnsiConsole.MarkupLine($"{Icon.Thinking} Attempting to fix: " + checkup.Title); await remedy.Cure(cts.Token); AnsiConsole.MarkupLine(" Fix applied. Run doctor again to verify."); } catch (Exception ex) { AnsiConsole.MarkupLine(" Fix failed - " + ex.Message); } } } } } AnsiConsole.Render(new Rule()); AnsiConsole.WriteLine(); if (diagnoses.Any(d => d.Status == Doctoring.Status.Error)) { AnsiConsole.MarkupLine($"[bold red]{Icon.Bell} There were one or more problems detected.[/]"); AnsiConsole.MarkupLine($"[bold red]Please review the errors and correct them and run maui-doctor again.[/]"); } else { AnsiConsole.MarkupLine($"[bold blue]{Icon.Success} Congratulations, everything looks great![/]"); } Console.Title = ToolName; return(0); }