From 9977476b15b2b6abcb29db1a68c3f706e8adb5fc Mon Sep 17 00:00:00 2001 From: Jarek Krochmalski Date: Sat, 22 Aug 2026 09:09:30 +0200 Subject: [PATCH] a compose stack with a restarting container shows a stop action instead of start --- src/lib/server/stack-status.ts | 33 +++++++++++++++++++++++++++++++++ 1 file changed, 33 insertions(+) create mode 100644 src/lib/server/stack-status.ts diff --git a/src/lib/server/stack-status.ts b/src/lib/server/stack-status.ts new file mode 100644 index 0000000..04678e8 --- /dev/null +++ b/src/lib/server/stack-status.ts @@ -0,0 +1,33 @@ +/** + * Derive a compose stack's overall status from its containers' states. + * Pure and unit-tested. A container in a restart loop reports state 'restarting' + * and is NOT stopped - it is actively trying to come up, so the stack must offer + * Stop, not Start (#1438). Init/migration containers that exited 0 are "completed" + * and don't count against health. + */ + +export type StackStatus = 'running' | 'partial' | 'restarting' | 'stopped'; + +export interface StackStatusCounts { + /** Total containers in the stack (including completed init containers). */ + total: number; + /** Containers with state === 'running'. */ + running: number; + /** Containers with state === 'restarting' (restart loop / coming up). */ + restarting: number; + /** Containers that exited 0 (init/migration - excluded from health). */ + completed: number; +} + +export function deriveStackStatus(counts: StackStatusCounts): StackStatus { + const { total, running, restarting, completed } = counts; + const activeTotal = total - completed; + if (activeTotal <= 0) return 'stopped'; + if (running >= activeTotal) return 'running'; + // Any live container (running or restarting) means the stack is not stopped. + if (running > 0 || restarting > 0) { + // All live containers are restarting and none is up -> the stack is thrashing. + return running === 0 ? 'restarting' : 'partial'; + } + return 'stopped'; +}