added encryption and disk selection
This commit is contained in:
9
go.mod
9
go.mod
@@ -2,13 +2,16 @@ module miasma-installer
|
|||||||
|
|
||||||
go 1.25.2
|
go 1.25.2
|
||||||
|
|
||||||
|
require (
|
||||||
|
github.com/charmbracelet/bubbles v0.21.0
|
||||||
|
github.com/charmbracelet/bubbletea v1.3.10
|
||||||
|
github.com/charmbracelet/lipgloss v1.1.0
|
||||||
|
)
|
||||||
|
|
||||||
require (
|
require (
|
||||||
github.com/atotto/clipboard v0.1.4 // indirect
|
github.com/atotto/clipboard v0.1.4 // indirect
|
||||||
github.com/aymanbagabas/go-osc52/v2 v2.0.1 // indirect
|
github.com/aymanbagabas/go-osc52/v2 v2.0.1 // indirect
|
||||||
github.com/charmbracelet/bubbles v0.21.0 // indirect
|
|
||||||
github.com/charmbracelet/bubbletea v1.3.10 // indirect
|
|
||||||
github.com/charmbracelet/colorprofile v0.2.3-0.20250311203215-f60798e515dc // indirect
|
github.com/charmbracelet/colorprofile v0.2.3-0.20250311203215-f60798e515dc // indirect
|
||||||
github.com/charmbracelet/lipgloss v1.1.0 // indirect
|
|
||||||
github.com/charmbracelet/x/ansi v0.10.1 // indirect
|
github.com/charmbracelet/x/ansi v0.10.1 // indirect
|
||||||
github.com/charmbracelet/x/cellbuf v0.0.13-0.20250311204145-2c3ea96c31dd // indirect
|
github.com/charmbracelet/x/cellbuf v0.0.13-0.20250311204145-2c3ea96c31dd // indirect
|
||||||
github.com/charmbracelet/x/term v0.2.1 // indirect
|
github.com/charmbracelet/x/term v0.2.1 // indirect
|
||||||
|
|||||||
8
go.sum
8
go.sum
@@ -2,6 +2,8 @@ github.com/atotto/clipboard v0.1.4 h1:EH0zSVneZPSuFR11BlR9YppQTVDbh5+16AmcJi4g1z
|
|||||||
github.com/atotto/clipboard v0.1.4/go.mod h1:ZY9tmq7sm5xIbd9bOK4onWV4S6X0u6GY7Vn0Yu86PYI=
|
github.com/atotto/clipboard v0.1.4/go.mod h1:ZY9tmq7sm5xIbd9bOK4onWV4S6X0u6GY7Vn0Yu86PYI=
|
||||||
github.com/aymanbagabas/go-osc52/v2 v2.0.1 h1:HwpRHbFMcZLEVr42D4p7XBqjyuxQH5SMiErDT4WkJ2k=
|
github.com/aymanbagabas/go-osc52/v2 v2.0.1 h1:HwpRHbFMcZLEVr42D4p7XBqjyuxQH5SMiErDT4WkJ2k=
|
||||||
github.com/aymanbagabas/go-osc52/v2 v2.0.1/go.mod h1:uYgXzlJ7ZpABp8OJ+exZzJJhRNQ2ASbcXHWsFqH8hp8=
|
github.com/aymanbagabas/go-osc52/v2 v2.0.1/go.mod h1:uYgXzlJ7ZpABp8OJ+exZzJJhRNQ2ASbcXHWsFqH8hp8=
|
||||||
|
github.com/aymanbagabas/go-udiff v0.2.0 h1:TK0fH4MteXUDspT88n8CKzvK0X9O2xu9yQjWpi6yML8=
|
||||||
|
github.com/aymanbagabas/go-udiff v0.2.0/go.mod h1:RE4Ex0qsGkTAJoQdQQCA0uG+nAzJO/pI/QwceO5fgrA=
|
||||||
github.com/charmbracelet/bubbles v0.21.0 h1:9TdC97SdRVg/1aaXNVWfFH3nnLAwOXr8Fn6u6mfQdFs=
|
github.com/charmbracelet/bubbles v0.21.0 h1:9TdC97SdRVg/1aaXNVWfFH3nnLAwOXr8Fn6u6mfQdFs=
|
||||||
github.com/charmbracelet/bubbles v0.21.0/go.mod h1:HF+v6QUR4HkEpz62dx7ym2xc71/KBHg+zKwJtMw+qtg=
|
github.com/charmbracelet/bubbles v0.21.0/go.mod h1:HF+v6QUR4HkEpz62dx7ym2xc71/KBHg+zKwJtMw+qtg=
|
||||||
github.com/charmbracelet/bubbletea v1.3.10 h1:otUDHWMMzQSB0Pkc87rm691KZ3SWa4KUlvF9nRvCICw=
|
github.com/charmbracelet/bubbletea v1.3.10 h1:otUDHWMMzQSB0Pkc87rm691KZ3SWa4KUlvF9nRvCICw=
|
||||||
@@ -14,10 +16,14 @@ github.com/charmbracelet/x/ansi v0.10.1 h1:rL3Koar5XvX0pHGfovN03f5cxLbCF2YvLeyz7
|
|||||||
github.com/charmbracelet/x/ansi v0.10.1/go.mod h1:3RQDQ6lDnROptfpWuUVIUG64bD2g2BgntdxH0Ya5TeE=
|
github.com/charmbracelet/x/ansi v0.10.1/go.mod h1:3RQDQ6lDnROptfpWuUVIUG64bD2g2BgntdxH0Ya5TeE=
|
||||||
github.com/charmbracelet/x/cellbuf v0.0.13-0.20250311204145-2c3ea96c31dd h1:vy0GVL4jeHEwG5YOXDmi86oYw2yuYUGqz6a8sLwg0X8=
|
github.com/charmbracelet/x/cellbuf v0.0.13-0.20250311204145-2c3ea96c31dd h1:vy0GVL4jeHEwG5YOXDmi86oYw2yuYUGqz6a8sLwg0X8=
|
||||||
github.com/charmbracelet/x/cellbuf v0.0.13-0.20250311204145-2c3ea96c31dd/go.mod h1:xe0nKWGd3eJgtqZRaN9RjMtK7xUYchjzPr7q6kcvCCs=
|
github.com/charmbracelet/x/cellbuf v0.0.13-0.20250311204145-2c3ea96c31dd/go.mod h1:xe0nKWGd3eJgtqZRaN9RjMtK7xUYchjzPr7q6kcvCCs=
|
||||||
|
github.com/charmbracelet/x/exp/golden v0.0.0-20241011142426-46044092ad91 h1:payRxjMjKgx2PaCWLZ4p3ro9y97+TVLZNaRZgJwSVDQ=
|
||||||
|
github.com/charmbracelet/x/exp/golden v0.0.0-20241011142426-46044092ad91/go.mod h1:wDlXFlCrmJ8J+swcL/MnGUuYnqgQdW9rhSD61oNMb6U=
|
||||||
github.com/charmbracelet/x/term v0.2.1 h1:AQeHeLZ1OqSXhrAWpYUtZyX1T3zVxfpZuEQMIQaGIAQ=
|
github.com/charmbracelet/x/term v0.2.1 h1:AQeHeLZ1OqSXhrAWpYUtZyX1T3zVxfpZuEQMIQaGIAQ=
|
||||||
github.com/charmbracelet/x/term v0.2.1/go.mod h1:oQ4enTYFV7QN4m0i9mzHrViD7TQKvNEEkHUMCmsxdUg=
|
github.com/charmbracelet/x/term v0.2.1/go.mod h1:oQ4enTYFV7QN4m0i9mzHrViD7TQKvNEEkHUMCmsxdUg=
|
||||||
github.com/erikgeiser/coninput v0.0.0-20211004153227-1c3628e74d0f h1:Y/CXytFA4m6baUTXGLOoWe4PQhGxaX0KpnayAqC48p4=
|
github.com/erikgeiser/coninput v0.0.0-20211004153227-1c3628e74d0f h1:Y/CXytFA4m6baUTXGLOoWe4PQhGxaX0KpnayAqC48p4=
|
||||||
github.com/erikgeiser/coninput v0.0.0-20211004153227-1c3628e74d0f/go.mod h1:vw97MGsxSvLiUE2X8qFplwetxpGLQrlU1Q9AUEIzCaM=
|
github.com/erikgeiser/coninput v0.0.0-20211004153227-1c3628e74d0f/go.mod h1:vw97MGsxSvLiUE2X8qFplwetxpGLQrlU1Q9AUEIzCaM=
|
||||||
|
github.com/kylelemons/godebug v1.1.0 h1:RPNrshWIDI6G2gRW9EHilWtl7Z6Sb1BR0xunSBf0SNc=
|
||||||
|
github.com/kylelemons/godebug v1.1.0/go.mod h1:9/0rRGxNHcop5bhtWyNeEfOS8JIWk580+fNqagV/RAw=
|
||||||
github.com/lucasb-eyer/go-colorful v1.2.0 h1:1nnpGOrhyZZuNyfu1QjKiUICQ74+3FNCN69Aj6K7nkY=
|
github.com/lucasb-eyer/go-colorful v1.2.0 h1:1nnpGOrhyZZuNyfu1QjKiUICQ74+3FNCN69Aj6K7nkY=
|
||||||
github.com/lucasb-eyer/go-colorful v1.2.0/go.mod h1:R4dSotOR9KMtayYi1e77YzuveK+i7ruzyGqttikkLy0=
|
github.com/lucasb-eyer/go-colorful v1.2.0/go.mod h1:R4dSotOR9KMtayYi1e77YzuveK+i7ruzyGqttikkLy0=
|
||||||
github.com/mattn/go-isatty v0.0.20 h1:xfD0iDuEKnDkl03q4limB+vH+GxLEtL/jb4xVJSWWEY=
|
github.com/mattn/go-isatty v0.0.20 h1:xfD0iDuEKnDkl03q4limB+vH+GxLEtL/jb4xVJSWWEY=
|
||||||
@@ -39,6 +45,8 @@ github.com/sahilm/fuzzy v0.1.1 h1:ceu5RHF8DGgoi+/dR5PsECjCDH1BE3Fnmpo7aVXOdRA=
|
|||||||
github.com/sahilm/fuzzy v0.1.1/go.mod h1:VFvziUEIMCrT6A6tw2RFIXPXXmzXbOsSHF0DOI8ZK9Y=
|
github.com/sahilm/fuzzy v0.1.1/go.mod h1:VFvziUEIMCrT6A6tw2RFIXPXXmzXbOsSHF0DOI8ZK9Y=
|
||||||
github.com/xo/terminfo v0.0.0-20220910002029-abceb7e1c41e h1:JVG44RsyaB9T2KIHavMF/ppJZNG9ZpyihvCd0w101no=
|
github.com/xo/terminfo v0.0.0-20220910002029-abceb7e1c41e h1:JVG44RsyaB9T2KIHavMF/ppJZNG9ZpyihvCd0w101no=
|
||||||
github.com/xo/terminfo v0.0.0-20220910002029-abceb7e1c41e/go.mod h1:RbqR21r5mrJuqunuUZ/Dhy/avygyECGrLceyNeo4LiM=
|
github.com/xo/terminfo v0.0.0-20220910002029-abceb7e1c41e/go.mod h1:RbqR21r5mrJuqunuUZ/Dhy/avygyECGrLceyNeo4LiM=
|
||||||
|
golang.org/x/exp v0.0.0-20220909182711-5c715a9e8561 h1:MDc5xs78ZrZr3HMQugiXOAkSZtfTpbJLDr/lwfgO53E=
|
||||||
|
golang.org/x/exp v0.0.0-20220909182711-5c715a9e8561/go.mod h1:cyybsKvd6eL0RnXn6p/Grxp8F5bW7iYuBgsNCOHpMYE=
|
||||||
golang.org/x/sys v0.0.0-20210809222454-d867a43fc93e/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
golang.org/x/sys v0.0.0-20210809222454-d867a43fc93e/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||||
golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||||
golang.org/x/sys v0.36.0 h1:KVRy2GtZBrk1cBYA7MKu5bEZFxQk4NIDV6RLVcC8o0k=
|
golang.org/x/sys v0.36.0 h1:KVRy2GtZBrk1cBYA7MKu5bEZFxQk4NIDV6RLVcC8o0k=
|
||||||
|
|||||||
67
tui/model.go
67
tui/model.go
@@ -11,6 +11,10 @@ type State int
|
|||||||
const (
|
const (
|
||||||
StateWelcome State = iota
|
StateWelcome State = iota
|
||||||
StateDiskSelection
|
StateDiskSelection
|
||||||
|
StateEncryption
|
||||||
|
StateHostname
|
||||||
|
StateUser
|
||||||
|
StateConfirm
|
||||||
StateInstalling
|
StateInstalling
|
||||||
StateFinished
|
StateFinished
|
||||||
)
|
)
|
||||||
@@ -20,6 +24,12 @@ type RootModel struct {
|
|||||||
CurrentModel tea.Model
|
CurrentModel tea.Model
|
||||||
Width int
|
Width int
|
||||||
Height int
|
Height int
|
||||||
|
SelectedDisk string
|
||||||
|
EnableLUKS bool
|
||||||
|
Hostname string
|
||||||
|
Username string
|
||||||
|
UserPassword string
|
||||||
|
RootPassword string
|
||||||
}
|
}
|
||||||
|
|
||||||
func NewModel() RootModel {
|
func NewModel() RootModel {
|
||||||
@@ -29,6 +39,10 @@ func NewModel() RootModel {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (m RootModel) Init() tea.Cmd {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
func (m RootModel) Update(msg tea.Msg) (tea.Model, tea.Cmd) {
|
func (m RootModel) Update(msg tea.Msg) (tea.Model, tea.Cmd) {
|
||||||
var cmd tea.Cmd
|
var cmd tea.Cmd
|
||||||
|
|
||||||
@@ -54,13 +68,56 @@ func (m RootModel) Update(msg tea.Msg) (tea.Model, tea.Cmd) {
|
|||||||
|
|
||||||
case steps.DiskSelModel:
|
case steps.DiskSelModel:
|
||||||
if m.CurrentModel.(steps.DiskSelModel).Finished {
|
if m.CurrentModel.(steps.DiskSelModel).Finished {
|
||||||
// need to update this to pass actual selected disk model to the installer
|
m.SelectedDisk = m.CurrentModel.(steps.DiskSelModel).SelectedDisk
|
||||||
selectedDisk := m.CurrentModel.(steps.DiskSelModel).SelectedDisk
|
m.State = StateEncryption
|
||||||
m.State = StateInstalling
|
m.CurrentModel = steps.NewEncryptionModel()
|
||||||
m.CurrentModel = steps.NewInstallModel(selectedDisk)
|
|
||||||
return m, m.CurrentModel.Init()
|
return m, m.CurrentModel.Init()
|
||||||
}
|
}
|
||||||
// logic for StateInstalling -> StateFinished will go here
|
|
||||||
|
case steps.EncryptionModel:
|
||||||
|
if m.CurrentModel.(steps.EncryptionModel).Finished {
|
||||||
|
m.EnableLUKS = m.CurrentModel.(steps.EncryptionModel).EnableEncryption
|
||||||
|
if m.EnableLUKS {
|
||||||
|
m.RootPassword = m.CurrentModel.(steps.EncryptionModel).Password
|
||||||
|
}
|
||||||
|
m.State = StateHostname
|
||||||
|
m.CurrentModel = steps.NewHostnameModel()
|
||||||
|
return m, m.CurrentModel.Init()
|
||||||
|
}
|
||||||
|
|
||||||
|
case steps.HostnameModel:
|
||||||
|
if m.CurrentModel.(steps.HostnameModel).Finished {
|
||||||
|
m.Hostname = m.CurrentModel.(steps.HostnameModel).Hostname
|
||||||
|
m.State = StateUser
|
||||||
|
m.CurrentModel = steps.NewUserModel()
|
||||||
|
return m, m.CurrentModel.Init()
|
||||||
|
}
|
||||||
|
|
||||||
|
case steps.UserModel:
|
||||||
|
if m.CurrentModel.(steps.UserModel).Finished {
|
||||||
|
m.Username = m.CurrentModel.(steps.UserModel).Username
|
||||||
|
m.UserPassword = m.CurrentModel.(steps.UserModel).Password
|
||||||
|
m.State = StateConfirm
|
||||||
|
m.CurrentModel = steps.NewConfirmModel(m.SelectedDisk, m.EnableLUKS, m.Hostname, m.Username)
|
||||||
|
return m, m.CurrentModel.Init()
|
||||||
|
}
|
||||||
|
|
||||||
|
case steps.ConfirmModel:
|
||||||
|
if m.CurrentModel.(steps.ConfirmModel).Finished {
|
||||||
|
if m.CurrentModel.(steps.ConfirmModel).Confirmed {
|
||||||
|
m.State = StateInstalling
|
||||||
|
m.CurrentModel = steps.NewInstallModel(m.SelectedDisk, m.EnableLUKS, m.Hostname, m.Username, m.UserPassword, m.RootPassword)
|
||||||
|
return m, m.CurrentModel.Init()
|
||||||
|
} else {
|
||||||
|
return m, tea.Quit
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
case steps.InstallModel:
|
||||||
|
if m.CurrentModel.(steps.InstallModel).Finished {
|
||||||
|
m.State = StateFinished
|
||||||
|
return m, tea.Quit
|
||||||
|
}
|
||||||
}
|
}
|
||||||
return m, cmd
|
return m, cmd
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -20,15 +20,15 @@ func (i Disk) Description() string { return i.description }
|
|||||||
type ItemDelegate struct{}
|
type ItemDelegate struct{}
|
||||||
|
|
||||||
func (i ItemDelegate) Height() int { return 1 }
|
func (i ItemDelegate) Height() int { return 1 }
|
||||||
func (i ItemDelegate) Width() int { return 0 }
|
func (i ItemDelegate) Spacing() int { return 0 }
|
||||||
func (i ItemDelegate) Update(msg tea.Msg, m list.Model) tea.Cmd { return nil }
|
func (i ItemDelegate) Update(msg tea.Msg, m *list.Model) tea.Cmd { return nil }
|
||||||
func (i ItemDelegate) Render(w io.Writer, m list.Model, index int, item list.Item) {
|
func (d ItemDelegate) Render(w io.Writer, m list.Model, index int, item list.Item) {
|
||||||
i, ok := item.(Disk)
|
disk, ok := item.(Disk)
|
||||||
if !ok {
|
if !ok {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
s := fmt.Sprintf("%d. %s - %s", index+1, i.Title(), i.Description())
|
s := fmt.Sprintf("%d. %s - %s", index+1, disk.Title(), disk.Description())
|
||||||
|
|
||||||
// focus styles
|
// focus styles
|
||||||
if index == m.Index() {
|
if index == m.Index() {
|
||||||
@@ -53,12 +53,11 @@ func NewDiskSelModel(width, height int) DiskSelModel {
|
|||||||
Disk{name: "/dev/loop0", description: "2GB Fanxiang SSD"},
|
Disk{name: "/dev/loop0", description: "2GB Fanxiang SSD"},
|
||||||
}
|
}
|
||||||
|
|
||||||
l := list.New(disks, itemDelegate{}, width, height-5)
|
l := list.New(disks, ItemDelegate{}, width, height-5)
|
||||||
l.Title = styles.TitleStyle.Render("Select Installation Disk")
|
l.Title = "Select Installation Disk"
|
||||||
l.SetShowStatusBar(false)
|
l.SetShowStatusBar(false)
|
||||||
l.SetFilterEnabled(false)
|
l.SetFilteringEnabled(false)
|
||||||
l.Styles.Title = styles.TitleStyle
|
l.Styles.Title = styles.TitleStyle
|
||||||
l.KeyMap.Unbind()
|
|
||||||
|
|
||||||
return DiskSelModel{list: l}
|
return DiskSelModel{list: l}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,132 @@
|
|||||||
|
package steps
|
||||||
|
|
||||||
|
import (
|
||||||
|
"encoding/json"
|
||||||
|
"fmt"
|
||||||
|
"miasma-installer/config"
|
||||||
|
"miasma-installer/tui/styles"
|
||||||
|
"os"
|
||||||
|
"os/exec"
|
||||||
|
"path/filepath"
|
||||||
|
"strings"
|
||||||
|
|
||||||
|
tea "github.com/charmbracelet/bubbletea"
|
||||||
|
)
|
||||||
|
|
||||||
|
type InstallModel struct {
|
||||||
|
config *config.InstallConfig
|
||||||
|
Finished bool
|
||||||
|
status string
|
||||||
|
progress []string
|
||||||
|
err error
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewInstallModel(disk string, enableLUKS bool, hostname string, username string, userPassword string, rootPassword string) InstallModel {
|
||||||
|
return InstallModel{
|
||||||
|
config: &config.InstallConfig{
|
||||||
|
Disk: disk,
|
||||||
|
EnableLUKS: enableLUKS,
|
||||||
|
Hostname: hostname,
|
||||||
|
Username: username,
|
||||||
|
UserPassword: userPassword,
|
||||||
|
RootPassword: rootPassword,
|
||||||
|
Timezone: "UTC",
|
||||||
|
Locale: "en_US.UTF-8",
|
||||||
|
},
|
||||||
|
status: "Preparing installation...",
|
||||||
|
progress: []string{},
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (m InstallModel) Init() tea.Cmd {
|
||||||
|
return m.startInstall()
|
||||||
|
}
|
||||||
|
|
||||||
|
type installCompleteMsg struct {
|
||||||
|
err error
|
||||||
|
}
|
||||||
|
|
||||||
|
func (m InstallModel) startInstall() tea.Cmd {
|
||||||
|
return func() tea.Msg {
|
||||||
|
archConfig := m.config.ToArchInstall()
|
||||||
|
|
||||||
|
configPath := "/tmp/miasma-install-config.json"
|
||||||
|
data, err := json.MarshalIndent(archConfig, "", " ")
|
||||||
|
if err != nil {
|
||||||
|
return installCompleteMsg{err: fmt.Errorf("failed to marshal config: %w", err)}
|
||||||
|
}
|
||||||
|
|
||||||
|
if err := os.WriteFile(configPath, data, 0644); err != nil {
|
||||||
|
return installCompleteMsg{err: fmt.Errorf("failed to write config: %w", err)}
|
||||||
|
}
|
||||||
|
|
||||||
|
cmd := exec.Command("archinstall", "--config", configPath)
|
||||||
|
output, err := cmd.CombinedOutput()
|
||||||
|
if err != nil {
|
||||||
|
return installCompleteMsg{err: fmt.Errorf("archinstall failed: %w\nOutput: %s", err, string(output))}
|
||||||
|
}
|
||||||
|
|
||||||
|
if err := m.runPostInstallScripts(); err != nil {
|
||||||
|
return installCompleteMsg{err: fmt.Errorf("post-install scripts failed: %w", err)}
|
||||||
|
}
|
||||||
|
|
||||||
|
return installCompleteMsg{err: nil}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (m InstallModel) runPostInstallScripts() error {
|
||||||
|
scriptsDir := "./scripts"
|
||||||
|
entries, err := os.ReadDir(scriptsDir)
|
||||||
|
if err != nil {
|
||||||
|
if os.IsNotExist(err) {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, entry := range entries {
|
||||||
|
if entry.IsDir() || !strings.HasSuffix(entry.Name(), ".sh") {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
scriptPath := filepath.Join(scriptsDir, entry.Name())
|
||||||
|
cmd := exec.Command("/bin/bash", scriptPath)
|
||||||
|
if output, err := cmd.CombinedOutput(); err != nil {
|
||||||
|
return fmt.Errorf("script %s failed: %w\nOutput: %s", entry.Name(), err, string(output))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (m InstallModel) Update(msg tea.Msg) (tea.Model, tea.Cmd) {
|
||||||
|
switch msg := msg.(type) {
|
||||||
|
case installCompleteMsg:
|
||||||
|
if msg.err != nil {
|
||||||
|
m.err = msg.err
|
||||||
|
m.status = "Installation failed"
|
||||||
|
} else {
|
||||||
|
m.status = "Installation complete!"
|
||||||
|
}
|
||||||
|
m.Finished = true
|
||||||
|
return m, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
return m, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (m InstallModel) View() string {
|
||||||
|
s := styles.TitleStyle.Render("Installing Miasma OS")
|
||||||
|
s += "\n\n"
|
||||||
|
s += m.status + "\n\n"
|
||||||
|
|
||||||
|
if m.err != nil {
|
||||||
|
s += styles.HelpStyle.Render(fmt.Sprintf("Error: %v\n", m.err))
|
||||||
|
}
|
||||||
|
|
||||||
|
if m.Finished && m.err == nil {
|
||||||
|
s += styles.HelpStyle.Render("Press Ctrl+C to exit")
|
||||||
|
}
|
||||||
|
|
||||||
|
return styles.MainStyle.Render(s)
|
||||||
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user