//go:build windows // Package preflight runs quick environment checks before deployment starts. // Each check returns a human-readable result; failures are warnings, not blockers. package preflight import ( "fmt" "os/exec" "strings" "syscall" "unsafe" ) // Result is one pre-flight check outcome. type Result struct { Name string OK bool Detail string } // RunAll executes all pre-flight checks and returns results. func RunAll() []Result { return []Result{ checkAdmin(), checkWinget(), checkNetwork(), checkDisk(), } } func checkAdmin() Result { r := Result{Name: "Spusteno jako Administrator"} // If we got here via #Requires -RunAsAdministrator or manifested exe, // we're admin. Double-check via net session. cmd := exec.Command("net", "session") cmd.SysProcAttr = &syscall.SysProcAttr{HideWindow: true} if err := cmd.Run(); err != nil { r.OK = false r.Detail = "Neni spusteno jako Administrator" return r } r.OK = true r.Detail = "OK" return r } func checkWinget() Result { r := Result{Name: "Winget dostupny"} cmd := exec.Command("winget", "--version") cmd.SysProcAttr = &syscall.SysProcAttr{HideWindow: true} out, err := cmd.CombinedOutput() if err != nil { r.OK = false r.Detail = "winget nenalezen - software se nenainstaluje" return r } ver := strings.TrimSpace(string(out)) r.OK = true r.Detail = ver return r } func checkNetwork() Result { r := Result{Name: "Pripojeni k internetu"} // Try to resolve a well-known hostname cmd := exec.Command("powershell.exe", "-NonInteractive", "-Command", "[System.Net.Dns]::GetHostEntry('www.google.com').AddressList[0].IPAddressToString") cmd.SysProcAttr = &syscall.SysProcAttr{HideWindow: true} out, err := cmd.CombinedOutput() if err != nil || strings.TrimSpace(string(out)) == "" { r.OK = false r.Detail = "DNS resolve selhal - Atera a winget nebudou fungovat" return r } r.OK = true r.Detail = "OK" return r } func checkDisk() Result { r := Result{Name: "Volne misto na C:"} kernel32 := syscall.NewLazyDLL("kernel32.dll") getDiskFreeSpaceEx := kernel32.NewProc("GetDiskFreeSpaceExW") var freeBytesAvailable, totalBytes, totalFreeBytes uint64 drive, _ := syscall.UTF16PtrFromString("C:\\") ret, _, _ := getDiskFreeSpaceEx.Call( uintptr(unsafe.Pointer(drive)), uintptr(unsafe.Pointer(&freeBytesAvailable)), uintptr(unsafe.Pointer(&totalBytes)), uintptr(unsafe.Pointer(&totalFreeBytes)), ) if ret == 0 { r.OK = false r.Detail = "Nelze zjistit volne misto" return r } freeGB := float64(freeBytesAvailable) / (1024 * 1024 * 1024) if freeGB < 5 { r.OK = false r.Detail = fmt.Sprintf("%.1f GB - malo mista (min 5 GB)", freeGB) return r } r.OK = true r.Detail = fmt.Sprintf("%.1f GB volnych", freeGB) return r }