From a7bd4d9457c6186ef4d6c29c1e74926079ba1997 Mon Sep 17 00:00:00 2001 From: tumillanino Date: Fri, 31 Oct 2025 23:12:11 +1100 Subject: [PATCH] added cosmic packages to installer. Still no auto reboot --- TESTING.md | 71 ++++++++++++++++++++++++++++ main.go | 29 +++++++++--- tui/steps/install.go | 107 ++++++++++++++++++++++++++++++------------- 3 files changed, 169 insertions(+), 38 deletions(-) create mode 100644 TESTING.md diff --git a/TESTING.md b/TESTING.md new file mode 100644 index 0000000..2577e0e --- /dev/null +++ b/TESTING.md @@ -0,0 +1,71 @@ +# Miasma OS Installer - Testing Guide + +## Testing in a Minimal Arch VM + +### Setup Test Environment + +1. **Create Arch VM with minimal install:** + - Boot Arch ISO + - Choose "Install Arch Linux" + - When prompted for profile, select "minimal" (no desktop) + - Complete basic installation + - Reboot into minimal Arch system + +2. **Prepare for testing:** + ```bash + # Login as root + # Ensure network is working + ping -c 3 archlinux.org + + # Install git and go (for building) + pacman -Sy git go base-devel + + # Clone the installer + git clone https://git.miasma-os.com/miasma/miasma-installer.git + cd miasma-installer + + # Build the installer + make build + ``` + +3. **Run the installer:** + ```bash + # Must be run as root + sudo ./build/miasma-installer + ``` + +### What Should Happen + +1. **During Installation:** + - Installer detects available disks + - Partitions selected disk (GPT with EFI + root) + - Formats with btrfs (with subvolumes) + - Optional LUKS2 encryption + - Installs base system + linux-hardened + - Installs Cosmic Desktop packages + - Configures bootloader (systemd-boot) + - Sets up user account with sudo + +2. **After Installation:** + - Installer exits cleanly + - Unmount filesystems: `umount -R /mnt` + - Reboot: `reboot` + - System boots into Cosmic Desktop login + - Login with created user credentials + +### For archiso Integration + +The installer will be included as a custom package in the archiso build: +- ISO boots directly to root shell +- Installer auto-starts or user runs `miasma-installer` +- After installation completes, user reboots +- System boots into installed Miasma OS with Cosmic Desktop + +### Troubleshooting + +If installation fails: +- Check `/mnt` is empty before starting +- Ensure UEFI mode: `ls /sys/firmware/efi` +- Check disk permissions: `lsblk` +- View detailed errors in the TUI +- Manually unmount if needed: `umount -R /mnt` diff --git a/main.go b/main.go index 4aa6d01..5b7c9f8 100644 --- a/main.go +++ b/main.go @@ -9,17 +9,34 @@ import ( ) func main() { - p := tea.NewProgram(tui.NewModel(), tea.WithAltScreen()) - - if _, err := p.Run(); err != nil { - fmt.Printf("An error has occured during installation: %v\n", err) + if os.Geteuid() != 0 { + fmt.Println("Error: This installer must be run as root") + fmt.Println("Please run: sudo miasma-installer") + os.Exit(1) + } + + if _, err := os.Stat("/sys/firmware/efi"); os.IsNotExist(err) { + fmt.Println("Error: UEFI boot mode not detected") + fmt.Println("Miasma OS requires UEFI boot mode") + os.Exit(1) + } + + p := tea.NewProgram(tui.NewModel(), tea.WithAltScreen()) + + finalModel, err := p.Run() + if err != nil { + fmt.Printf("\nAn error occurred during installation: %v\n", err) os.Exit(1) } - finalModel, _ := p.Run() if root, ok := finalModel.(tui.RootModel); ok { if root.State == tui.StateFinished { - fmt.Println("\nMiasma OS Installation Complete! Please reboot.") + fmt.Println("\nāœ“ Miasma OS Installation Complete!") + fmt.Println("\nThe system will reboot in 5 seconds...") + fmt.Println("Press Ctrl+C to cancel reboot") + + fmt.Print("\nUnmounting filesystems...") + os.Exit(0) } } } diff --git a/tui/steps/install.go b/tui/steps/install.go index 6c91b35..55bcebc 100644 --- a/tui/steps/install.go +++ b/tui/steps/install.go @@ -266,7 +266,7 @@ func (m InstallModel) configureSystem() error { genfstab := exec.Command("genfstab", "-U", "/mnt") fstab, err := genfstab.Output() if err != nil { - return err + return fmt.Errorf("genfstab failed: %w", err) } os.WriteFile("/mnt/etc/fstab", fstab, 0644) @@ -277,7 +277,9 @@ func (m InstallModel) configureSystem() error { os.WriteFile("/mnt/etc/hosts", []byte(hosts), 0644) os.WriteFile("/mnt/etc/locale.gen", []byte("en_US.UTF-8 UTF-8\n"), 0644) - chroot([]string{"locale-gen"}) + if _, err := chroot([]string{"locale-gen"}); err != nil { + return fmt.Errorf("locale-gen failed: %w", err) + } os.WriteFile("/mnt/etc/locale.conf", []byte("LANG=en_US.UTF-8\n"), 0644) @@ -285,24 +287,38 @@ func (m InstallModel) configureSystem() error { chroot([]string{"hwclock", "--systohc"}) useraddCmd := fmt.Sprintf("useradd -m -G wheel -s /bin/bash %s", m.config.Username) - chroot([]string{"sh", "-c", useraddCmd}) + if _, err := chroot([]string{"sh", "-c", useraddCmd}); err != nil { + return fmt.Errorf("useradd failed: %w", err) + } passwdCmd := fmt.Sprintf("echo '%s:%s' | chpasswd", m.config.Username, m.config.UserPassword) - chroot([]string{"sh", "-c", passwdCmd}) + if _, err := chroot([]string{"sh", "-c", passwdCmd}); err != nil { + return fmt.Errorf("user password set failed: %w", err) + } - rootPasswdCmd := fmt.Sprintf("echo 'root:%s' | chpasswd", m.config.RootPassword) - chroot([]string{"sh", "-c", rootPasswdCmd}) + rootPasswd := m.config.RootPassword + if rootPasswd == "" { + rootPasswd = m.config.UserPassword + } + rootPasswdCmd := fmt.Sprintf("echo 'root:%s' | chpasswd", rootPasswd) + if _, err := chroot([]string{"sh", "-c", rootPasswdCmd}); err != nil { + return fmt.Errorf("root password set failed: %w", err) + } sudoers := "%wheel ALL=(ALL:ALL) ALL\n" os.WriteFile("/mnt/etc/sudoers.d/wheel", []byte(sudoers), 0440) - chroot([]string{"systemctl", "enable", "NetworkManager"}) + if _, err := chroot([]string{"systemctl", "enable", "NetworkManager"}); err != nil { + return fmt.Errorf("failed to enable NetworkManager: %w", err) + } return nil } func (m InstallModel) installBootloader() error { - chroot([]string{"bootctl", "install"}) + if _, err := chroot([]string{"bootctl", "install"}); err != nil { + return fmt.Errorf("bootctl install failed: %w", err) + } loaderConf := `default arch.conf timeout 3 @@ -317,25 +333,18 @@ editor no rootPart = disk + "p2" } - rootUUID := "" - if m.config.EnableLUKS { - blkid := exec.Command("blkid", "-s", "UUID", "-o", "value", rootPart) - output, _ := blkid.Output() - rootUUID = strings.TrimSpace(string(output)) - } else { - rootPart = "/dev/mapper/cryptroot" - blkid := exec.Command("blkid", "-s", "UUID", "-o", "value", rootPart) - output, _ := blkid.Output() - rootUUID = strings.TrimSpace(string(output)) - } - - options := fmt.Sprintf("root=UUID=%s rootflags=subvol=@ rw", rootUUID) + var options string if m.config.EnableLUKS { cryptUUID := "" - blkid := exec.Command("blkid", "-s", "UUID", "-o", "value", disk+"2") + blkid := exec.Command("blkid", "-s", "UUID", "-o", "value", rootPart) output, _ := blkid.Output() cryptUUID = strings.TrimSpace(string(output)) - options = fmt.Sprintf("cryptdevice=UUID=%s:cryptroot root=/dev/mapper/cryptroot rootflags=subvol=@ rw", cryptUUID) + options = fmt.Sprintf("cryptdevice=UUID=%s:cryptroot root=/dev/mapper/cryptroot rootflags=subvol=@ rw quiet splash", cryptUUID) + } else { + blkid := exec.Command("blkid", "-s", "UUID", "-o", "value", rootPart) + output, _ := blkid.Output() + rootUUID := strings.TrimSpace(string(output)) + options = fmt.Sprintf("root=UUID=%s rootflags=subvol=@ rw quiet splash", rootUUID) } entryConf := fmt.Sprintf(`title Miasma OS @@ -346,21 +355,55 @@ options %s os.WriteFile("/mnt/boot/loader/entries/arch.conf", []byte(entryConf), 0644) if m.config.EnableLUKS { - mkinitcpio, _ := os.ReadFile("/mnt/etc/mkinitcpio.conf") - newConf := strings.Replace(string(mkinitcpio), + mkinitcpioConf, err := os.ReadFile("/mnt/etc/mkinitcpio.conf") + if err != nil { + return fmt.Errorf("failed to read mkinitcpio.conf: %w", err) + } + + newConf := string(mkinitcpioConf) + newConf = strings.Replace(newConf, + "HOOKS=(base udev autodetect modconf kms keyboard keymap consolefont block filesystems fsck)", + "HOOKS=(base udev autodetect keyboard keymap consolefont modconf kms block encrypt filesystems fsck)", + 1) + newConf = strings.Replace(newConf, "HOOKS=(base udev autodetect modconf block filesystems keyboard fsck)", "HOOKS=(base udev autodetect keyboard keymap consolefont modconf block encrypt filesystems fsck)", 1) - os.WriteFile("/mnt/etc/mkinitcpio.conf", []byte(newConf), 0644) - chroot([]string{"mkinitcpio", "-P"}) + + if err := os.WriteFile("/mnt/etc/mkinitcpio.conf", []byte(newConf), 0644); err != nil { + return fmt.Errorf("failed to write mkinitcpio.conf: %w", err) + } + + if _, err := chroot([]string{"mkinitcpio", "-P"}); err != nil { + return fmt.Errorf("mkinitcpio failed: %w", err) + } } return nil } func (m InstallModel) installDesktop() error { - chroot([]string{"pacman", "-Sy", "--noconfirm", "cosmic-session", "cosmic-greeter"}) - chroot([]string{"systemctl", "enable", "cosmic-greeter"}) + cosmicPackages := []string{ + "cosmic-session", + "cosmic-greeter", + "cosmic-files", + "cosmic-edit", + "cosmic-term", + "cosmic-store", + "cosmic-settings", + "xorg-xwayland", + } + + for _, pkg := range cosmicPackages { + if _, err := chroot([]string{"pacman", "-Sy", "--noconfirm", pkg}); err != nil { + return fmt.Errorf("failed to install %s: %w", pkg, err) + } + } + + if _, err := chroot([]string{"systemctl", "enable", "cosmic-greeter"}); err != nil { + return fmt.Errorf("failed to enable cosmic-greeter: %w", err) + } + return nil } @@ -414,7 +457,7 @@ func (m InstallModel) Update(msg tea.Msg) (tea.Model, tea.Cmd) { m.status = "Installation complete!" } m.Finished = true - return m, nil + return m, tea.Quit } return m, nil @@ -445,10 +488,10 @@ func (m InstallModel) View() string { } if m.Finished && m.err == nil { - success := "āœ“ Installation successful!\n\nYou can now reboot your system." + success := "āœ“ Installation successful!\n\nPress Ctrl+C to exit and reboot." content.WriteString(styles.BoxStyle.Render(success)) content.WriteString("\n\n") - content.WriteString(styles.RenderHelp("Ctrl+C", "Exit")) + content.WriteString(styles.RenderHelp("Ctrl+C", "Exit and reboot")) } return styles.AppStyle.Render(content.String())