From d06af1a87f7a6f4d64c9b6f1791f03d4b45b2dbb Mon Sep 17 00:00:00 2001 From: X9 Dev Date: Thu, 16 Apr 2026 14:30:15 +0200 Subject: [PATCH] gui: auto-reboot after deployment with 60s countdown Summary screen shows countdown "Restart za Xs..." and reboots automatically. Buttons: "Restartovat ted" (immediate) and "Zrusit restart" (cancel). Co-Authored-By: Claude Sonnet 4.6 --- internal/gui/gui.go | 68 ++++++++++++++++++++++++++++++++++++++++++--- 1 file changed, 64 insertions(+), 4 deletions(-) diff --git a/internal/gui/gui.go b/internal/gui/gui.go index b9c8d9a..0418832 100644 --- a/internal/gui/gui.go +++ b/internal/gui/gui.go @@ -14,6 +14,7 @@ import ( "encoding/json" "fmt" "os" + "os/exec" "sync" "time" @@ -324,11 +325,16 @@ func runPhase(runCfg runner.RunConfig, steps []runner.Step) []runner.Result { } // -------------------------------------------------------------------------- -// Phase 3 – Summary +// Phase 3 – Summary with auto-reboot countdown // -------------------------------------------------------------------------- +const rebootCountdown = 60 // seconds before automatic reboot + func donePhase(results []runner.Result) { - var mw *walk.MainWindow + var ( + mw *walk.MainWindow + countdownLbl *walk.Label + ) ok, errs, skipped := 0, 0, 0 rows := make([]Widget, 0, len(results)) @@ -358,6 +364,9 @@ func donePhase(results []runner.Result) { summaryText := fmt.Sprintf("OK: %d CHYBY: %d PRESKOCENO: %d", ok, errs, skipped) + // cancelled by "Zrusit restart" button + cancelReboot := make(chan struct{}) + if err := (MainWindow{ AssignTo: &mw, Title: "xetup \u2014 hotovo", @@ -371,7 +380,7 @@ func donePhase(results []runner.Result) { }, HSeparator{}, ScrollView{ - MinSize: Size{Height: 560}, + MinSize: Size{Height: 510}, Layout: VBox{MarginsZero: true}, Children: rows, }, @@ -381,16 +390,34 @@ func donePhase(results []runner.Result) { Alignment: AlignHCenterVNear, Font: Font{Bold: true}, }, + Label{ + AssignTo: &countdownLbl, + Text: fmt.Sprintf("Restart za %ds...", rebootCountdown), + Alignment: AlignHCenterVNear, + }, Composite{ Layout: HBox{MarginsZero: true}, Children: []Widget{ HSpacer{}, PushButton{ - Text: " ZAVRIT ", + Text: " Restartovat ted ", OnClicked: func() { + close(cancelReboot) // signal goroutine to stop ticker + reboot() mw.Close() }, }, + PushButton{ + Text: " Zrusit restart ", + OnClicked: func() { + select { + case <-cancelReboot: // already closed + default: + close(cancelReboot) + } + countdownLbl.SetText("Restart zrusen.") + }, + }, HSpacer{}, }, }, @@ -399,9 +426,42 @@ func donePhase(results []runner.Result) { return } + // Countdown goroutine + go func() { + ticker := time.NewTicker(time.Second) + defer ticker.Stop() + remaining := rebootCountdown + for { + select { + case <-cancelReboot: + return + case <-ticker.C: + remaining-- + r := remaining + mw.Synchronize(func() { + if r > 0 { + countdownLbl.SetText(fmt.Sprintf("Restart za %ds...", r)) + } else { + countdownLbl.SetText("Restartuji...") + } + }) + if remaining <= 0 { + reboot() + mw.Synchronize(func() { mw.Close() }) + return + } + } + } + }() + mw.Run() } +// reboot issues an immediate Windows restart. +func reboot() { + exec.Command("shutdown", "/r", "/t", "0").Run() //nolint:errcheck +} + // -------------------------------------------------------------------------- // Helpers // --------------------------------------------------------------------------