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:
@@ -33,6 +33,12 @@ func (m model) Update(msg tea.Msg) (tea.Model, tea.Cmd) {
|
||||
}
|
||||
return m, pollSATProgress(m.progressPrefix, m.progressSince)
|
||||
}
|
||||
if m.busy && m.installCancel != nil {
|
||||
if len(msg.lines) > 0 {
|
||||
m.progressLines = msg.lines
|
||||
}
|
||||
return m, pollInstallProgress(DefaultInstallLogFile)
|
||||
}
|
||||
return m, nil
|
||||
case snapshotMsg:
|
||||
m.banner = msg.banner
|
||||
@@ -112,6 +118,44 @@ func (m model) Update(msg tea.Msg) (tea.Model, tea.Cmd) {
|
||||
m.screen = screenExportTargets
|
||||
m.cursor = 0
|
||||
return m, m.refreshSnapshotCmd()
|
||||
case installDisksMsg:
|
||||
m.busy = false
|
||||
m.busyTitle = ""
|
||||
if msg.err != nil {
|
||||
m.title = "Install to disk"
|
||||
m.body = msg.err.Error()
|
||||
m.prevScreen = screenTools
|
||||
m.screen = screenOutput
|
||||
return m, m.refreshSnapshotCmd()
|
||||
}
|
||||
if len(msg.disks) == 0 {
|
||||
m.title = "Install to disk"
|
||||
m.body = "No suitable disks found.\n\nOnly non-USB, non-boot disks are shown.\nAttach a target disk and try again."
|
||||
m.prevScreen = screenTools
|
||||
m.screen = screenOutput
|
||||
return m, m.refreshSnapshotCmd()
|
||||
}
|
||||
m.installDisks = msg.disks
|
||||
m.screen = screenInstallDiskPick
|
||||
m.cursor = 0
|
||||
return m, m.refreshSnapshotCmd()
|
||||
case installDoneMsg:
|
||||
if m.installCancel != nil {
|
||||
m.installCancel()
|
||||
m.installCancel = nil
|
||||
}
|
||||
m.busy = false
|
||||
m.busyTitle = ""
|
||||
m.progressLines = nil
|
||||
m.prevScreen = screenTools
|
||||
m.screen = screenOutput
|
||||
m.title = "Install to disk"
|
||||
if msg.err != nil {
|
||||
m.body = fmt.Sprintf("Installation FAILED.\n\nLog: %s\n\nERROR: %v", DefaultInstallLogFile, msg.err)
|
||||
} else {
|
||||
m.body = fmt.Sprintf("Installation complete.\n\nRemove the ISO and reboot to start the installed system.\n\nLog: %s", DefaultInstallLogFile)
|
||||
}
|
||||
return m, m.refreshSnapshotCmd()
|
||||
case nvtopClosedMsg:
|
||||
return m, nil
|
||||
case gpuStressDoneMsg:
|
||||
@@ -204,6 +248,10 @@ func (m model) updateKey(msg tea.KeyMsg) (tea.Model, tea.Cmd) {
|
||||
return m.updateMenu(msg, len(m.targets), m.handleExportTargetsMenu)
|
||||
case screenInterfacePick:
|
||||
return m.updateMenu(msg, len(m.interfaces), m.handleInterfacePickMenu)
|
||||
case screenTools:
|
||||
return m.updateMenu(msg, len(m.toolsMenu), m.handleToolsMenu)
|
||||
case screenInstallDiskPick:
|
||||
return m.updateMenu(msg, len(m.installDisks), m.handleInstallDiskPickMenu)
|
||||
case screenOutput:
|
||||
switch msg.String() {
|
||||
case "esc", "enter", "q":
|
||||
@@ -300,6 +348,12 @@ func (m model) updateMenu(msg tea.KeyMsg, size int, onEnter func() (tea.Model, t
|
||||
case screenInterfacePick:
|
||||
m.screen = screenNetwork
|
||||
m.cursor = 0
|
||||
case screenTools:
|
||||
m.screen = screenSettings
|
||||
m.cursor = 0
|
||||
case screenInstallDiskPick:
|
||||
m.screen = screenTools
|
||||
m.cursor = 0
|
||||
}
|
||||
case "q", "ctrl+c":
|
||||
return m, tea.Quit
|
||||
|
||||
Reference in New Issue
Block a user