All checks were successful
release / build-and-release (push) Successful in 24s
xetup.exe now acts as an orchestrator across system reboots: - PS scripts exit 9 to signal "reboot needed, re-run me" (WU) or "done but reboot needed to finalize" (Dell BIOS) - On exit 9: xetup saves state.json, ensures adminx9 account, copies itself to stable path, enables autologon, registers X9-Resume scheduled task (AtLogOn adminx9, RunLevel Highest) - On resume: loads pending steps from state, continues seamlessly with "Pokracuji po restartu..." label in the run window - On completion: disables autologon, removes X9-Resume task, deletes state file, shows summary with accumulated results across all reboot rounds New packages: internal/state, internal/prereboot Script 12: simplified to exit 0 (done) or exit 9 (reboot needed) Script 11: exit 9 when DCU exit code 1 (BIOS staged, reboot needed) Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
74 lines
2.1 KiB
Go
74 lines
2.1 KiB
Go
// Package state manages the persistent xetup deployment state used to resume
|
|
// after a system reboot. The state file is written before each reboot and
|
|
// deleted once all deployment steps have completed.
|
|
package state
|
|
|
|
import (
|
|
"encoding/json"
|
|
"os"
|
|
"path/filepath"
|
|
"time"
|
|
|
|
"git.xetup.x9.cz/x9/xetup/internal/config"
|
|
)
|
|
|
|
// StepResult records the outcome of a single deployment step.
|
|
// Results are accumulated across reboot rounds so the final summary
|
|
// can show every step regardless of which round it ran in.
|
|
type StepResult struct {
|
|
StepID string `json:"stepID"`
|
|
Num string `json:"num"`
|
|
Name string `json:"name"`
|
|
Status string `json:"status"` // "OK", "ERROR", "SKIPPED"
|
|
Elapsed time.Duration `json:"elapsed"`
|
|
}
|
|
|
|
// State holds everything xetup needs to resume after a reboot.
|
|
type State struct {
|
|
Config config.Config `json:"config"`
|
|
ConfigPath string `json:"configPath"` // path to original config.json
|
|
LogFile string `json:"logFile"`
|
|
PendingSteps []string `json:"pendingSteps"` // step IDs to run next, in canonical order
|
|
Results []StepResult `json:"results"` // accumulated across all rounds
|
|
}
|
|
|
|
// Load reads the state file. Returns a non-nil error when the file is absent.
|
|
func Load() (*State, error) {
|
|
data, err := os.ReadFile(statePath())
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
var s State
|
|
if err := json.Unmarshal(data, &s); err != nil {
|
|
return nil, err
|
|
}
|
|
return &s, nil
|
|
}
|
|
|
|
// Save writes the state file, creating parent directories as needed.
|
|
func Save(s *State) error {
|
|
p := statePath()
|
|
if err := os.MkdirAll(filepath.Dir(p), 0755); err != nil {
|
|
return err
|
|
}
|
|
data, err := json.MarshalIndent(s, "", " ")
|
|
if err != nil {
|
|
return err
|
|
}
|
|
return os.WriteFile(p, data, 0644)
|
|
}
|
|
|
|
// Delete removes the state file. Silently ignores not-found.
|
|
func Delete() error {
|
|
err := os.Remove(statePath())
|
|
if os.IsNotExist(err) {
|
|
return nil
|
|
}
|
|
return err
|
|
}
|
|
|
|
// Exists reports whether a state file is present on disk.
|
|
func Exists() bool {
|
|
_, err := os.Stat(statePath())
|
|
return err == nil
|
|
}
|