feat(installer): add 'Install to disk' in Tools submenu
Copies the live system to a local disk via unsquashfs — no debootstrap, no network required. Supports UEFI (GPT+EFI) and BIOS (MBR) layouts. ISO: - Add squashfs-tools, parted, grub-pc, grub-efi-amd64 to package list - New overlay script bee-install: partitions, formats, unsquashfs, writes fstab, runs grub-install+update-grub in chroot Go TUI: - Settings → Tools submenu (Install to disk, Check tools) - Disk picker screen: lists non-USB, non-boot disks via lsblk - Confirm screen warns about data loss - Runs with live progress tail of /tmp/bee-install.log - platform/install.go: ListInstallDisks, InstallToDisk, findLiveBootDevice Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
108
audit/internal/tui/screen_tools.go
Normal file
108
audit/internal/tui/screen_tools.go
Normal file
@@ -0,0 +1,108 @@
|
||||
package tui
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"bee/audit/internal/platform"
|
||||
|
||||
tea "github.com/charmbracelet/bubbletea"
|
||||
)
|
||||
|
||||
// handleToolsMenu handles the Tools submenu selection.
|
||||
func (m model) handleToolsMenu() (tea.Model, tea.Cmd) {
|
||||
switch m.cursor {
|
||||
case 0: // Install to disk
|
||||
m.busy = true
|
||||
m.busyTitle = "Scanning disks"
|
||||
return m, func() tea.Msg {
|
||||
disks, err := m.app.ListInstallDisks()
|
||||
return installDisksMsg{disks: disks, err: err}
|
||||
}
|
||||
case 1: // Check tools
|
||||
m.busy = true
|
||||
m.busyTitle = "Check tools"
|
||||
return m, func() tea.Msg {
|
||||
result := m.app.ToolCheckResult([]string{
|
||||
"dmidecode", "smartctl", "nvme", "ipmitool", "lspci",
|
||||
"ethtool", "bee", "nvidia-smi", "bee-gpu-stress",
|
||||
"memtester", "dhclient", "lsblk", "mount",
|
||||
"unsquashfs", "parted", "grub-install", "bee-install",
|
||||
"all_reduce_perf", "dcgmi",
|
||||
})
|
||||
return resultMsg{title: result.Title, body: result.Body, back: screenTools}
|
||||
}
|
||||
case 2: // Back
|
||||
m.screen = screenSettings
|
||||
m.cursor = 0
|
||||
return m, nil
|
||||
}
|
||||
return m, nil
|
||||
}
|
||||
|
||||
// handleInstallDiskPickMenu handles disk selection for installation.
|
||||
func (m model) handleInstallDiskPickMenu() (tea.Model, tea.Cmd) {
|
||||
if m.cursor >= len(m.installDisks) {
|
||||
return m, nil
|
||||
}
|
||||
m.selectedDisk = m.installDisks[m.cursor].Device
|
||||
m.pendingAction = actionInstallToDisk
|
||||
m.screen = screenConfirm
|
||||
m.cursor = 0
|
||||
return m, nil
|
||||
}
|
||||
|
||||
// startInstall launches the bee-install script and polls its log for progress.
|
||||
func (m model) startInstall() (tea.Model, tea.Cmd) {
|
||||
device := m.selectedDisk
|
||||
logFile := DefaultInstallLogFile
|
||||
|
||||
ctx, cancel := context.WithCancel(context.Background())
|
||||
m.installCancel = cancel
|
||||
m.installSince = time.Now()
|
||||
m.busy = true
|
||||
m.busyTitle = "Install to disk: " + device
|
||||
m.progressLines = nil
|
||||
|
||||
installCmd := func() tea.Msg {
|
||||
err := m.app.InstallToDisk(ctx, device, logFile)
|
||||
return installDoneMsg{err: err}
|
||||
}
|
||||
|
||||
return m, tea.Batch(installCmd, pollInstallProgress(logFile))
|
||||
}
|
||||
|
||||
func renderInstallDiskPick(m model) string {
|
||||
var b strings.Builder
|
||||
fmt.Fprintln(&b, "INSTALL TO DISK")
|
||||
fmt.Fprintln(&b)
|
||||
fmt.Fprintln(&b, " WARNING: the selected disk will be completely WIPED.")
|
||||
fmt.Fprintln(&b)
|
||||
fmt.Fprintln(&b, " Available disks (USB and boot media excluded):")
|
||||
fmt.Fprintln(&b)
|
||||
if len(m.installDisks) == 0 {
|
||||
fmt.Fprintln(&b, " (no suitable disks found)")
|
||||
} else {
|
||||
for i, d := range m.installDisks {
|
||||
pfx := " "
|
||||
if m.cursor == i {
|
||||
pfx = "> "
|
||||
}
|
||||
fmt.Fprintf(&b, "%s%s\n", pfx, diskLabel(d))
|
||||
}
|
||||
}
|
||||
fmt.Fprintln(&b)
|
||||
fmt.Fprintln(&b, "─────────────────────────────────────────────────────────────────")
|
||||
fmt.Fprint(&b, "[↑/↓] move [enter] select [esc] back [ctrl+c] quit")
|
||||
return b.String()
|
||||
}
|
||||
|
||||
func diskLabel(d platform.InstallDisk) string {
|
||||
model := d.Model
|
||||
if model == "" {
|
||||
model = "Unknown"
|
||||
}
|
||||
return fmt.Sprintf("%-12s %-8s %s", d.Device, d.Size, model)
|
||||
}
|
||||
Reference in New Issue
Block a user