package main import ( "fmt" "os" "strings" _ "github.com/mulgadc/bluebottle/pkg/fipsboot" "github.com/mulgadc/spinifex/spinifex/operator/installer/autoinstall" "github.com/mulgadc/spinifex/spinifex/operator/installer/install" "github.com/mulgadc/spinifex/spinifex/operator/installer/ui" ) func main() { // Check for a headless autoinstall config on the boot media before // launching the interactive TUI. If found and enabled, run without // any user input then eject the USB and reboot. if cfg, err := autoinstall.Load(); err != nil { fmt.Fprintf(os.Stderr, "autoinstall: %v\n", err) os.Exit(1) } else if cfg != nil { if err := install.Run(cfg); err != nil { fmt.Fprintf(os.Stderr, "install failed: %v\n", err) os.Exit(1) } autoinstall.EjectAndReboot() return } // Normal interactive path. ttyPath := detectTTY() cfg, err := ui.Run(ttyPath) if err != nil { fmt.Fprintf(os.Stderr, "installer: %v\n", err) os.Exit(1) } if err := install.Run(cfg); err != nil { fmt.Fprintf(os.Stderr, "install failed: %v\n", err) os.Exit(1) } } // detectTTY returns the TTY device path the installer should attach to. // It first checks the SPINIFEX_CONSOLE environment variable (set by // spinifex-init.sh after console fallback logic), then falls back to parsing // /proc/cmdline. spinifex-init.sh may override the GRUB-set value (e.g. when // GRUB says tty1 but no framebuffer exists, it falls back to ttyS0 and // exports the corrected value). func detectTTY() string { // Prefer the env var — spinifex-init.sh sets this after resolving the // actual console (including headless/framebuffer fallback). if val := os.Getenv("SPINIFEX_CONSOLE"); val != "" && val != "auto" { return "/dev/" + val } // Fall back to kernel cmdline for direct invocation without spinifex-init. data, err := os.ReadFile("/proc/cmdline") if err == nil { for param := range strings.FieldsSeq(string(data)) { if after, ok := strings.CutPrefix(param, "SPINIFEX_CONSOLE="); ok { val := after if val != "" && val != "auto" { return "/dev/" + val } } } } return "/dev/tty1" }