Commit 386fa8c

Eric Bower  ·  2026-05-07 10:07:59 -0400 EDT
parent e2e0d12
style(runner): update session status
1 files changed,  +40, -13
M main.go
+40, -13
 1@@ -14,6 +14,7 @@ import (
 2 	"os/exec"
 3 	"os/signal"
 4 	"path/filepath"
 5+	"strconv"
 6 	"strings"
 7 	"syscall"
 8 	"time"
 9@@ -500,16 +501,17 @@ func waitAndReport(cfg *Cfg, log *slog.Logger, name, jobID string) error {
10 	fmt.Fprintln(os.Stdout)
11 	fmt.Fprint(os.Stdout, "⏳ waiting for completion...\n")
12 
13-	// Track what we've already printed to avoid duplicates
14+	// Track state for each session
15 	type sessionState struct {
16 		status   string // "running", "success", "failed"
17 		exitCode string
18 		duration string
19-		printed  bool
20+		printed  bool // true once final state (success/failed) is printed
21 	}
22 	known := make(map[string]*sessionState)
23 	var jobSessions []SessionInfo
24 	var sessionOrder []string // track insertion order for deterministic output
25+	var liveLines []string    // last set of status lines printed (for overwrite)
26 
27 	for {
28 		select {
29@@ -552,6 +554,8 @@ func waitAndReport(cfg *Cfg, log *slog.Logger, name, jobID string) error {
30 
31 			if s.Ended == "" {
32 				state.status = "running"
33+				created, _ := strconv.ParseInt(s.Created, 10, 64)
34+				state.duration = fmtDurationTs(created, time.Now().Unix())
35 			} else {
36 				if s.ExitCode == "0" {
37 					state.status = "success"
38@@ -560,22 +564,45 @@ func waitAndReport(cfg *Cfg, log *slog.Logger, name, jobID string) error {
39 					state.exitCode = s.ExitCode
40 				}
41 				state.duration = fmtDuration(s.Created, s.Ended)
42+				state.printed = true // lock final state
43 			}
44+		}
45 
46-			// Print when state changes
47-			if !state.printed {
48-				icon := map[string]string{"running": "🚀", "success": "✅", "failed": "❌"}[state.status]
49-				detail := ""
50-				if state.status == "failed" && state.exitCode != "" {
51-					detail = fmt.Sprintf(", exit %s", state.exitCode)
52-				}
53-				if state.duration != "" && state.duration != "—" {
54-					detail += fmt.Sprintf(" (%s)", state.duration)
55+		// Build current status lines
56+		lines := make([]string, 0, len(sessionOrder))
57+		for _, short := range sessionOrder {
58+			state := known[short]
59+			icon := map[string]string{"running": "🚀", "success": "✅", "failed": "❌"}[state.status]
60+			detail := ""
61+			if state.status == "failed" && state.exitCode != "" {
62+				detail = fmt.Sprintf(", exit %s", state.exitCode)
63+			}
64+			if state.duration != "" && state.duration != "—" {
65+				detail += fmt.Sprintf(" (%s)", state.duration)
66+			}
67+			lines = append(lines, fmt.Sprintf("   %-12s %s %s%s", short, icon, state.status, detail))
68+		}
69+
70+		// Overwrite previous lines with cursor-up, or print fresh
71+		if len(liveLines) > 0 {
72+			// Move cursor up to overwrite previous lines
73+			for range len(liveLines) {
74+				fmt.Fprint(os.Stdout, "\033[A")
75+			}
76+			// Clear each line
77+			for i, line := range lines {
78+				if i > 0 {
79+					fmt.Fprint(os.Stdout, "\n")
80 				}
81-				fmt.Fprintf(os.Stdout, "   %-12s %s %s%s\n", s.Short, icon, state.status, detail)
82-				state.printed = true
83+				fmt.Fprint(os.Stdout, line+"\033[K")
84+			}
85+			fmt.Fprint(os.Stdout, "\n")
86+		} else {
87+			for _, line := range lines {
88+				fmt.Fprintln(os.Stdout, line)
89 			}
90 		}
91+		liveLines = lines
92 
93 		// Check if all sessions are done
94 		if allCompleted(jobSessions) {