gui: auto-reboot after deployment with 60s countdown
All checks were successful
release / build-and-release (push) Successful in 22s

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 <noreply@anthropic.com>
This commit is contained in:
X9 Dev 2026-04-16 14:30:15 +02:00
parent 2236fe48e9
commit d06af1a87f

View file

@ -14,6 +14,7 @@ import (
"encoding/json" "encoding/json"
"fmt" "fmt"
"os" "os"
"os/exec"
"sync" "sync"
"time" "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) { func donePhase(results []runner.Result) {
var mw *walk.MainWindow var (
mw *walk.MainWindow
countdownLbl *walk.Label
)
ok, errs, skipped := 0, 0, 0 ok, errs, skipped := 0, 0, 0
rows := make([]Widget, 0, len(results)) 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) summaryText := fmt.Sprintf("OK: %d CHYBY: %d PRESKOCENO: %d", ok, errs, skipped)
// cancelled by "Zrusit restart" button
cancelReboot := make(chan struct{})
if err := (MainWindow{ if err := (MainWindow{
AssignTo: &mw, AssignTo: &mw,
Title: "xetup \u2014 hotovo", Title: "xetup \u2014 hotovo",
@ -371,7 +380,7 @@ func donePhase(results []runner.Result) {
}, },
HSeparator{}, HSeparator{},
ScrollView{ ScrollView{
MinSize: Size{Height: 560}, MinSize: Size{Height: 510},
Layout: VBox{MarginsZero: true}, Layout: VBox{MarginsZero: true},
Children: rows, Children: rows,
}, },
@ -381,16 +390,34 @@ func donePhase(results []runner.Result) {
Alignment: AlignHCenterVNear, Alignment: AlignHCenterVNear,
Font: Font{Bold: true}, Font: Font{Bold: true},
}, },
Label{
AssignTo: &countdownLbl,
Text: fmt.Sprintf("Restart za %ds...", rebootCountdown),
Alignment: AlignHCenterVNear,
},
Composite{ Composite{
Layout: HBox{MarginsZero: true}, Layout: HBox{MarginsZero: true},
Children: []Widget{ Children: []Widget{
HSpacer{}, HSpacer{},
PushButton{ PushButton{
Text: " ZAVRIT ", Text: " Restartovat ted ",
OnClicked: func() { OnClicked: func() {
close(cancelReboot) // signal goroutine to stop ticker
reboot()
mw.Close() mw.Close()
}, },
}, },
PushButton{
Text: " Zrusit restart ",
OnClicked: func() {
select {
case <-cancelReboot: // already closed
default:
close(cancelReboot)
}
countdownLbl.SetText("Restart zrusen.")
},
},
HSpacer{}, HSpacer{},
}, },
}, },
@ -399,9 +426,42 @@ func donePhase(results []runner.Result) {
return 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() mw.Run()
} }
// reboot issues an immediate Windows restart.
func reboot() {
exec.Command("shutdown", "/r", "/t", "0").Run() //nolint:errcheck
}
// -------------------------------------------------------------------------- // --------------------------------------------------------------------------
// Helpers // Helpers
// -------------------------------------------------------------------------- // --------------------------------------------------------------------------