detached 5214853

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 VerifiedCD88F9E564E0102646FB6077A1A07C7C7C3E349D · 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"
44
55 // SetupRepositoriesCommand contains data for repository configuration
66 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
1213 }
@@ -4,9 +4,10 @@ import "github.com/bnema/archup/internal/domain/packages"
4 4
5 // SetupRepositoriesCommand contains data for repository configuration5 // SetupRepositoriesCommand contains data for repository configuration
6 type SetupRepositoriesCommand struct {6 type SetupRepositoriesCommand struct {
7- MountPoint string // Root mount point7+ MountPoint string // Root mount point
8- EnableMultilib bool // Enable multilib repository8+ EnableMultilib bool // Enable multilib repository
9- EnableChaotic bool // Enable Chaotic-AUR repository9+ EnableChaotic bool // Enable Chaotic-AUR repository
10- AURHelper packages.AURHelper // AURHelperParu or AURHelperYay10+ AURHelper packages.AURHelper // AURHelperParu or AURHelperYay
11- AdditionalRepos []string // Additional repository URLs11+ 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
7575 }
7676 }
7777
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+
7885 // For chaotic-aur: install keyring and mirrorlist BEFORE adding to pacman.conf
7986 if repo.EnableChaotic() {
8087 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 {
195202 chaotic := "\n[chaotic-aur]\nInclude = /etc/pacman.d/chaotic-mirrorlist\n"
196203 return conf + chaotic
197204 }
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.conf85 // 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 + chaotic203 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
22
33 import (
44 "context"
5+ "fmt"
6+ "strings"
57 "testing"
68
79 "github.com/bnema/archup/internal/application/commands"
@@ -103,6 +105,68 @@ func TestReposHandler_Handle_AllEnabled(t *testing.T) {
103105 }
104106 }
105107
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+
106170 func TestReposHandler_Handle_WithAdditionalRepos(t *testing.T) {
107171 ctrl := gomock.NewController(t)
108172 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
198198 EnableMultilib: true,
199199 EnableChaotic: true,
200200 AURHelper: parseAURHelper(formData.AURHelper),
201+ KernelVariant: parseKernelVariant(formData.KernelVariant),
201202 }
202203 if _, err := svc.RunRepositorySetup(ctx, repoCmd); err != nil {
203204 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)

Keyboard shortcuts

Single-key shortcuts are on. ctrl k and esc work whatever this says.

ctrl k :
go to a section, branch, tag, file or repository
?
this list
j k
move the row cursor down / up
enter
open the row under the cursor; on Files, a directory opens or closes in place
↑ ↓ ← → home end
in the file tree (focus on a row): previous / next row, close / open a directory or go to its parent / first entry, first / last row. They never open a file
l h
on Files: open a directory or a file / close a directory or go up to its parent, then to the parent page. Elsewhere: into the side panel (on a merge request, its files) / back to the content; there j k move between its controls
g g G
first / last row
/
filter the list; on Files, find a file anywhere in the repository (esc clears, enter opens the best match)
b
switch branch or tag
g s g f g c g b g m g i g r g a
tabs: summary, files, commits, branches & tags, merge requests, issues, releases, actions (when available)
g h
home
w
wide mode: use the whole width of the screen, or not (remembered)
y
copy a permalink pinned to the commit
d u x
diff: split / unified / collapse or expand all
e E
diff: the whole file in view / every file, with the lines the hunks hide
n
merge request: next note in the diff
esc
close a dialog, leave a field

Branches 1

Tags 0

    ↑↓ moveenter openesc close