Skip to content
ConsoleMonster

Patch progress

Run six harmless preview jobs with a concurrency limit and one retry outcome.

Demos Dashboards

Use it when independent jobs need bounded concurrency and retained failures for retry.

  • Live
  • 88×22
  • PowerShell 7.4+
  • .NET 8.0+
Patch progress 88×2224-bitFull

How it's built

The whole program in one file, with its sample data at the top. It drew the recording above; both languages draw the same cells.

PowerShellpatch-progress.ps1View on GitHub
# Run synthetic work with a concurrency limit and retain failures for retry.
[CmdletBinding()]
param(
    [switch] $StaticSnapshot,
    [ValidateRange(0, 2)] [int] $Step = 0
)

Import-Module ConsoleMonster
# All data is synthetic and inline; no services are contacted or changed.
# A parallel dry run: the delay and one failure are synthetic. No server is contacted.
# Data: fictional inputs kept in memory.
$servers = @('web-01', 'web-02', 'web-03', 'job-01', 'sql-01', 'sql-02')
# Layout: construct the widgets shown on screen.
function New-PatchFrame($Tick) {
    New-ConsoleRows -Gap 1 {
        '[bold]Parallel patch run[/] [muted]· throttle 3[/]'
        $rows = foreach ($server in $servers) {
            $stage = @('Queued', 'Patching', 'Finished')[$Tick]
            $result = if ($Tick -lt 2) { '-' } elseif ($server -eq 'web-03') { 'Retry needed' } else { 'Pass' }
            [pscustomobject] @{ Host = $server; Stage = $stage; Result = $result }
        }
        New-ConsoleTable -InputObject $rows -Property Host, Stage, Result
        if ($Tick -eq 2) { '[pass]5 passed[/]  [warn]1 retry needed[/]' }
        else { '[muted]Ctrl+C cancels active work.[/]' }
    }
}
# Live updates: print one state headlessly, or run the bounded simulation.
if ($StaticSnapshot -or [Console]::IsInputRedirected) {
    New-PatchFrame $Step | Write-ConsoleWidget
    return
}
$results = $servers | ForEach-ConsoleItem -Parallel -ThrottleLimit 3 -TimeoutSeconds 3 -Title 'Patch run' -ErrorAction SilentlyContinue -ErrorVariable failures {
    Start-Sleep -Milliseconds 350
    if ($_ -eq 'web-03') { throw "$_ restart check failed" }
    "$_ preview passed"
}
# $results contains successes; $failures retains error records including the failed host.
# Keep both for retry/reporting. No patch command runs here.
New-PatchFrame 2 | Write-ConsoleWidget
C#Program.csView on GitHub
// Run synthetic work with a concurrency limit and retain failures for retry.
// All data is synthetic and inline; no services are contacted or changed.
using ConsoleMonster;
using ConsoleMonster.Layout;
using ConsoleMonster.Live;
using ConsoleMonster.Presenters;
using ConsoleMonster.Widgets;

// A parallel dry run: the delay and one failure are synthetic. No server is contacted.
// Data: fictional inputs kept in memory.
string[] servers = { "web-01", "web-02", "web-03", "job-01", "sql-01", "sql-02" };
// Run options: redirected input selects a deterministic snapshot.
bool snapshot = args.Contains("--snapshot") || (!args.Contains("--interactive") && Console.IsInputRedirected);
int stepAt = Array.IndexOf(args, "--step");
int step = stepAt < 0 ? 0 : int.Parse(args[stepAt + 1], System.Globalization.CultureInfo.InvariantCulture);
if (step < 0 || step > 2) throw new ArgumentOutOfRangeException(nameof(step));

// Layout: construct the widgets shown on screen.
IWidget Frame(int tick) {
    Table table = new Table().AddColumn("Host").AddColumn("Stage").AddColumn("Result", column => column.Presenter = CellPresenters.StatusWord);
    foreach (string server in servers) {
        string stage = new[] { "Queued", "Patching", "Finished" }[tick];
        string result = tick < 2 ? "-" : server == "web-03" ? "Retry needed" : "Pass";
        table.AddRow(server, stage, TableCell.FromValue(result));
    }
    string summary = tick == 2 ? "[pass]5 passed[/]  [warn]1 retry needed[/]"
        : "[muted]Ctrl+C cancels active work.[/]";
    return new Rows(new TextBlock("[bold]Parallel patch run[/] [muted]· throttle 3[/]"),
        table, new TextBlock(summary)) { Gap = 1 };
}
// Live updates: print one state headlessly, or run the bounded simulation.
if (snapshot) {
    Terminal.Write(Frame(step));
    return;
}
IReadOnlyList<RunResult<string, string>> results = await ConsoleRun.ForEachAsync(servers, async (server, cancel) => {
    await Task.Delay(350, cancel);
    if (server == "web-03") throw new InvalidOperationException("Restart check failed");
    return server + " preview passed";
}, new RunOptions { ThrottleLimit = 3, Timeout = TimeSpan.FromSeconds(3), Title = "Patch run" });
// results retains each outcome for retry/reporting; no patch command runs here.
Terminal.Write(Frame(2));

Run it yourself

Three steps in PowerShell, four in C#. Every command copies with one click.

  1. Install the module

    Once. Windows PowerShell 5.1 or PowerShell 7 on Windows, macOS or Linux.

    Install-Module ConsoleMonster -Scope CurrentUser
  2. Save the program

    Download it, or copy it from above into patch-progress.ps1.

  3. Run it

    From the folder you saved it in.

    ./patch-progress.ps1
  1. Create a project

    A plain console app; .NET 8 or later.

    dotnet new console -n Patchprogress; cd Patchprogress
  2. Add ConsoleMonster

    The only package the program needs.

    dotnet add package ConsoleMonster
  3. Replace Program.cs

    Download it, or copy the program from above.

  4. Run it

    dotnet run

Or skip the copying. Every demo ships with the module and the project template.

Show-ConsoleDemo patch-progress
Get-ConsoleDemo patch-progress -Path ./patch-progress.ps1
dotnet new install ConsoleMonster.Templates
dotnet new consolemonster --demo patch-progress -n Patchprogress

Make it yours

Start with these, then read the rest of the file.

  1. Replace the delay with your patch operation after adding confirmation.

  2. Choose the concurrency limit and per-host timeout.

  3. Use the success results and retained failures to retry hosts and produce a report.

More dashboards

All demos