From bd1a91a6869353f4ba2c37f13b45e8cc03df83bf Mon Sep 17 00:00:00 2001 From: Thomas Jespersen Date: Wed, 27 May 2026 23:44:41 +0200 Subject: [PATCH 1/3] Show duration in quiet mode output for all CLI commands --- developer-cli/Commands/BuildCommand.cs | 2 +- developer-cli/Commands/End2EndCommand.cs | 9 ++++++++- developer-cli/Commands/FormatCommand.cs | 2 +- developer-cli/Commands/LintCommand.cs | 2 +- 4 files changed, 11 insertions(+), 4 deletions(-) diff --git a/developer-cli/Commands/BuildCommand.cs b/developer-cli/Commands/BuildCommand.cs index 8c883ff90f..eda6203e2b 100644 --- a/developer-cli/Commands/BuildCommand.cs +++ b/developer-cli/Commands/BuildCommand.cs @@ -108,7 +108,7 @@ private static void Execute(bool backend, bool frontend, bool emails, bool devel if (quiet) { - Console.WriteLine("Build succeeded."); + Console.WriteLine($"Build succeeded in {Stopwatch.GetElapsedTime(startTime).Format()}."); } else { diff --git a/developer-cli/Commands/End2EndCommand.cs b/developer-cli/Commands/End2EndCommand.cs index 551455f2af..f972743288 100644 --- a/developer-cli/Commands/End2EndCommand.cs +++ b/developer-cli/Commands/End2EndCommand.cs @@ -226,7 +226,14 @@ private static void Execute( stopwatch.Stop(); - if (!quiet) + if (quiet) + { + Console.WriteLine(overallSuccess + ? $"All tests completed in {stopwatch.Elapsed.TotalSeconds:F1}s." + : $"Some tests failed in {stopwatch.Elapsed.TotalSeconds:F1}s." + ); + } + else { AnsiConsole.MarkupLine(overallSuccess ? $"[green]All tests completed in {stopwatch.Elapsed.TotalSeconds:F1} seconds[/]" diff --git a/developer-cli/Commands/FormatCommand.cs b/developer-cli/Commands/FormatCommand.cs index e4e085c205..238f355cb8 100644 --- a/developer-cli/Commands/FormatCommand.cs +++ b/developer-cli/Commands/FormatCommand.cs @@ -86,7 +86,7 @@ private static void Execute(bool backend, bool frontend, bool developerCli, stri if (quiet) { - Console.WriteLine("Code formatted successfully."); + Console.WriteLine($"Code formatted successfully in {Stopwatch.GetElapsedTime(startTime).Format()}."); } else { diff --git a/developer-cli/Commands/LintCommand.cs b/developer-cli/Commands/LintCommand.cs index a38b95ecc3..d066941223 100644 --- a/developer-cli/Commands/LintCommand.cs +++ b/developer-cli/Commands/LintCommand.cs @@ -90,7 +90,7 @@ private static void Execute(bool backend, bool frontend, bool developerCli, stri Environment.Exit(1); } - Console.WriteLine("Linting completed successfully. No issues found."); + Console.WriteLine($"Linting completed successfully in {Stopwatch.GetElapsedTime(startTime).Format()}. No issues found."); } else { From 3430893a784df6a012ff4008bce0795f10d0c61f Mon Sep 17 00:00:00 2001 From: Thomas Jespersen Date: Wed, 27 May 2026 23:44:45 +0200 Subject: [PATCH 2/3] Include Migrate Database workflow in deploy confirmation prompt --- developer-cli/Commands/DeployCommand.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/developer-cli/Commands/DeployCommand.cs b/developer-cli/Commands/DeployCommand.cs index cef772b370..83abf5d350 100644 --- a/developer-cli/Commands/DeployCommand.cs +++ b/developer-cli/Commands/DeployCommand.cs @@ -577,7 +577,7 @@ [bold]Please review planned changes before continuing.[/] [yellow]** All variables can be changed on the GitHub Settings page. For example, if you want to deploy production or staging to different locations.[/] - 4. Disable the reusable GitHub workflows [blue]Deploy Container[/] and [blue]Plan and Deploy Infrastructure[/]. + 4. Disable the reusable GitHub workflows [blue]Deploy Container[/], [blue]Deploy Infrastructure[/], and [blue]Migrate Database[/]. 5. The [blue]Cloud Infrastructure - Deployment[/] GitHub Actions will be triggered deployment of Azure Infrastructure. This will take [yellow]between 15 and 45 minutes[/]. From d8bbb96cab8a2e95c3a47736596033794cf7d957 Mon Sep 17 00:00:00 2001 From: Thomas Jespersen Date: Wed, 27 May 2026 23:44:50 +0200 Subject: [PATCH 3/3] Add source state cache to skip redundant format and lint runs --- developer-cli/Commands/FormatCommand.cs | 17 +++++++ developer-cli/Commands/LintCommand.cs | 17 +++++++ developer-cli/Utilities/SourceStateCache.cs | 51 +++++++++++++++++++++ 3 files changed, 85 insertions(+) create mode 100644 developer-cli/Utilities/SourceStateCache.cs diff --git a/developer-cli/Commands/FormatCommand.cs b/developer-cli/Commands/FormatCommand.cs index 238f355cb8..4f0695d478 100644 --- a/developer-cli/Commands/FormatCommand.cs +++ b/developer-cli/Commands/FormatCommand.cs @@ -52,6 +52,21 @@ private static void Execute(bool backend, bool frontend, bool developerCli, stri try { + const string cacheKey = "format"; + if (SourceStateCache.IsUpToDate(cacheKey)) + { + if (quiet) + { + Console.WriteLine("No changes since last format run, skipping."); + } + else + { + AnsiConsole.MarkupLine("[green]No changes since last format run, skipping.[/]"); + } + + return; + } + var initialUncommittedFiles = quiet ? null : GitHelper.GetChangedFiles(); if (!quiet && initialUncommittedFiles!.Count > 0) { @@ -84,6 +99,8 @@ private static void Execute(bool backend, bool frontend, bool developerCli, stri developerCliTime = Stopwatch.GetElapsedTime(startTime) - backendTime - frontendTime; } + SourceStateCache.Save(cacheKey); + if (quiet) { Console.WriteLine($"Code formatted successfully in {Stopwatch.GetElapsedTime(startTime).Format()}."); diff --git a/developer-cli/Commands/LintCommand.cs b/developer-cli/Commands/LintCommand.cs index d066941223..6237186605 100644 --- a/developer-cli/Commands/LintCommand.cs +++ b/developer-cli/Commands/LintCommand.cs @@ -53,6 +53,21 @@ private static void Execute(bool backend, bool frontend, bool developerCli, stri try { + const string cacheKey = "lint"; + if (SourceStateCache.IsUpToDate(cacheKey)) + { + if (quiet) + { + Console.WriteLine("No changes since last lint run, skipping."); + } + else + { + AnsiConsole.MarkupLine("[green]No changes since last lint run, skipping.[/]"); + } + + return; + } + var startTime = Stopwatch.GetTimestamp(); var backendTime = TimeSpan.Zero; var frontendTime = TimeSpan.Zero; @@ -82,6 +97,8 @@ private static void Execute(bool backend, bool frontend, bool developerCli, stri developerCliTime = Stopwatch.GetElapsedTime(startTime) - backendTime - frontendTime; } + if (!hasIssues) SourceStateCache.Save(cacheKey); + if (quiet) { if (hasIssues) diff --git a/developer-cli/Utilities/SourceStateCache.cs b/developer-cli/Utilities/SourceStateCache.cs new file mode 100644 index 0000000000..fab934af67 --- /dev/null +++ b/developer-cli/Utilities/SourceStateCache.cs @@ -0,0 +1,51 @@ +using System.Security.Cryptography; +using System.Text; +using DeveloperCli.Installation; + +namespace DeveloperCli.Utilities; + +public static class SourceStateCache +{ + private static readonly string CacheDirectory = Path.Combine(Configuration.WorkspaceFolder, "developer-cli", "cache"); + + public static bool IsUpToDate(string cacheKey) + { + var cachePath = GetCachePath(cacheKey); + if (!File.Exists(cachePath)) return false; + + try + { + var savedState = File.ReadAllText(cachePath).Trim(); + return savedState == ComputeStateKey(); + } + catch + { + return false; + } + } + + public static void Save(string cacheKey) + { + try + { + Directory.CreateDirectory(CacheDirectory); + File.WriteAllText(GetCachePath(cacheKey), ComputeStateKey()); + } + catch + { + // Caching is best-effort; failures should not break the workflow + } + } + + private static string GetCachePath(string cacheKey) + { + return Path.Combine(CacheDirectory, $"{cacheKey}.hash"); + } + + private static string ComputeStateKey() + { + var head = ProcessHelper.StartProcess("git rev-parse HEAD", Configuration.SourceCodeFolder, true, exitOnError: false).Trim(); + var stash = ProcessHelper.StartProcess("git stash create", Configuration.SourceCodeFolder, true, exitOnError: false).Trim(); + return Convert.ToHexStringLower(SHA256.HashData(Encoding.UTF8.GetBytes($"{head}:{stash}"))); + } +}