fix(base): inject cryptsetup for encrypted installs
- commit
- 6495190c6f24238ed214e3b31a8fc47e2a0a3a63
- author
- brice · · 2026-02-27 05:25 UTC
- parent
- ef28457
- signature
- GPG Verified
CD88F9E564E0102646FB6077A1A07C7C7C3E349D· arch-gpg Verified for the committer email
4 files · +61 -0
M internal/application/commands/install_base.go +1 -0
| @@ -9,4 +9,5 @@ type InstallBaseCommand struct { | |||
| 9 | 9 | Packages []string // Additional packages to install (in addition to base) | |
| 10 | 10 | KernelVariant packages.KernelVariant // KernelStable, KernelZen, KernelLTS, KernelHardened, KernelCachyOS | |
| 11 | 11 | IncludeMicrocode bool // Whether to install CPU microcode | |
| 12 | + | Encrypted bool // true when disk encryption was chosen | |
| 12 | 13 | } | |
| @@ -9,4 +9,5 @@ type InstallBaseCommand struct { | |||||
| 9 | Packages []string // Additional packages to install (in addition to base) | 9 | Packages []string // Additional packages to install (in addition to base) | ||
| 10 | KernelVariant packages.KernelVariant // KernelStable, KernelZen, KernelLTS, KernelHardened, KernelCachyOS | 10 | KernelVariant packages.KernelVariant // KernelStable, KernelZen, KernelLTS, KernelHardened, KernelCachyOS | ||
| 11 | IncludeMicrocode bool // Whether to install CPU microcode | 11 | IncludeMicrocode bool // Whether to install CPU microcode | ||
| 12 | + | Encrypted bool // true when disk encryption was chosen | |||
| 12 | } | 13 | } | ||
M internal/application/handlers/install_base_handler.go +6 -0
| @@ -68,6 +68,12 @@ func (h *InstallBaseHandler) Handle(ctx context.Context, cmd commands.InstallBas | |||
| 68 | 68 | h.logger.Info("Adding CPU microcode packages") | |
| 69 | 69 | } | |
| 70 | 70 | ||
| 71 | + | // Add cryptsetup for encrypted installs | |
| 72 | + | if cmd.Encrypted { | |
| 73 | + | basePackages = append(basePackages, "cryptsetup") | |
| 74 | + | h.logger.Info("Adding cryptsetup for encrypted install") | |
| 75 | + | } | |
| 76 | + | ||
| 71 | 77 | // Add any additional packages from command | |
| 72 | 78 | if len(cmd.Packages) > 0 { | |
| 73 | 79 | basePackages = append(basePackages, cmd.Packages...) | |
| @@ -68,6 +68,12 @@ func (h *InstallBaseHandler) Handle(ctx context.Context, cmd commands.InstallBas | |||||
| 68 | h.logger.Info("Adding CPU microcode packages") | 68 | h.logger.Info("Adding CPU microcode packages") | ||
| 69 | } | 69 | } | ||
| 70 | 70 | ||||
| 71 | + | // Add cryptsetup for encrypted installs | |||
| 72 | + | if cmd.Encrypted { | |||
| 73 | + | basePackages = append(basePackages, "cryptsetup") | |||
| 74 | + | h.logger.Info("Adding cryptsetup for encrypted install") | |||
| 75 | + | } | |||
| 76 | + | ||||
| 71 | // Add any additional packages from command | 77 | // Add any additional packages from command | ||
| 72 | if len(cmd.Packages) > 0 { | 78 | if len(cmd.Packages) > 0 { | ||
| 73 | basePackages = append(basePackages, cmd.Packages...) | 79 | basePackages = append(basePackages, cmd.Packages...) | ||
M internal/application/handlers/install_base_handler_test.go +53 -0
| @@ -10,6 +10,9 @@ import ( | |||
| 10 | 10 | "go.uber.org/mock/gomock" | |
| 11 | 11 | ) | |
| 12 | 12 | ||
| 13 | + | // basePackagesContent is a minimal base.packages fixture used by all tests. | |
| 14 | + | var basePackagesContent = []byte("base\nlinux-firmware\n") | |
| 15 | + | ||
| 13 | 16 | func TestInstallBaseHandler_Handle_Success(t *testing.T) { | |
| 14 | 17 | ctrl := gomock.NewController(t) | |
| 15 | 18 | defer ctrl.Finish() | |
| @@ -20,6 +23,7 @@ func TestInstallBaseHandler_Handle_Success(t *testing.T) { | |||
| 20 | 23 | mockLogger := mocks.NewMockLogger(ctrl) | |
| 21 | 24 | ||
| 22 | 25 | mockLogger.EXPECT().Info(gomock.Any(), gomock.Any()).AnyTimes() | |
| 26 | + | mockFS.EXPECT().ReadFile(gomock.Any()).Return(basePackagesContent, nil).AnyTimes() | |
| 23 | 27 | mockExec.EXPECT().Execute(gomock.Any(), "pacstrap", "/mnt", "base", "linux-firmware", "linux-zen", "intel-ucode", "amd-ucode", "vim", "git").Return([]byte{}, nil).AnyTimes() | |
| 24 | 28 | mockExec.EXPECT().Execute(gomock.Any(), "genfstab", "-U", "/mnt").Return([]byte("# fstab"), nil).AnyTimes() | |
| 25 | 29 | mockFS.EXPECT().WriteFile(gomock.Any(), gomock.Any(), gomock.Any()).Return(nil).AnyTimes() | |
| @@ -102,6 +106,7 @@ func TestInstallBaseHandler_Handle_WithCustomPackages(t *testing.T) { | |||
| 102 | 106 | mockLogger := mocks.NewMockLogger(ctrl) | |
| 103 | 107 | ||
| 104 | 108 | mockLogger.EXPECT().Info(gomock.Any(), gomock.Any()).AnyTimes() | |
| 109 | + | mockFS.EXPECT().ReadFile(gomock.Any()).Return(basePackagesContent, nil).AnyTimes() | |
| 105 | 110 | mockExec.EXPECT().Execute(gomock.Any(), "pacstrap", "/mnt", "base", "linux-firmware", "linux", "neovim", "zsh", "tmux").Return([]byte{}, nil).AnyTimes() | |
| 106 | 111 | mockExec.EXPECT().Execute(gomock.Any(), "genfstab", "-U", "/mnt").Return([]byte("# fstab"), nil).AnyTimes() | |
| 107 | 112 | mockFS.EXPECT().WriteFile(gomock.Any(), gomock.Any(), gomock.Any()).Return(nil).AnyTimes() | |
| @@ -141,3 +146,51 @@ func TestInstallBaseHandler_Handle_WithCustomPackages(t *testing.T) { | |||
| 141 | 146 | } | |
| 142 | 147 | } | |
| 143 | 148 | } | |
| 149 | + | ||
| 150 | + | func TestInstallBaseHandler_Handle_WithEncryption_IncludesCryptsetup(t *testing.T) { | |
| 151 | + | ctrl := gomock.NewController(t) | |
| 152 | + | defer ctrl.Finish() | |
| 153 | + | ||
| 154 | + | mockFS := mocks.NewMockFileSystem(ctrl) | |
| 155 | + | mockExec := mocks.NewMockCommandExecutor(ctrl) | |
| 156 | + | mockChrExec := mocks.NewMockChrootExecutor(ctrl) | |
| 157 | + | mockLogger := mocks.NewMockLogger(ctrl) | |
| 158 | + | ||
| 159 | + | mockLogger.EXPECT().Info(gomock.Any(), gomock.Any()).AnyTimes() | |
| 160 | + | mockFS.EXPECT().ReadFile(gomock.Any()).Return(basePackagesContent, nil).AnyTimes() | |
| 161 | + | mockExec.EXPECT().Execute(gomock.Any(), "pacstrap", "/mnt", "base", "linux-firmware", "linux", "cryptsetup").Return([]byte{}, nil).AnyTimes() | |
| 162 | + | mockExec.EXPECT().Execute(gomock.Any(), "genfstab", "-U", "/mnt").Return([]byte("# fstab"), nil).AnyTimes() | |
| 163 | + | mockFS.EXPECT().WriteFile(gomock.Any(), gomock.Any(), gomock.Any()).Return(nil).AnyTimes() | |
| 164 | + | ||
| 165 | + | handler := NewInstallBaseHandler(mockFS, mockExec, mockChrExec, mockLogger) | |
| 166 | + | ||
| 167 | + | cmd := commands.InstallBaseCommand{ | |
| 168 | + | TargetDisk: "/dev/sda", | |
| 169 | + | MountPoint: "/mnt", | |
| 170 | + | KernelVariant: packages.KernelStable, | |
| 171 | + | IncludeMicrocode: false, | |
| 172 | + | Encrypted: true, | |
| 173 | + | } | |
| 174 | + | ||
| 175 | + | result, err := handler.Handle(context.Background(), cmd) | |
| 176 | + | ||
| 177 | + | if err != nil { | |
| 178 | + | t.Fatalf("expected no error, got %v", err) | |
| 179 | + | } | |
| 180 | + | ||
| 181 | + | if !result.Success { | |
| 182 | + | t.Error("expected success") | |
| 183 | + | } | |
| 184 | + | ||
| 185 | + | // Assert cryptsetup is in PackagesInstalled | |
| 186 | + | found := false | |
| 187 | + | for _, pkg := range result.PackagesInstalled { | |
| 188 | + | if pkg == "cryptsetup" { | |
| 189 | + | found = true | |
| 190 | + | break | |
| 191 | + | } | |
| 192 | + | } | |
| 193 | + | if !found { | |
| 194 | + | t.Error("expected cryptsetup to be in PackagesInstalled for encrypted install") | |
| 195 | + | } | |
| 196 | + | } | |
| @@ -10,6 +10,9 @@ import ( | |||||
| 10 | "go.uber.org/mock/gomock" | 10 | "go.uber.org/mock/gomock" | ||
| 11 | ) | 11 | ) | ||
| 12 | 12 | ||||
| 13 | + | // basePackagesContent is a minimal base.packages fixture used by all tests. | |||
| 14 | + | var basePackagesContent = []byte("base\nlinux-firmware\n") | |||
| 15 | + | ||||
| 13 | func TestInstallBaseHandler_Handle_Success(t *testing.T) { | 16 | func TestInstallBaseHandler_Handle_Success(t *testing.T) { | ||
| 14 | ctrl := gomock.NewController(t) | 17 | ctrl := gomock.NewController(t) | ||
| 15 | defer ctrl.Finish() | 18 | defer ctrl.Finish() | ||
| @@ -20,6 +23,7 @@ func TestInstallBaseHandler_Handle_Success(t *testing.T) { | |||||
| 20 | mockLogger := mocks.NewMockLogger(ctrl) | 23 | mockLogger := mocks.NewMockLogger(ctrl) | ||
| 21 | 24 | ||||
| 22 | mockLogger.EXPECT().Info(gomock.Any(), gomock.Any()).AnyTimes() | 25 | mockLogger.EXPECT().Info(gomock.Any(), gomock.Any()).AnyTimes() | ||
| 26 | + | mockFS.EXPECT().ReadFile(gomock.Any()).Return(basePackagesContent, nil).AnyTimes() | |||
| 23 | mockExec.EXPECT().Execute(gomock.Any(), "pacstrap", "/mnt", "base", "linux-firmware", "linux-zen", "intel-ucode", "amd-ucode", "vim", "git").Return([]byte{}, nil).AnyTimes() | 27 | mockExec.EXPECT().Execute(gomock.Any(), "pacstrap", "/mnt", "base", "linux-firmware", "linux-zen", "intel-ucode", "amd-ucode", "vim", "git").Return([]byte{}, nil).AnyTimes() | ||
| 24 | mockExec.EXPECT().Execute(gomock.Any(), "genfstab", "-U", "/mnt").Return([]byte("# fstab"), nil).AnyTimes() | 28 | mockExec.EXPECT().Execute(gomock.Any(), "genfstab", "-U", "/mnt").Return([]byte("# fstab"), nil).AnyTimes() | ||
| 25 | mockFS.EXPECT().WriteFile(gomock.Any(), gomock.Any(), gomock.Any()).Return(nil).AnyTimes() | 29 | mockFS.EXPECT().WriteFile(gomock.Any(), gomock.Any(), gomock.Any()).Return(nil).AnyTimes() | ||
| @@ -102,6 +106,7 @@ func TestInstallBaseHandler_Handle_WithCustomPackages(t *testing.T) { | |||||
| 102 | mockLogger := mocks.NewMockLogger(ctrl) | 106 | mockLogger := mocks.NewMockLogger(ctrl) | ||
| 103 | 107 | ||||
| 104 | mockLogger.EXPECT().Info(gomock.Any(), gomock.Any()).AnyTimes() | 108 | mockLogger.EXPECT().Info(gomock.Any(), gomock.Any()).AnyTimes() | ||
| 109 | + | mockFS.EXPECT().ReadFile(gomock.Any()).Return(basePackagesContent, nil).AnyTimes() | |||
| 105 | mockExec.EXPECT().Execute(gomock.Any(), "pacstrap", "/mnt", "base", "linux-firmware", "linux", "neovim", "zsh", "tmux").Return([]byte{}, nil).AnyTimes() | 110 | mockExec.EXPECT().Execute(gomock.Any(), "pacstrap", "/mnt", "base", "linux-firmware", "linux", "neovim", "zsh", "tmux").Return([]byte{}, nil).AnyTimes() | ||
| 106 | mockExec.EXPECT().Execute(gomock.Any(), "genfstab", "-U", "/mnt").Return([]byte("# fstab"), nil).AnyTimes() | 111 | mockExec.EXPECT().Execute(gomock.Any(), "genfstab", "-U", "/mnt").Return([]byte("# fstab"), nil).AnyTimes() | ||
| 107 | mockFS.EXPECT().WriteFile(gomock.Any(), gomock.Any(), gomock.Any()).Return(nil).AnyTimes() | 112 | mockFS.EXPECT().WriteFile(gomock.Any(), gomock.Any(), gomock.Any()).Return(nil).AnyTimes() | ||
| @@ -141,3 +146,51 @@ func TestInstallBaseHandler_Handle_WithCustomPackages(t *testing.T) { | |||||
| 141 | } | 146 | } | ||
| 142 | } | 147 | } | ||
| 143 | } | 148 | } | ||
| 149 | + | ||||
| 150 | + | func TestInstallBaseHandler_Handle_WithEncryption_IncludesCryptsetup(t *testing.T) { | |||
| 151 | + | ctrl := gomock.NewController(t) | |||
| 152 | + | defer ctrl.Finish() | |||
| 153 | + | ||||
| 154 | + | mockFS := mocks.NewMockFileSystem(ctrl) | |||
| 155 | + | mockExec := mocks.NewMockCommandExecutor(ctrl) | |||
| 156 | + | mockChrExec := mocks.NewMockChrootExecutor(ctrl) | |||
| 157 | + | mockLogger := mocks.NewMockLogger(ctrl) | |||
| 158 | + | ||||
| 159 | + | mockLogger.EXPECT().Info(gomock.Any(), gomock.Any()).AnyTimes() | |||
| 160 | + | mockFS.EXPECT().ReadFile(gomock.Any()).Return(basePackagesContent, nil).AnyTimes() | |||
| 161 | + | mockExec.EXPECT().Execute(gomock.Any(), "pacstrap", "/mnt", "base", "linux-firmware", "linux", "cryptsetup").Return([]byte{}, nil).AnyTimes() | |||
| 162 | + | mockExec.EXPECT().Execute(gomock.Any(), "genfstab", "-U", "/mnt").Return([]byte("# fstab"), nil).AnyTimes() | |||
| 163 | + | mockFS.EXPECT().WriteFile(gomock.Any(), gomock.Any(), gomock.Any()).Return(nil).AnyTimes() | |||
| 164 | + | ||||
| 165 | + | handler := NewInstallBaseHandler(mockFS, mockExec, mockChrExec, mockLogger) | |||
| 166 | + | ||||
| 167 | + | cmd := commands.InstallBaseCommand{ | |||
| 168 | + | TargetDisk: "/dev/sda", | |||
| 169 | + | MountPoint: "/mnt", | |||
| 170 | + | KernelVariant: packages.KernelStable, | |||
| 171 | + | IncludeMicrocode: false, | |||
| 172 | + | Encrypted: true, | |||
| 173 | + | } | |||
| 174 | + | ||||
| 175 | + | result, err := handler.Handle(context.Background(), cmd) | |||
| 176 | + | ||||
| 177 | + | if err != nil { | |||
| 178 | + | t.Fatalf("expected no error, got %v", err) | |||
| 179 | + | } | |||
| 180 | + | ||||
| 181 | + | if !result.Success { | |||
| 182 | + | t.Error("expected success") | |||
| 183 | + | } | |||
| 184 | + | ||||
| 185 | + | // Assert cryptsetup is in PackagesInstalled | |||
| 186 | + | found := false | |||
| 187 | + | for _, pkg := range result.PackagesInstalled { | |||
| 188 | + | if pkg == "cryptsetup" { | |||
| 189 | + | found = true | |||
| 190 | + | break | |||
| 191 | + | } | |||
| 192 | + | } | |||
| 193 | + | if !found { | |||
| 194 | + | t.Error("expected cryptsetup to be in PackagesInstalled for encrypted install") | |||
| 195 | + | } | |||
| 196 | + | } | |||
M internal/interfaces/tui/handlers/install_handler.go +1 -0
| @@ -141,6 +141,7 @@ func CreateInstallationCommand(app AppContext, formData models.FormData) tea.Cmd | |||
| 141 | 141 | MountPoint: "/mnt", | |
| 142 | 142 | KernelVariant: parseKernelVariant(formData.KernelVariant), | |
| 143 | 143 | IncludeMicrocode: formData.Microcode, | |
| 144 | + | Encrypted: parseEncryptionType(formData.EncryptionType) != disk.EncryptionTypeNone, | |
| 144 | 145 | } | |
| 145 | 146 | if _, err := svc.RunBaseInstall(ctx, baseCmd); err != nil { | |
| 146 | 147 | logger.Error("Base installation failed", "error", err) | |
| @@ -141,6 +141,7 @@ func CreateInstallationCommand(app AppContext, formData models.FormData) tea.Cmd | |||||
| 141 | MountPoint: "/mnt", | 141 | MountPoint: "/mnt", | ||
| 142 | KernelVariant: parseKernelVariant(formData.KernelVariant), | 142 | KernelVariant: parseKernelVariant(formData.KernelVariant), | ||
| 143 | IncludeMicrocode: formData.Microcode, | 143 | IncludeMicrocode: formData.Microcode, | ||
| 144 | + | Encrypted: parseEncryptionType(formData.EncryptionType) != disk.EncryptionTypeNone, | |||
| 144 | } | 145 | } | ||
| 145 | if _, err := svc.RunBaseInstall(ctx, baseCmd); err != nil { | 146 | if _, err := svc.RunBaseInstall(ctx, baseCmd); err != nil { | ||
| 146 | logger.Error("Base installation failed", "error", err) | 147 | logger.Error("Base installation failed", "error", err) | ||