fix(repos): add CachyOS repo setup when linux-cachyos kernel is selected
- commit
- 5214853c633bf977d5be10fa9527f5d91f2bdc8e
- author
- brice · · 2026-02-27 05:26 UTC
- parent
- 6495190
- signature
- GPG Verified
CD88F9E564E0102646FB6077A1A07C7C7C3E349D· arch-gpg Verified for the committer email
4 files · +111 -5
M internal/application/commands/setup_repos.go +6 -5
| @@ -4,9 +4,10 @@ import "github.com/bnema/archup/internal/domain/packages" | |||
| 4 | 4 | ||
| 5 | 5 | // SetupRepositoriesCommand contains data for repository configuration | |
| 6 | 6 | type SetupRepositoriesCommand struct { | |
| 7 | - | MountPoint string // Root mount point | |
| 8 | - | EnableMultilib bool // Enable multilib repository | |
| 9 | - | EnableChaotic bool // Enable Chaotic-AUR repository | |
| 10 | - | AURHelper packages.AURHelper // AURHelperParu or AURHelperYay | |
| 11 | - | AdditionalRepos []string // Additional repository URLs | |
| 7 | + | MountPoint string // Root mount point | |
| 8 | + | EnableMultilib bool // Enable multilib repository | |
| 9 | + | EnableChaotic bool // Enable Chaotic-AUR repository | |
| 10 | + | AURHelper packages.AURHelper // AURHelperParu or AURHelperYay | |
| 11 | + | KernelVariant packages.KernelVariant // Kernel variant for repo setup | |
| 12 | + | AdditionalRepos []string // Additional repository URLs | |
| 12 | 13 | } | |
| @@ -4,9 +4,10 @@ import "github.com/bnema/archup/internal/domain/packages" | |||||
| 4 | 4 | ||||
| 5 | // SetupRepositoriesCommand contains data for repository configuration | 5 | // SetupRepositoriesCommand contains data for repository configuration | ||
| 6 | type SetupRepositoriesCommand struct { | 6 | type SetupRepositoriesCommand struct { | ||
| 7 | - | MountPoint string // Root mount point | 7 | + | MountPoint string // Root mount point |
| 8 | - | EnableMultilib bool // Enable multilib repository | 8 | + | EnableMultilib bool // Enable multilib repository |
| 9 | - | EnableChaotic bool // Enable Chaotic-AUR repository | 9 | + | EnableChaotic bool // Enable Chaotic-AUR repository |
| 10 | - | AURHelper packages.AURHelper // AURHelperParu or AURHelperYay | 10 | + | AURHelper packages.AURHelper // AURHelperParu or AURHelperYay |
| 11 | - | AdditionalRepos []string // Additional repository URLs | 11 | + | KernelVariant packages.KernelVariant // Kernel variant for repo setup |
| 12 | + | AdditionalRepos []string // Additional repository URLs | |||
| 12 | } | 13 | } | ||
M internal/application/handlers/repos_handler.go +40 -0
| @@ -75,6 +75,13 @@ func (h *ReposHandler) Handle(ctx context.Context, cmd commands.SetupRepositorie | |||
| 75 | 75 | } | |
| 76 | 76 | } | |
| 77 | 77 | ||
| 78 | + | // For CachyOS kernel: setup CachyOS repo BEFORE sync | |
| 79 | + | if cmd.KernelVariant == packages.KernelCachyOS { | |
| 80 | + | if err := h.setupCachyOSRepo(ctx, cmd.MountPoint); err != nil { | |
| 81 | + | return fail("Failed to setup CachyOS repository", err) | |
| 82 | + | } | |
| 83 | + | } | |
| 84 | + | ||
| 78 | 85 | // For chaotic-aur: install keyring and mirrorlist BEFORE adding to pacman.conf | |
| 79 | 86 | if repo.EnableChaotic() { | |
| 80 | 87 | if _, err := h.chrExec.ExecuteInChroot(ctx, cmd.MountPoint, "pacman-key", "--recv-key", "3056513887B78AEB", "--keyserver", "keyserver.ubuntu.com"); err != nil { | |
| @@ -195,3 +202,36 @@ func ensureChaoticRepo(conf string) string { | |||
| 195 | 202 | chaotic := "\n[chaotic-aur]\nInclude = /etc/pacman.d/chaotic-mirrorlist\n" | |
| 196 | 203 | return conf + chaotic | |
| 197 | 204 | } | |
| 205 | + | ||
| 206 | + | func (h *ReposHandler) setupCachyOSRepo(ctx context.Context, mountPoint string) error { | |
| 207 | + | if _, err := h.chrExec.ExecuteInChroot(ctx, mountPoint, "pacman-key", "--recv-key", | |
| 208 | + | "F3B607488DB35A47", "--keyserver", "keyserver.ubuntu.com"); err != nil { | |
| 209 | + | return fmt.Errorf("failed to receive CachyOS key: %w", err) | |
| 210 | + | } | |
| 211 | + | if _, err := h.chrExec.ExecuteInChroot(ctx, mountPoint, "pacman-key", "--lsign-key", | |
| 212 | + | "F3B607488DB35A47"); err != nil { | |
| 213 | + | return fmt.Errorf("failed to sign CachyOS key: %w", err) | |
| 214 | + | } | |
| 215 | + | if _, err := h.chrExec.ExecuteInChroot(ctx, mountPoint, "pacman", "-U", "--noconfirm", | |
| 216 | + | "https://mirror.cachyos.org/repo/x86_64/cachyos/cachyos-keyring-20240331-1-any.pkg.tar.zst", | |
| 217 | + | "https://mirror.cachyos.org/repo/x86_64/cachyos/cachyos-mirrorlist-18-1-any.pkg.tar.zst", | |
| 218 | + | "https://mirror.cachyos.org/repo/x86_64/cachyos/cachyos-v3-mirrorlist-18-1-any.pkg.tar.zst", | |
| 219 | + | "https://mirror.cachyos.org/repo/x86_64/cachyos/cachyos-v4-mirrorlist-6-1-any.pkg.tar.zst"); err != nil { | |
| 220 | + | return fmt.Errorf("failed to install CachyOS packages: %w", err) | |
| 221 | + | } | |
| 222 | + | pacmanConfPath := filepath.Join(mountPoint, "etc", "pacman.conf") | |
| 223 | + | confBytes, err := h.fs.ReadFile(pacmanConfPath) | |
| 224 | + | if err != nil { | |
| 225 | + | return fmt.Errorf("failed to read pacman.conf: %w", err) | |
| 226 | + | } | |
| 227 | + | conf := ensureCachyOSRepo(string(confBytes)) | |
| 228 | + | return h.fs.WriteFile(pacmanConfPath, []byte(conf), 0644) | |
| 229 | + | } | |
| 230 | + | ||
| 231 | + | func ensureCachyOSRepo(conf string) string { | |
| 232 | + | if strings.Contains(conf, "[cachyos]") { | |
| 233 | + | return conf | |
| 234 | + | } | |
| 235 | + | conf = strings.TrimRight(conf, "\n") | |
| 236 | + | return conf + "\n[cachyos]\nInclude = /etc/pacman.d/cachyos-mirrorlist\n" | |
| 237 | + | } | |
| @@ -75,6 +75,13 @@ func (h *ReposHandler) Handle(ctx context.Context, cmd commands.SetupRepositorie | |||||
| 75 | } | 75 | } | ||
| 76 | } | 76 | } | ||
| 77 | 77 | ||||
| 78 | + | // For CachyOS kernel: setup CachyOS repo BEFORE sync | |||
| 79 | + | if cmd.KernelVariant == packages.KernelCachyOS { | |||
| 80 | + | if err := h.setupCachyOSRepo(ctx, cmd.MountPoint); err != nil { | |||
| 81 | + | return fail("Failed to setup CachyOS repository", err) | |||
| 82 | + | } | |||
| 83 | + | } | |||
| 84 | + | ||||
| 78 | // For chaotic-aur: install keyring and mirrorlist BEFORE adding to pacman.conf | 85 | // For chaotic-aur: install keyring and mirrorlist BEFORE adding to pacman.conf | ||
| 79 | if repo.EnableChaotic() { | 86 | if repo.EnableChaotic() { | ||
| 80 | if _, err := h.chrExec.ExecuteInChroot(ctx, cmd.MountPoint, "pacman-key", "--recv-key", "3056513887B78AEB", "--keyserver", "keyserver.ubuntu.com"); err != nil { | 87 | if _, err := h.chrExec.ExecuteInChroot(ctx, cmd.MountPoint, "pacman-key", "--recv-key", "3056513887B78AEB", "--keyserver", "keyserver.ubuntu.com"); err != nil { | ||
| @@ -195,3 +202,36 @@ func ensureChaoticRepo(conf string) string { | |||||
| 195 | chaotic := "\n[chaotic-aur]\nInclude = /etc/pacman.d/chaotic-mirrorlist\n" | 202 | chaotic := "\n[chaotic-aur]\nInclude = /etc/pacman.d/chaotic-mirrorlist\n" | ||
| 196 | return conf + chaotic | 203 | return conf + chaotic | ||
| 197 | } | 204 | } | ||
| 205 | + | ||||
| 206 | + | func (h *ReposHandler) setupCachyOSRepo(ctx context.Context, mountPoint string) error { | |||
| 207 | + | if _, err := h.chrExec.ExecuteInChroot(ctx, mountPoint, "pacman-key", "--recv-key", | |||
| 208 | + | "F3B607488DB35A47", "--keyserver", "keyserver.ubuntu.com"); err != nil { | |||
| 209 | + | return fmt.Errorf("failed to receive CachyOS key: %w", err) | |||
| 210 | + | } | |||
| 211 | + | if _, err := h.chrExec.ExecuteInChroot(ctx, mountPoint, "pacman-key", "--lsign-key", | |||
| 212 | + | "F3B607488DB35A47"); err != nil { | |||
| 213 | + | return fmt.Errorf("failed to sign CachyOS key: %w", err) | |||
| 214 | + | } | |||
| 215 | + | if _, err := h.chrExec.ExecuteInChroot(ctx, mountPoint, "pacman", "-U", "--noconfirm", | |||
| 216 | + | "https://mirror.cachyos.org/repo/x86_64/cachyos/cachyos-keyring-20240331-1-any.pkg.tar.zst", | |||
| 217 | + | "https://mirror.cachyos.org/repo/x86_64/cachyos/cachyos-mirrorlist-18-1-any.pkg.tar.zst", | |||
| 218 | + | "https://mirror.cachyos.org/repo/x86_64/cachyos/cachyos-v3-mirrorlist-18-1-any.pkg.tar.zst", | |||
| 219 | + | "https://mirror.cachyos.org/repo/x86_64/cachyos/cachyos-v4-mirrorlist-6-1-any.pkg.tar.zst"); err != nil { | |||
| 220 | + | return fmt.Errorf("failed to install CachyOS packages: %w", err) | |||
| 221 | + | } | |||
| 222 | + | pacmanConfPath := filepath.Join(mountPoint, "etc", "pacman.conf") | |||
| 223 | + | confBytes, err := h.fs.ReadFile(pacmanConfPath) | |||
| 224 | + | if err != nil { | |||
| 225 | + | return fmt.Errorf("failed to read pacman.conf: %w", err) | |||
| 226 | + | } | |||
| 227 | + | conf := ensureCachyOSRepo(string(confBytes)) | |||
| 228 | + | return h.fs.WriteFile(pacmanConfPath, []byte(conf), 0644) | |||
| 229 | + | } | |||
| 230 | + | ||||
| 231 | + | func ensureCachyOSRepo(conf string) string { | |||
| 232 | + | if strings.Contains(conf, "[cachyos]") { | |||
| 233 | + | return conf | |||
| 234 | + | } | |||
| 235 | + | conf = strings.TrimRight(conf, "\n") | |||
| 236 | + | return conf + "\n[cachyos]\nInclude = /etc/pacman.d/cachyos-mirrorlist\n" | |||
| 237 | + | } | |||
M internal/application/handlers/repos_handler_test.go +64 -0
| @@ -2,6 +2,8 @@ package handlers | |||
| 2 | 2 | ||
| 3 | 3 | import ( | |
| 4 | 4 | "context" | |
| 5 | + | "fmt" | |
| 6 | + | "strings" | |
| 5 | 7 | "testing" | |
| 6 | 8 | ||
| 7 | 9 | "github.com/bnema/archup/internal/application/commands" | |
| @@ -103,6 +105,68 @@ func TestReposHandler_Handle_AllEnabled(t *testing.T) { | |||
| 103 | 105 | } | |
| 104 | 106 | } | |
| 105 | 107 | ||
| 108 | + | func TestReposHandler_Handle_CachyOSKernel_AddsCachyOSRepo(t *testing.T) { | |
| 109 | + | ctrl := gomock.NewController(t) | |
| 110 | + | defer ctrl.Finish() | |
| 111 | + | ||
| 112 | + | mockFS := mocks.NewMockFileSystem(ctrl) | |
| 113 | + | mockChrExec := mocks.NewMockChrootExecutor(ctrl) | |
| 114 | + | mockLogger := mocks.NewMockLogger(ctrl) | |
| 115 | + | ||
| 116 | + | mockLogger.EXPECT().Info(gomock.Any(), gomock.Any()).AnyTimes() | |
| 117 | + | mockLogger.EXPECT().Warn(gomock.Any(), gomock.Any()).AnyTimes() | |
| 118 | + | // extra.packages read will fail gracefully | |
| 119 | + | mockFS.EXPECT().ReadFile(gomock.Not("/mnt/etc/pacman.conf")).Return(nil, fmt.Errorf("not found")).AnyTimes() | |
| 120 | + | ||
| 121 | + | // Expect: recv CachyOS key | |
| 122 | + | mockChrExec.EXPECT().ExecuteInChroot(gomock.Any(), "/mnt", "pacman-key", "--recv-key", | |
| 123 | + | "F3B607488DB35A47", "--keyserver", "keyserver.ubuntu.com").Return([]byte{}, nil) | |
| 124 | + | ||
| 125 | + | // Expect: lsign CachyOS key | |
| 126 | + | mockChrExec.EXPECT().ExecuteInChroot(gomock.Any(), "/mnt", "pacman-key", "--lsign-key", | |
| 127 | + | "F3B607488DB35A47").Return([]byte{}, nil) | |
| 128 | + | ||
| 129 | + | // Expect: pacman -U for CachyOS packages (4 URLs) | |
| 130 | + | mockChrExec.EXPECT().ExecuteInChroot(gomock.Any(), "/mnt", "pacman", "-U", "--noconfirm", | |
| 131 | + | "https://mirror.cachyos.org/repo/x86_64/cachyos/cachyos-keyring-20240331-1-any.pkg.tar.zst", | |
| 132 | + | "https://mirror.cachyos.org/repo/x86_64/cachyos/cachyos-mirrorlist-18-1-any.pkg.tar.zst", | |
| 133 | + | "https://mirror.cachyos.org/repo/x86_64/cachyos/cachyos-v3-mirrorlist-18-1-any.pkg.tar.zst", | |
| 134 | + | "https://mirror.cachyos.org/repo/x86_64/cachyos/cachyos-v4-mirrorlist-6-1-any.pkg.tar.zst", | |
| 135 | + | ).Return([]byte{}, nil) | |
| 136 | + | ||
| 137 | + | // Expect: ReadFile for pacman.conf | |
| 138 | + | mockFS.EXPECT().ReadFile("/mnt/etc/pacman.conf").Return([]byte("[core]\nInclude = /etc/pacman.d/mirrorlist\n"), nil) | |
| 139 | + | ||
| 140 | + | // Expect: WriteFile with content containing [cachyos] | |
| 141 | + | mockFS.EXPECT().WriteFile("/mnt/etc/pacman.conf", gomock.Any(), gomock.Any()).DoAndReturn( | |
| 142 | + | func(path string, data []byte, perm interface{}) error { | |
| 143 | + | if !strings.Contains(string(data), "[cachyos]") { | |
| 144 | + | t.Errorf("expected pacman.conf to contain [cachyos], got:\n%s", string(data)) | |
| 145 | + | } | |
| 146 | + | return nil | |
| 147 | + | }) | |
| 148 | + | ||
| 149 | + | handler := NewReposHandler(mockFS, mockChrExec, mockLogger) | |
| 150 | + | ||
| 151 | + | cmd := commands.SetupRepositoriesCommand{ | |
| 152 | + | MountPoint: "/mnt", | |
| 153 | + | EnableMultilib: false, | |
| 154 | + | EnableChaotic: false, | |
| 155 | + | AURHelper: packages.AURHelperParu, | |
| 156 | + | KernelVariant: packages.KernelCachyOS, | |
| 157 | + | } | |
| 158 | + | ||
| 159 | + | result, err := handler.Handle(context.Background(), cmd) | |
| 160 | + | ||
| 161 | + | if err != nil { | |
| 162 | + | t.Fatalf("expected no error, got %v", err) | |
| 163 | + | } | |
| 164 | + | ||
| 165 | + | if !result.Success { | |
| 166 | + | t.Error("expected success") | |
| 167 | + | } | |
| 168 | + | } | |
| 169 | + | ||
| 106 | 170 | func TestReposHandler_Handle_WithAdditionalRepos(t *testing.T) { | |
| 107 | 171 | ctrl := gomock.NewController(t) | |
| 108 | 172 | defer ctrl.Finish() | |
| @@ -2,6 +2,8 @@ package handlers | |||||
| 2 | 2 | ||||
| 3 | import ( | 3 | import ( | ||
| 4 | "context" | 4 | "context" | ||
| 5 | + | "fmt" | |||
| 6 | + | "strings" | |||
| 5 | "testing" | 7 | "testing" | ||
| 6 | 8 | ||||
| 7 | "github.com/bnema/archup/internal/application/commands" | 9 | "github.com/bnema/archup/internal/application/commands" | ||
| @@ -103,6 +105,68 @@ func TestReposHandler_Handle_AllEnabled(t *testing.T) { | |||||
| 103 | } | 105 | } | ||
| 104 | } | 106 | } | ||
| 105 | 107 | ||||
| 108 | + | func TestReposHandler_Handle_CachyOSKernel_AddsCachyOSRepo(t *testing.T) { | |||
| 109 | + | ctrl := gomock.NewController(t) | |||
| 110 | + | defer ctrl.Finish() | |||
| 111 | + | ||||
| 112 | + | mockFS := mocks.NewMockFileSystem(ctrl) | |||
| 113 | + | mockChrExec := mocks.NewMockChrootExecutor(ctrl) | |||
| 114 | + | mockLogger := mocks.NewMockLogger(ctrl) | |||
| 115 | + | ||||
| 116 | + | mockLogger.EXPECT().Info(gomock.Any(), gomock.Any()).AnyTimes() | |||
| 117 | + | mockLogger.EXPECT().Warn(gomock.Any(), gomock.Any()).AnyTimes() | |||
| 118 | + | // extra.packages read will fail gracefully | |||
| 119 | + | mockFS.EXPECT().ReadFile(gomock.Not("/mnt/etc/pacman.conf")).Return(nil, fmt.Errorf("not found")).AnyTimes() | |||
| 120 | + | ||||
| 121 | + | // Expect: recv CachyOS key | |||
| 122 | + | mockChrExec.EXPECT().ExecuteInChroot(gomock.Any(), "/mnt", "pacman-key", "--recv-key", | |||
| 123 | + | "F3B607488DB35A47", "--keyserver", "keyserver.ubuntu.com").Return([]byte{}, nil) | |||
| 124 | + | ||||
| 125 | + | // Expect: lsign CachyOS key | |||
| 126 | + | mockChrExec.EXPECT().ExecuteInChroot(gomock.Any(), "/mnt", "pacman-key", "--lsign-key", | |||
| 127 | + | "F3B607488DB35A47").Return([]byte{}, nil) | |||
| 128 | + | ||||
| 129 | + | // Expect: pacman -U for CachyOS packages (4 URLs) | |||
| 130 | + | mockChrExec.EXPECT().ExecuteInChroot(gomock.Any(), "/mnt", "pacman", "-U", "--noconfirm", | |||
| 131 | + | "https://mirror.cachyos.org/repo/x86_64/cachyos/cachyos-keyring-20240331-1-any.pkg.tar.zst", | |||
| 132 | + | "https://mirror.cachyos.org/repo/x86_64/cachyos/cachyos-mirrorlist-18-1-any.pkg.tar.zst", | |||
| 133 | + | "https://mirror.cachyos.org/repo/x86_64/cachyos/cachyos-v3-mirrorlist-18-1-any.pkg.tar.zst", | |||
| 134 | + | "https://mirror.cachyos.org/repo/x86_64/cachyos/cachyos-v4-mirrorlist-6-1-any.pkg.tar.zst", | |||
| 135 | + | ).Return([]byte{}, nil) | |||
| 136 | + | ||||
| 137 | + | // Expect: ReadFile for pacman.conf | |||
| 138 | + | mockFS.EXPECT().ReadFile("/mnt/etc/pacman.conf").Return([]byte("[core]\nInclude = /etc/pacman.d/mirrorlist\n"), nil) | |||
| 139 | + | ||||
| 140 | + | // Expect: WriteFile with content containing [cachyos] | |||
| 141 | + | mockFS.EXPECT().WriteFile("/mnt/etc/pacman.conf", gomock.Any(), gomock.Any()).DoAndReturn( | |||
| 142 | + | func(path string, data []byte, perm interface{}) error { | |||
| 143 | + | if !strings.Contains(string(data), "[cachyos]") { | |||
| 144 | + | t.Errorf("expected pacman.conf to contain [cachyos], got:\n%s", string(data)) | |||
| 145 | + | } | |||
| 146 | + | return nil | |||
| 147 | + | }) | |||
| 148 | + | ||||
| 149 | + | handler := NewReposHandler(mockFS, mockChrExec, mockLogger) | |||
| 150 | + | ||||
| 151 | + | cmd := commands.SetupRepositoriesCommand{ | |||
| 152 | + | MountPoint: "/mnt", | |||
| 153 | + | EnableMultilib: false, | |||
| 154 | + | EnableChaotic: false, | |||
| 155 | + | AURHelper: packages.AURHelperParu, | |||
| 156 | + | KernelVariant: packages.KernelCachyOS, | |||
| 157 | + | } | |||
| 158 | + | ||||
| 159 | + | result, err := handler.Handle(context.Background(), cmd) | |||
| 160 | + | ||||
| 161 | + | if err != nil { | |||
| 162 | + | t.Fatalf("expected no error, got %v", err) | |||
| 163 | + | } | |||
| 164 | + | ||||
| 165 | + | if !result.Success { | |||
| 166 | + | t.Error("expected success") | |||
| 167 | + | } | |||
| 168 | + | } | |||
| 169 | + | ||||
| 106 | func TestReposHandler_Handle_WithAdditionalRepos(t *testing.T) { | 170 | func TestReposHandler_Handle_WithAdditionalRepos(t *testing.T) { | ||
| 107 | ctrl := gomock.NewController(t) | 171 | ctrl := gomock.NewController(t) | ||
| 108 | defer ctrl.Finish() | 172 | defer ctrl.Finish() | ||
M internal/interfaces/tui/handlers/install_handler.go +1 -0
| @@ -198,6 +198,7 @@ func CreateInstallationCommand(app AppContext, formData models.FormData) tea.Cmd | |||
| 198 | 198 | EnableMultilib: true, | |
| 199 | 199 | EnableChaotic: true, | |
| 200 | 200 | AURHelper: parseAURHelper(formData.AURHelper), | |
| 201 | + | KernelVariant: parseKernelVariant(formData.KernelVariant), | |
| 201 | 202 | } | |
| 202 | 203 | if _, err := svc.RunRepositorySetup(ctx, repoCmd); err != nil { | |
| 203 | 204 | logger.Error("Repository setup failed", "error", err) | |
| @@ -198,6 +198,7 @@ func CreateInstallationCommand(app AppContext, formData models.FormData) tea.Cmd | |||||
| 198 | EnableMultilib: true, | 198 | EnableMultilib: true, | ||
| 199 | EnableChaotic: true, | 199 | EnableChaotic: true, | ||
| 200 | AURHelper: parseAURHelper(formData.AURHelper), | 200 | AURHelper: parseAURHelper(formData.AURHelper), | ||
| 201 | + | KernelVariant: parseKernelVariant(formData.KernelVariant), | |||
| 201 | } | 202 | } | ||
| 202 | if _, err := svc.RunRepositorySetup(ctx, repoCmd); err != nil { | 203 | if _, err := svc.RunRepositorySetup(ctx, repoCmd); err != nil { | ||
| 203 | logger.Error("Repository setup failed", "error", err) | 204 | logger.Error("Repository setup failed", "error", err) | ||