feat(health): surface pending-updates counts on the Health dashboard and CLI (ScadaBridge#63)

Health page gains PendingUpdatesHealthTiles (stale instances / sites with stale
artifacts, Deployer+Administrator only, site-scope narrowed, shared cache). New
GetPendingUpdates management command (Deployer) and 'health pending-updates' CLI
leaf. Flatten/cache computation untouched; the command reads
IPendingUpdatesEvaluator directly because the UI cache is not reachable from
ManagementService.

Claude-Session: https://claude.ai/code/session_01GT67AEHd2Acv1YPwGfXEoV
This commit is contained in:
Joseph Doherty
2026-11-09 13:18:54 -05:00
parent 004bf3cf06
commit d50433410d
15 changed files with 447 additions and 2 deletions
@@ -19,6 +19,7 @@ public static class HealthCommands
var command = new Command("health") { Description = "Health monitoring" };
command.Add(BuildSummary(urlOption, formatOption, usernameOption, passwordOption));
command.Add(BuildPendingUpdates(urlOption, formatOption, usernameOption, passwordOption));
command.Add(BuildSite(urlOption, formatOption, usernameOption, passwordOption));
command.Add(BuildEventLog(urlOption, formatOption, usernameOption, passwordOption));
command.Add(BuildParkedMessages(urlOption, formatOption, usernameOption, passwordOption));
@@ -37,6 +38,20 @@ public static class HealthCommands
return cmd;
}
private static Command BuildPendingUpdates(Option<string> urlOption, Option<string> formatOption, Option<string> usernameOption, Option<string> passwordOption)
{
var cmd = new Command("pending-updates")
{
Description = "Get fleet-wide pending-updates counts (stale instances, sites with stale artifacts)"
};
cmd.SetAction(async (ParseResult result) =>
{
return await CommandHelpers.ExecuteCommandAsync(
result, urlOption, formatOption, usernameOption, passwordOption, new GetPendingUpdatesCommand());
});
return cmd;
}
private static Command BuildSite(Option<string> urlOption, Option<string> formatOption, Option<string> usernameOption, Option<string> passwordOption)
{
var identifierOption = new Option<string>("--identifier") { Description = "Site identifier", Required = true };
+8
View File
@@ -1810,6 +1810,14 @@ Return the current health state for all known sites as a JSON object keyed by si
scadabridge --url <url> health summary
```
#### `health pending-updates`
Return the fleet-wide pending-updates counts — instances whose deployed configuration has drifted from their current flatten, and sites running out-of-date system-wide artifacts — with a per-site breakdown. Requires the Deployer role; a site-scoped caller sees only their permitted sites. Each call evaluates the fleet (the Central UI's cached read is not shared with the CLI), so poll it at a scripting cadence, not a tight loop.
```sh
scadabridge --url <url> health pending-updates
```
#### `health site`
Return the health state for a single site.