Email report: HTML summary sent via SMTP2Go (mail-eu.smtp2go.com) at the end of every deployment. Subject "xetup report HOSTNAME", body contains per-step status table with timestamps. Non-blocking (goroutine) so it doesn't delay the summary screen. Pre-flight checks: admin rights, winget availability, network connectivity (DNS resolve), and disk space verified before the config form. Results shown as colored status lines at the top of the GUI - red warnings tell the technician what's wrong before starting a 30-minute deployment. Parallel winget: 02-software.ps1 now launches all winget installs as background jobs (Start-Job) and waits for all to complete. 7-Zip, Acrobat, OpenVPN run simultaneously instead of sequentially, saving 3-5 minutes per deployment. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
111 lines
2.7 KiB
Go
111 lines
2.7 KiB
Go
//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
|
|
}
|