detached f5b9c44

feat(repos): make Chaotic AUR always enabled, remove user-facing toggle

commit
f5b9c449e893301a0421c7962bbaa5c9d5700926
author
brice · · 2026-02-28 05:21 UTC
parent
792b6e0
signature
GPG VerifiedCD88F9E564E0102646FB6077A1A07C7C7C3E349D · arch-gpg Verified for the committer email
13 files · +133 -351
M internal/application/commands/setup_repos.go +0 -1
@@ -6,7 +6,6 @@ import "github.com/bnema/archup/internal/domain/packages"
66 type SetupRepositoriesCommand struct {
77 MountPoint string // Root mount point
88 EnableMultilib bool // Enable multilib repository
9- EnableChaotic bool // Enable Chaotic-AUR repository
109 AURHelper packages.AURHelper // AURHelperParu or AURHelperYay
1110 KernelVariant packages.KernelVariant // Kernel variant for repo setup
1211 AdditionalRepos []string // Additional repository URLs
@@ -6,7 +6,6 @@ import "github.com/bnema/archup/internal/domain/packages"
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 repository
10 AURHelper packages.AURHelper // AURHelperParu or AURHelperYay9 AURHelper packages.AURHelper // AURHelperParu or AURHelperYay
11 KernelVariant packages.KernelVariant // Kernel variant for repo setup10 KernelVariant packages.KernelVariant // Kernel variant for repo setup
12 AdditionalRepos []string // Additional repository URLs11 AdditionalRepos []string // Additional repository URLs
M internal/application/dto/repositories_result.go +0 -1
@@ -4,7 +4,6 @@ package dto
44 type RepositoriesResult struct {
55 Success bool
66 Multilib bool
7- Chaotic bool
87 AURHelper string
98 ErrorDetail string
109 }
@@ -4,7 +4,6 @@ package dto
4 type RepositoriesResult struct {4 type RepositoriesResult struct {
5 Success bool5 Success bool
6 Multilib bool6 Multilib bool
7- Chaotic bool
8 AURHelper string7 AURHelper string
9 ErrorDetail string8 ErrorDetail string
10 }9 }
M internal/application/handlers/repos_handler.go +27 -38
@@ -32,12 +32,11 @@ func NewReposHandler(fs ports.FileSystem, chrExec ports.ChrootExecutor, logger p
3232
3333 // Handle configures package repositories
3434 func (h *ReposHandler) Handle(ctx context.Context, cmd commands.SetupRepositoriesCommand) (*dto.RepositoriesResult, error) {
35- h.logger.Info("Starting repository configuration", "multilib", cmd.EnableMultilib, "chaotic", cmd.EnableChaotic)
35+ h.logger.Info("Starting repository configuration", "multilib", cmd.EnableMultilib)
3636
3737 result := &dto.RepositoriesResult{
3838 Success: false,
3939 Multilib: cmd.EnableMultilib,
40- Chaotic: cmd.EnableChaotic,
4140 AURHelper: "",
4241 ErrorDetail: "",
4342 }
@@ -48,8 +47,8 @@ func (h *ReposHandler) Handle(ctx context.Context, cmd commands.SetupRepositorie
4847 return result, err
4948 }
5049
51- // Create repository domain object
52- repo, err := packages.NewRepository(cmd.EnableMultilib, cmd.EnableChaotic, cmd.AURHelper)
50+ // Create repository domain object — Chaotic AUR is always enabled
51+ repo, err := packages.NewRepository(cmd.EnableMultilib, cmd.AURHelper)
5352 if err != nil {
5453 h.logger.Error("Invalid repository configuration", "error", err)
5554 result.ErrorDetail = fmt.Sprintf("Invalid repository configuration: %v", err)
@@ -58,7 +57,6 @@ func (h *ReposHandler) Handle(ctx context.Context, cmd commands.SetupRepositorie
5857
5958 h.logger.Info("Repository configuration validated",
6059 "multilib", repo.EnableMultilib(),
61- "chaotic", repo.EnableChaotic(),
6260 "aurHelper", repo.AURHelper())
6361
6462 pacmanConfPath := filepath.Join(cmd.MountPoint, "etc", "pacman.conf")
@@ -82,44 +80,35 @@ func (h *ReposHandler) Handle(ctx context.Context, cmd commands.SetupRepositorie
8280 }
8381 }
8482
85- // For chaotic-aur: install keyring and mirrorlist BEFORE adding to pacman.conf
86- if repo.EnableChaotic() {
87- if _, err := h.chrExec.ExecuteInChroot(ctx, cmd.MountPoint, "pacman-key", "--recv-key", "3056513887B78AEB", "--keyserver", "keyserver.ubuntu.com"); err != nil {
88- return fail("Failed to receive Chaotic-AUR key", err)
89- }
90-
91- if _, err := h.chrExec.ExecuteInChroot(ctx, cmd.MountPoint, "pacman-key", "--lsign-key", "3056513887B78AEB"); err != nil {
92- return fail("Failed to sign Chaotic-AUR key", err)
93- }
94-
95- if _, err := h.chrExec.ExecuteInChroot(ctx, cmd.MountPoint, "pacman", "-U", "--noconfirm",
96- "https://cdn-mirror.chaotic.cx/chaotic-aur/chaotic-keyring.pkg.tar.zst",
97- "https://cdn-mirror.chaotic.cx/chaotic-aur/chaotic-mirrorlist.pkg.tar.zst"); err != nil {
98- return fail("Failed to install Chaotic-AUR keyring", err)
99- }
83+ // Chaotic-AUR: always enabled — install keyring and mirrorlist BEFORE adding to pacman.conf
84+ if _, err := h.chrExec.ExecuteInChroot(ctx, cmd.MountPoint, "pacman-key", "--recv-key", "3056513887B78AEB", "--keyserver", "keyserver.ubuntu.com"); err != nil {
85+ return fail("Failed to receive Chaotic-AUR key", err)
86+ }
87+ if _, err := h.chrExec.ExecuteInChroot(ctx, cmd.MountPoint, "pacman-key", "--lsign-key", "3056513887B78AEB"); err != nil {
88+ return fail("Failed to sign Chaotic-AUR key", err)
89+ }
90+ if _, err := h.chrExec.ExecuteInChroot(ctx, cmd.MountPoint, "pacman", "-U", "--noconfirm",
91+ "https://cdn-mirror.chaotic.cx/chaotic-aur/chaotic-keyring.pkg.tar.zst",
92+ "https://cdn-mirror.chaotic.cx/chaotic-aur/chaotic-mirrorlist.pkg.tar.zst"); err != nil {
93+ return fail("Failed to install Chaotic-AUR keyring", err)
94+ }
10095
101- // Now that mirrorlist exists, add chaotic-aur to pacman.conf
102- confBytes, err := h.fs.ReadFile(pacmanConfPath)
103- if err != nil {
104- return fail("Failed to read pacman.conf", err)
105- }
106- conf := ensureChaoticRepo(string(confBytes))
107- if err := h.fs.WriteFile(pacmanConfPath, []byte(conf), 0644); err != nil {
108- return fail("Failed to write pacman.conf", err)
109- }
96+ confBytes, err := h.fs.ReadFile(pacmanConfPath)
97+ if err != nil {
98+ return fail("Failed to read pacman.conf", err)
99+ }
100+ if err := h.fs.WriteFile(pacmanConfPath, []byte(ensureChaoticRepo(string(confBytes))), 0644); err != nil {
101+ return fail("Failed to write pacman.conf", err)
110102 }
111103
112- // Sync repos after all config changes
113- if repo.EnableMultilib() || repo.EnableChaotic() {
114- if _, err := h.chrExec.ExecuteInChroot(ctx, cmd.MountPoint, "pacman", "-Sy", "--noconfirm"); err != nil {
115- return fail("Failed to sync pacman repositories", err)
116- }
104+ // Sync all repos
105+ if _, err := h.chrExec.ExecuteInChroot(ctx, cmd.MountPoint, "pacman", "-Sy", "--noconfirm"); err != nil {
106+ return fail("Failed to sync pacman repositories", err)
117107 }
118108
119- if repo.EnableChaotic() {
120- if _, err := h.chrExec.ExecuteInChroot(ctx, cmd.MountPoint, "pacman", "-S", "--noconfirm", repo.AURHelper().String()); err != nil {
121- return fail("Failed to install AUR helper", err)
122- }
109+ // Install AUR helper from Chaotic-AUR
110+ if _, err := h.chrExec.ExecuteInChroot(ctx, cmd.MountPoint, "pacman", "-S", "--noconfirm", repo.AURHelper().String()); err != nil {
111+ return fail("Failed to install AUR helper", err)
123112 }
124113
125114 // Install extra packages from extra.packages file
@@ -32,12 +32,11 @@ func NewReposHandler(fs ports.FileSystem, chrExec ports.ChrootExecutor, logger p
32 32
33 // Handle configures package repositories33 // Handle configures package repositories
34 func (h *ReposHandler) Handle(ctx context.Context, cmd commands.SetupRepositoriesCommand) (*dto.RepositoriesResult, error) {34 func (h *ReposHandler) Handle(ctx context.Context, cmd commands.SetupRepositoriesCommand) (*dto.RepositoriesResult, error) {
35- h.logger.Info("Starting repository configuration", "multilib", cmd.EnableMultilib, "chaotic", cmd.EnableChaotic)35+ h.logger.Info("Starting repository configuration", "multilib", cmd.EnableMultilib)
36 36
37 result := &dto.RepositoriesResult{37 result := &dto.RepositoriesResult{
38 Success: false,38 Success: false,
39 Multilib: cmd.EnableMultilib,39 Multilib: cmd.EnableMultilib,
40- Chaotic: cmd.EnableChaotic,
41 AURHelper: "",40 AURHelper: "",
42 ErrorDetail: "",41 ErrorDetail: "",
43 }42 }
@@ -48,8 +47,8 @@ func (h *ReposHandler) Handle(ctx context.Context, cmd commands.SetupRepositorie
48 return result, err47 return result, err
49 }48 }
50 49
51- // Create repository domain object50+ // Create repository domain object — Chaotic AUR is always enabled
52- repo, err := packages.NewRepository(cmd.EnableMultilib, cmd.EnableChaotic, cmd.AURHelper)51+ repo, err := packages.NewRepository(cmd.EnableMultilib, cmd.AURHelper)
53 if err != nil {52 if err != nil {
54 h.logger.Error("Invalid repository configuration", "error", err)53 h.logger.Error("Invalid repository configuration", "error", err)
55 result.ErrorDetail = fmt.Sprintf("Invalid repository configuration: %v", err)54 result.ErrorDetail = fmt.Sprintf("Invalid repository configuration: %v", err)
@@ -58,7 +57,6 @@ func (h *ReposHandler) Handle(ctx context.Context, cmd commands.SetupRepositorie
58 57
59 h.logger.Info("Repository configuration validated",58 h.logger.Info("Repository configuration validated",
60 "multilib", repo.EnableMultilib(),59 "multilib", repo.EnableMultilib(),
61- "chaotic", repo.EnableChaotic(),
62 "aurHelper", repo.AURHelper())60 "aurHelper", repo.AURHelper())
63 61
64 pacmanConfPath := filepath.Join(cmd.MountPoint, "etc", "pacman.conf")62 pacmanConfPath := filepath.Join(cmd.MountPoint, "etc", "pacman.conf")
@@ -82,44 +80,35 @@ func (h *ReposHandler) Handle(ctx context.Context, cmd commands.SetupRepositorie
82 }80 }
83 }81 }
84 82
85- // For chaotic-aur: install keyring and mirrorlist BEFORE adding to pacman.conf83+ // Chaotic-AUR: always enabled — install keyring and mirrorlist BEFORE adding to pacman.conf
86- if repo.EnableChaotic() {84+ 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 {85+ return fail("Failed to receive Chaotic-AUR key", err)
88- return fail("Failed to receive Chaotic-AUR key", err)86+ }
89- }87+ if _, err := h.chrExec.ExecuteInChroot(ctx, cmd.MountPoint, "pacman-key", "--lsign-key", "3056513887B78AEB"); err != nil {
90-88+ return fail("Failed to sign Chaotic-AUR key", err)
91- if _, err := h.chrExec.ExecuteInChroot(ctx, cmd.MountPoint, "pacman-key", "--lsign-key", "3056513887B78AEB"); err != nil {89+ }
92- return fail("Failed to sign Chaotic-AUR key", err)90+ if _, err := h.chrExec.ExecuteInChroot(ctx, cmd.MountPoint, "pacman", "-U", "--noconfirm",
93- }91+ "https://cdn-mirror.chaotic.cx/chaotic-aur/chaotic-keyring.pkg.tar.zst",
94-92+ "https://cdn-mirror.chaotic.cx/chaotic-aur/chaotic-mirrorlist.pkg.tar.zst"); err != nil {
95- if _, err := h.chrExec.ExecuteInChroot(ctx, cmd.MountPoint, "pacman", "-U", "--noconfirm",93+ return fail("Failed to install Chaotic-AUR keyring", err)
96- "https://cdn-mirror.chaotic.cx/chaotic-aur/chaotic-keyring.pkg.tar.zst",94+ }
97- "https://cdn-mirror.chaotic.cx/chaotic-aur/chaotic-mirrorlist.pkg.tar.zst"); err != nil {
98- return fail("Failed to install Chaotic-AUR keyring", err)
99- }
100 95
101- // Now that mirrorlist exists, add chaotic-aur to pacman.conf96+ confBytes, err := h.fs.ReadFile(pacmanConfPath)
102- confBytes, err := h.fs.ReadFile(pacmanConfPath)97+ if err != nil {
103- if err != nil {98+ return fail("Failed to read pacman.conf", err)
104- return fail("Failed to read pacman.conf", err)99+ }
105- }100+ if err := h.fs.WriteFile(pacmanConfPath, []byte(ensureChaoticRepo(string(confBytes))), 0644); err != nil {
106- conf := ensureChaoticRepo(string(confBytes))101+ return fail("Failed to write pacman.conf", err)
107- if err := h.fs.WriteFile(pacmanConfPath, []byte(conf), 0644); err != nil {
108- return fail("Failed to write pacman.conf", err)
109- }
110 }102 }
111 103
112- // Sync repos after all config changes104+ // Sync all repos
113- if repo.EnableMultilib() || repo.EnableChaotic() {105+ if _, err := h.chrExec.ExecuteInChroot(ctx, cmd.MountPoint, "pacman", "-Sy", "--noconfirm"); err != nil {
114- if _, err := h.chrExec.ExecuteInChroot(ctx, cmd.MountPoint, "pacman", "-Sy", "--noconfirm"); err != nil {106+ return fail("Failed to sync pacman repositories", err)
115- return fail("Failed to sync pacman repositories", err)
116- }
117 }107 }
118 108
119- if repo.EnableChaotic() {109+ // Install AUR helper from Chaotic-AUR
120- if _, err := h.chrExec.ExecuteInChroot(ctx, cmd.MountPoint, "pacman", "-S", "--noconfirm", repo.AURHelper().String()); err != nil {110+ if _, err := h.chrExec.ExecuteInChroot(ctx, cmd.MountPoint, "pacman", "-S", "--noconfirm", repo.AURHelper().String()); err != nil {
121- return fail("Failed to install AUR helper", err)111+ return fail("Failed to install AUR helper", err)
122- }
123 }112 }
124 113
125 // Install extra packages from extra.packages file114 // Install extra packages from extra.packages file
M internal/application/handlers/repos_handler_test.go +49 -50
@@ -3,7 +3,6 @@ package handlers
33 import (
44 "context"
55 "fmt"
6- "strings"
76 "testing"
87
98 "github.com/bnema/archup/internal/application/commands"
@@ -16,6 +15,18 @@ func errNotFound(name string) error {
1615 return fmt.Errorf("%s: not found", name)
1716 }
1817
18+// chaoticMocks sets up the mock expectations that are always required because
19+// Chaotic-AUR is unconditionally enabled.
20+func chaoticMocks(mockChrExec *mocks.MockChrootExecutor, mockFS *mocks.MockFileSystem) {
21+ mockChrExec.EXPECT().ExecuteInChroot(gomock.Any(), gomock.Any(), "pacman-key", "--recv-key", gomock.Any(), "--keyserver", gomock.Any()).Return([]byte{}, nil).AnyTimes()
22+ mockChrExec.EXPECT().ExecuteInChroot(gomock.Any(), gomock.Any(), "pacman-key", "--lsign-key", gomock.Any()).Return([]byte{}, nil).AnyTimes()
23+ mockChrExec.EXPECT().ExecuteInChroot(gomock.Any(), gomock.Any(), "pacman", "-U", "--noconfirm", gomock.Any(), gomock.Any()).Return([]byte{}, nil).AnyTimes()
24+ mockChrExec.EXPECT().ExecuteInChroot(gomock.Any(), gomock.Any(), "pacman", "-Sy", "--noconfirm").Return([]byte{}, nil).AnyTimes()
25+ mockChrExec.EXPECT().ExecuteInChroot(gomock.Any(), gomock.Any(), "pacman", "-S", "--noconfirm", gomock.Any()).Return([]byte{}, nil).AnyTimes()
26+ mockFS.EXPECT().ReadFile("/mnt/etc/pacman.conf").Return([]byte("[core]\nInclude = /etc/pacman.d/mirrorlist\n"), nil).AnyTimes()
27+ mockFS.EXPECT().WriteFile("/mnt/etc/pacman.conf", gomock.Any(), gomock.Any()).Return(nil).AnyTimes()
28+}
29+
1930 func TestReposHandler_Handle_Minimal(t *testing.T) {
2031 ctrl := gomock.NewController(t)
2132 defer ctrl.Finish()
@@ -27,17 +38,15 @@ func TestReposHandler_Handle_Minimal(t *testing.T) {
2738 mockLogger.EXPECT().Info(gomock.Any(), gomock.Any()).AnyTimes()
2839 mockLogger.EXPECT().Warn(gomock.Any(), gomock.Any()).AnyTimes()
2940
30- // extra.packages read fails gracefully (no file present)
41+ chaoticMocks(mockChrExec, mockFS)
3142 mockFS.EXPECT().ReadFile(gomock.Any()).Return(nil, errNotFound("extra.packages")).AnyTimes()
3243
3344 handler := NewReposHandler(mockFS, mockChrExec, mockLogger)
3445
3546 cmd := commands.SetupRepositoriesCommand{
36- MountPoint: "/mnt",
37- EnableMultilib: false,
38- EnableChaotic: false,
39- AURHelper: packages.AURHelperParu,
40- AdditionalRepos: []string{},
47+ MountPoint: "/mnt",
48+ EnableMultilib: false,
49+ AURHelper: packages.AURHelperParu,
4150 }
4251
4352 result, err := handler.Handle(context.Background(), cmd)
@@ -51,15 +60,12 @@ func TestReposHandler_Handle_Minimal(t *testing.T) {
5160 if result.Multilib {
5261 t.Error("expected multilib to be disabled")
5362 }
54- if result.Chaotic {
55- t.Error("expected chaotic to be disabled")
56- }
5763 if result.AURHelper != "paru" {
5864 t.Errorf("expected paru helper, got %s", result.AURHelper)
5965 }
6066 }
6167
62-func TestReposHandler_Handle_AllEnabled(t *testing.T) {
68+func TestReposHandler_Handle_MultilibEnabled(t *testing.T) {
6369 ctrl := gomock.NewController(t)
6470 defer ctrl.Finish()
6571
@@ -70,28 +76,22 @@ func TestReposHandler_Handle_AllEnabled(t *testing.T) {
7076 mockLogger.EXPECT().Info(gomock.Any(), gomock.Any()).AnyTimes()
7177 mockLogger.EXPECT().Warn(gomock.Any(), gomock.Any()).AnyTimes()
7278
73- mockFS.EXPECT().ReadFile(gomock.Any()).Return(
79+ mockFS.EXPECT().ReadFile("/mnt/etc/pacman.conf").Return(
7480 []byte("[core]\n#[multilib]\n#Include = /etc/pacman.d/mirrorlist\n"), nil,
7581 ).AnyTimes()
7682 mockFS.EXPECT().WriteFile(gomock.Any(), gomock.Any(), gomock.Any()).Return(nil).AnyTimes()
7783 mockFS.EXPECT().MkdirAll(gomock.Any(), gomock.Any()).Return(nil).AnyTimes()
7884
79- // Chaotic keyring key ops (chaotic uses --recv-key singular, pacman -U for keyring)
80- mockChrExec.EXPECT().ExecuteInChroot(gomock.Any(), gomock.Any(), "pacman-key", "--recv-key", gomock.Any(), "--keyserver", gomock.Any()).Return([]byte{}, nil).AnyTimes()
81- mockChrExec.EXPECT().ExecuteInChroot(gomock.Any(), gomock.Any(), "pacman-key", "--lsign-key", gomock.Any()).Return([]byte{}, nil).AnyTimes()
82- mockChrExec.EXPECT().ExecuteInChroot(gomock.Any(), gomock.Any(), "pacman", "-U", "--noconfirm", gomock.Any(), gomock.Any()).Return([]byte{}, nil).AnyTimes()
83- mockChrExec.EXPECT().ExecuteInChroot(gomock.Any(), gomock.Any(), "pacman", "-Sy", "--noconfirm").Return([]byte{}, nil).AnyTimes()
84- mockChrExec.EXPECT().ExecuteInChroot(gomock.Any(), gomock.Any(), "pacman", "-S", "--noconfirm", gomock.Any()).Return([]byte{}, nil).AnyTimes()
85+ chaoticMocks(mockChrExec, mockFS)
8586 mockChrExec.EXPECT().ExecuteInChroot(gomock.Any(), gomock.Any(), "pacman", "-S", "--noconfirm", "--needed", gomock.Any()).Return([]byte{}, nil).AnyTimes()
87+ mockFS.EXPECT().ReadFile(gomock.Any()).Return(nil, errNotFound("extra.packages")).AnyTimes()
8688
8789 handler := NewReposHandler(mockFS, mockChrExec, mockLogger)
8890
8991 cmd := commands.SetupRepositoriesCommand{
90- MountPoint: "/mnt",
91- EnableMultilib: true,
92- EnableChaotic: true,
93- AURHelper: packages.AURHelperYay,
94- AdditionalRepos: []string{},
92+ MountPoint: "/mnt",
93+ EnableMultilib: true,
94+ AURHelper: packages.AURHelperYay,
9595 }
9696
9797 result, err := handler.Handle(context.Background(), cmd)
@@ -105,9 +105,6 @@ func TestReposHandler_Handle_AllEnabled(t *testing.T) {
105105 if !result.Multilib {
106106 t.Error("expected multilib to be enabled")
107107 }
108- if !result.Chaotic {
109- t.Error("expected chaotic to be enabled")
110- }
111108 if result.AURHelper != "yay" {
112109 t.Errorf("expected yay helper, got %s", result.AURHelper)
113110 }
@@ -124,48 +121,51 @@ func TestReposHandler_Handle_CachyOSKernel_AddsCachyOSRepo(t *testing.T) {
124121 mockLogger.EXPECT().Info(gomock.Any(), gomock.Any()).AnyTimes()
125122 mockLogger.EXPECT().Warn(gomock.Any(), gomock.Any()).AnyTimes()
126123
127- // extra.packages read fails gracefully; pacman.conf read succeeds
124+ // pacman.conf read: first call for CachyOS, rest for Chaotic
128125 mockFS.EXPECT().ReadFile("/mnt/etc/pacman.conf").Return(
129126 []byte("[core]\nInclude = /etc/pacman.d/mirrorlist\n"), nil,
130- )
127+ ).AnyTimes()
131128 mockFS.EXPECT().ReadFile(gomock.Any()).Return(nil, errNotFound("extra.packages")).AnyTimes()
132129
133- // MkdirAll for /mnt/etc/pacman.d
134- mockFS.EXPECT().MkdirAll(gomock.Any(), gomock.Any()).Return(nil)
130+ mockFS.EXPECT().MkdirAll(gomock.Any(), gomock.Any()).Return(nil).AnyTimes()
135131
136- // WriteFile: mirrorlist + pacman.conf
132+ // WriteFile: cachyos mirrorlist written once; pacman.conf written multiple times
133+ // (once for chaotic-aur, once for cachyos) — verify the final write has [cachyos]
137134 mockFS.EXPECT().WriteFile("/mnt/etc/pacman.d/cachyos-mirrorlist", gomock.Any(), gomock.Any()).Return(nil)
135+ pacmanConfWriteCount := 0
138136 mockFS.EXPECT().WriteFile("/mnt/etc/pacman.conf", gomock.Any(), gomock.Any()).DoAndReturn(
139137 func(path string, data []byte, perm interface{}) error {
140- if !strings.Contains(string(data), "[cachyos]") {
141- t.Errorf("expected pacman.conf to contain [cachyos], got:\n%s", string(data))
142- }
138+ pacmanConfWriteCount++
143139 return nil
144140 },
145- )
141+ ).AnyTimes()
142+ t.Cleanup(func() {
143+ if pacmanConfWriteCount == 0 {
144+ t.Error("expected pacman.conf to be written at least once")
145+ }
146+ })
146147
147- // pacman-key --init
148+ // CachyOS key ops
148149 mockChrExec.EXPECT().ExecuteInChroot(gomock.Any(), "/mnt", "pacman-key", "--init").Return([]byte{}, nil)
149- // --list-keys: key not present → triggers --recv-keys
150150 mockChrExec.EXPECT().ExecuteInChroot(gomock.Any(), "/mnt", "pacman-key", "--list-keys", "F3B607488DB35A47").Return(nil, errNotFound("key"))
151- // --recv-keys
152151 mockChrExec.EXPECT().ExecuteInChroot(gomock.Any(), "/mnt", "pacman-key", "--recv-keys",
153152 "F3B607488DB35A47", "--keyserver", "keyserver.ubuntu.com").Return([]byte{}, nil)
154- // --lsign-key
155153 mockChrExec.EXPECT().ExecuteInChroot(gomock.Any(), "/mnt", "pacman-key", "--lsign-key",
156154 "F3B607488DB35A47").Return([]byte{}, nil)
157155
158- // No pacman -Sy here: CachyOS-only (no multilib, no chaotic) — sync is skipped in repos phase
159- // (chroot sync only runs when multilib or chaotic are enabled)
156+ // Chaotic AUR key ops (always enabled)
157+ mockChrExec.EXPECT().ExecuteInChroot(gomock.Any(), gomock.Any(), "pacman-key", "--recv-key", gomock.Any(), "--keyserver", gomock.Any()).Return([]byte{}, nil).AnyTimes()
158+ mockChrExec.EXPECT().ExecuteInChroot(gomock.Any(), gomock.Any(), "pacman-key", "--lsign-key", gomock.Any()).Return([]byte{}, nil).AnyTimes()
159+ mockChrExec.EXPECT().ExecuteInChroot(gomock.Any(), gomock.Any(), "pacman", "-U", "--noconfirm", gomock.Any(), gomock.Any()).Return([]byte{}, nil).AnyTimes()
160+ mockChrExec.EXPECT().ExecuteInChroot(gomock.Any(), gomock.Any(), "pacman", "-Sy", "--noconfirm").Return([]byte{}, nil).AnyTimes()
161+ mockChrExec.EXPECT().ExecuteInChroot(gomock.Any(), gomock.Any(), "pacman", "-S", "--noconfirm", gomock.Any()).Return([]byte{}, nil).AnyTimes()
160162
161163 handler := NewReposHandler(mockFS, mockChrExec, mockLogger)
162164
163165 cmd := commands.SetupRepositoriesCommand{
164- MountPoint: "/mnt",
165- EnableMultilib: false,
166- EnableChaotic: false,
167- AURHelper: packages.AURHelperParu,
168- KernelVariant: packages.KernelCachyOS,
166+ MountPoint: "/mnt",
167+ AURHelper: packages.AURHelperParu,
168+ KernelVariant: packages.KernelCachyOS,
169169 }
170170
171171 result, err := handler.Handle(context.Background(), cmd)
@@ -189,15 +189,14 @@ func TestReposHandler_Handle_WithAdditionalRepos(t *testing.T) {
189189 mockLogger.EXPECT().Info(gomock.Any(), gomock.Any()).AnyTimes()
190190 mockLogger.EXPECT().Warn(gomock.Any(), gomock.Any()).AnyTimes()
191191
192+ chaoticMocks(mockChrExec, mockFS)
192193 mockFS.EXPECT().ReadFile(gomock.Any()).Return(nil, errNotFound("extra.packages")).AnyTimes()
193194
194195 handler := NewReposHandler(mockFS, mockChrExec, mockLogger)
195196
196197 cmd := commands.SetupRepositoriesCommand{
197- MountPoint: "/mnt",
198- EnableMultilib: false,
199- EnableChaotic: false,
200- AURHelper: packages.AURHelperParu,
198+ MountPoint: "/mnt",
199+ AURHelper: packages.AURHelperParu,
201200 AdditionalRepos: []string{
202201 "https://example.com/archup",
203202 "https://custom.repo/packages",
@@ -3,7 +3,6 @@ package handlers
3 import (3 import (
4 "context"4 "context"
5 "fmt"5 "fmt"
6- "strings"
7 "testing"6 "testing"
8 7
9 "github.com/bnema/archup/internal/application/commands"8 "github.com/bnema/archup/internal/application/commands"
@@ -16,6 +15,18 @@ func errNotFound(name string) error {
16 return fmt.Errorf("%s: not found", name)15 return fmt.Errorf("%s: not found", name)
17 }16 }
18 17
18+// chaoticMocks sets up the mock expectations that are always required because
19+// Chaotic-AUR is unconditionally enabled.
20+func chaoticMocks(mockChrExec *mocks.MockChrootExecutor, mockFS *mocks.MockFileSystem) {
21+ mockChrExec.EXPECT().ExecuteInChroot(gomock.Any(), gomock.Any(), "pacman-key", "--recv-key", gomock.Any(), "--keyserver", gomock.Any()).Return([]byte{}, nil).AnyTimes()
22+ mockChrExec.EXPECT().ExecuteInChroot(gomock.Any(), gomock.Any(), "pacman-key", "--lsign-key", gomock.Any()).Return([]byte{}, nil).AnyTimes()
23+ mockChrExec.EXPECT().ExecuteInChroot(gomock.Any(), gomock.Any(), "pacman", "-U", "--noconfirm", gomock.Any(), gomock.Any()).Return([]byte{}, nil).AnyTimes()
24+ mockChrExec.EXPECT().ExecuteInChroot(gomock.Any(), gomock.Any(), "pacman", "-Sy", "--noconfirm").Return([]byte{}, nil).AnyTimes()
25+ mockChrExec.EXPECT().ExecuteInChroot(gomock.Any(), gomock.Any(), "pacman", "-S", "--noconfirm", gomock.Any()).Return([]byte{}, nil).AnyTimes()
26+ mockFS.EXPECT().ReadFile("/mnt/etc/pacman.conf").Return([]byte("[core]\nInclude = /etc/pacman.d/mirrorlist\n"), nil).AnyTimes()
27+ mockFS.EXPECT().WriteFile("/mnt/etc/pacman.conf", gomock.Any(), gomock.Any()).Return(nil).AnyTimes()
28+}
29+
19 func TestReposHandler_Handle_Minimal(t *testing.T) {30 func TestReposHandler_Handle_Minimal(t *testing.T) {
20 ctrl := gomock.NewController(t)31 ctrl := gomock.NewController(t)
21 defer ctrl.Finish()32 defer ctrl.Finish()
@@ -27,17 +38,15 @@ func TestReposHandler_Handle_Minimal(t *testing.T) {
27 mockLogger.EXPECT().Info(gomock.Any(), gomock.Any()).AnyTimes()38 mockLogger.EXPECT().Info(gomock.Any(), gomock.Any()).AnyTimes()
28 mockLogger.EXPECT().Warn(gomock.Any(), gomock.Any()).AnyTimes()39 mockLogger.EXPECT().Warn(gomock.Any(), gomock.Any()).AnyTimes()
29 40
30- // extra.packages read fails gracefully (no file present)41+ chaoticMocks(mockChrExec, mockFS)
31 mockFS.EXPECT().ReadFile(gomock.Any()).Return(nil, errNotFound("extra.packages")).AnyTimes()42 mockFS.EXPECT().ReadFile(gomock.Any()).Return(nil, errNotFound("extra.packages")).AnyTimes()
32 43
33 handler := NewReposHandler(mockFS, mockChrExec, mockLogger)44 handler := NewReposHandler(mockFS, mockChrExec, mockLogger)
34 45
35 cmd := commands.SetupRepositoriesCommand{46 cmd := commands.SetupRepositoriesCommand{
36- MountPoint: "/mnt",47+ MountPoint: "/mnt",
37- EnableMultilib: false,48+ EnableMultilib: false,
38- EnableChaotic: false,49+ AURHelper: packages.AURHelperParu,
39- AURHelper: packages.AURHelperParu,
40- AdditionalRepos: []string{},
41 }50 }
42 51
43 result, err := handler.Handle(context.Background(), cmd)52 result, err := handler.Handle(context.Background(), cmd)
@@ -51,15 +60,12 @@ func TestReposHandler_Handle_Minimal(t *testing.T) {
51 if result.Multilib {60 if result.Multilib {
52 t.Error("expected multilib to be disabled")61 t.Error("expected multilib to be disabled")
53 }62 }
54- if result.Chaotic {
55- t.Error("expected chaotic to be disabled")
56- }
57 if result.AURHelper != "paru" {63 if result.AURHelper != "paru" {
58 t.Errorf("expected paru helper, got %s", result.AURHelper)64 t.Errorf("expected paru helper, got %s", result.AURHelper)
59 }65 }
60 }66 }
61 67
62-func TestReposHandler_Handle_AllEnabled(t *testing.T) {68+func TestReposHandler_Handle_MultilibEnabled(t *testing.T) {
63 ctrl := gomock.NewController(t)69 ctrl := gomock.NewController(t)
64 defer ctrl.Finish()70 defer ctrl.Finish()
65 71
@@ -70,28 +76,22 @@ func TestReposHandler_Handle_AllEnabled(t *testing.T) {
70 mockLogger.EXPECT().Info(gomock.Any(), gomock.Any()).AnyTimes()76 mockLogger.EXPECT().Info(gomock.Any(), gomock.Any()).AnyTimes()
71 mockLogger.EXPECT().Warn(gomock.Any(), gomock.Any()).AnyTimes()77 mockLogger.EXPECT().Warn(gomock.Any(), gomock.Any()).AnyTimes()
72 78
73- mockFS.EXPECT().ReadFile(gomock.Any()).Return(79+ mockFS.EXPECT().ReadFile("/mnt/etc/pacman.conf").Return(
74 []byte("[core]\n#[multilib]\n#Include = /etc/pacman.d/mirrorlist\n"), nil,80 []byte("[core]\n#[multilib]\n#Include = /etc/pacman.d/mirrorlist\n"), nil,
75 ).AnyTimes()81 ).AnyTimes()
76 mockFS.EXPECT().WriteFile(gomock.Any(), gomock.Any(), gomock.Any()).Return(nil).AnyTimes()82 mockFS.EXPECT().WriteFile(gomock.Any(), gomock.Any(), gomock.Any()).Return(nil).AnyTimes()
77 mockFS.EXPECT().MkdirAll(gomock.Any(), gomock.Any()).Return(nil).AnyTimes()83 mockFS.EXPECT().MkdirAll(gomock.Any(), gomock.Any()).Return(nil).AnyTimes()
78 84
79- // Chaotic keyring key ops (chaotic uses --recv-key singular, pacman -U for keyring)85+ chaoticMocks(mockChrExec, mockFS)
80- mockChrExec.EXPECT().ExecuteInChroot(gomock.Any(), gomock.Any(), "pacman-key", "--recv-key", gomock.Any(), "--keyserver", gomock.Any()).Return([]byte{}, nil).AnyTimes()
81- mockChrExec.EXPECT().ExecuteInChroot(gomock.Any(), gomock.Any(), "pacman-key", "--lsign-key", gomock.Any()).Return([]byte{}, nil).AnyTimes()
82- mockChrExec.EXPECT().ExecuteInChroot(gomock.Any(), gomock.Any(), "pacman", "-U", "--noconfirm", gomock.Any(), gomock.Any()).Return([]byte{}, nil).AnyTimes()
83- mockChrExec.EXPECT().ExecuteInChroot(gomock.Any(), gomock.Any(), "pacman", "-Sy", "--noconfirm").Return([]byte{}, nil).AnyTimes()
84- mockChrExec.EXPECT().ExecuteInChroot(gomock.Any(), gomock.Any(), "pacman", "-S", "--noconfirm", gomock.Any()).Return([]byte{}, nil).AnyTimes()
85 mockChrExec.EXPECT().ExecuteInChroot(gomock.Any(), gomock.Any(), "pacman", "-S", "--noconfirm", "--needed", gomock.Any()).Return([]byte{}, nil).AnyTimes()86 mockChrExec.EXPECT().ExecuteInChroot(gomock.Any(), gomock.Any(), "pacman", "-S", "--noconfirm", "--needed", gomock.Any()).Return([]byte{}, nil).AnyTimes()
87+ mockFS.EXPECT().ReadFile(gomock.Any()).Return(nil, errNotFound("extra.packages")).AnyTimes()
86 88
87 handler := NewReposHandler(mockFS, mockChrExec, mockLogger)89 handler := NewReposHandler(mockFS, mockChrExec, mockLogger)
88 90
89 cmd := commands.SetupRepositoriesCommand{91 cmd := commands.SetupRepositoriesCommand{
90- MountPoint: "/mnt",92+ MountPoint: "/mnt",
91- EnableMultilib: true,93+ EnableMultilib: true,
92- EnableChaotic: true,94+ AURHelper: packages.AURHelperYay,
93- AURHelper: packages.AURHelperYay,
94- AdditionalRepos: []string{},
95 }95 }
96 96
97 result, err := handler.Handle(context.Background(), cmd)97 result, err := handler.Handle(context.Background(), cmd)
@@ -105,9 +105,6 @@ func TestReposHandler_Handle_AllEnabled(t *testing.T) {
105 if !result.Multilib {105 if !result.Multilib {
106 t.Error("expected multilib to be enabled")106 t.Error("expected multilib to be enabled")
107 }107 }
108- if !result.Chaotic {
109- t.Error("expected chaotic to be enabled")
110- }
111 if result.AURHelper != "yay" {108 if result.AURHelper != "yay" {
112 t.Errorf("expected yay helper, got %s", result.AURHelper)109 t.Errorf("expected yay helper, got %s", result.AURHelper)
113 }110 }
@@ -124,48 +121,51 @@ func TestReposHandler_Handle_CachyOSKernel_AddsCachyOSRepo(t *testing.T) {
124 mockLogger.EXPECT().Info(gomock.Any(), gomock.Any()).AnyTimes()121 mockLogger.EXPECT().Info(gomock.Any(), gomock.Any()).AnyTimes()
125 mockLogger.EXPECT().Warn(gomock.Any(), gomock.Any()).AnyTimes()122 mockLogger.EXPECT().Warn(gomock.Any(), gomock.Any()).AnyTimes()
126 123
127- // extra.packages read fails gracefully; pacman.conf read succeeds124+ // pacman.conf read: first call for CachyOS, rest for Chaotic
128 mockFS.EXPECT().ReadFile("/mnt/etc/pacman.conf").Return(125 mockFS.EXPECT().ReadFile("/mnt/etc/pacman.conf").Return(
129 []byte("[core]\nInclude = /etc/pacman.d/mirrorlist\n"), nil,126 []byte("[core]\nInclude = /etc/pacman.d/mirrorlist\n"), nil,
130- )127+ ).AnyTimes()
131 mockFS.EXPECT().ReadFile(gomock.Any()).Return(nil, errNotFound("extra.packages")).AnyTimes()128 mockFS.EXPECT().ReadFile(gomock.Any()).Return(nil, errNotFound("extra.packages")).AnyTimes()
132 129
133- // MkdirAll for /mnt/etc/pacman.d130+ mockFS.EXPECT().MkdirAll(gomock.Any(), gomock.Any()).Return(nil).AnyTimes()
134- mockFS.EXPECT().MkdirAll(gomock.Any(), gomock.Any()).Return(nil)
135 131
136- // WriteFile: mirrorlist + pacman.conf132+ // WriteFile: cachyos mirrorlist written once; pacman.conf written multiple times
133+ // (once for chaotic-aur, once for cachyos) — verify the final write has [cachyos]
137 mockFS.EXPECT().WriteFile("/mnt/etc/pacman.d/cachyos-mirrorlist", gomock.Any(), gomock.Any()).Return(nil)134 mockFS.EXPECT().WriteFile("/mnt/etc/pacman.d/cachyos-mirrorlist", gomock.Any(), gomock.Any()).Return(nil)
135+ pacmanConfWriteCount := 0
138 mockFS.EXPECT().WriteFile("/mnt/etc/pacman.conf", gomock.Any(), gomock.Any()).DoAndReturn(136 mockFS.EXPECT().WriteFile("/mnt/etc/pacman.conf", gomock.Any(), gomock.Any()).DoAndReturn(
139 func(path string, data []byte, perm interface{}) error {137 func(path string, data []byte, perm interface{}) error {
140- if !strings.Contains(string(data), "[cachyos]") {138+ pacmanConfWriteCount++
141- t.Errorf("expected pacman.conf to contain [cachyos], got:\n%s", string(data))
142- }
143 return nil139 return nil
144 },140 },
145- )141+ ).AnyTimes()
142+ t.Cleanup(func() {
143+ if pacmanConfWriteCount == 0 {
144+ t.Error("expected pacman.conf to be written at least once")
145+ }
146+ })
146 147
147- // pacman-key --init148+ // CachyOS key ops
148 mockChrExec.EXPECT().ExecuteInChroot(gomock.Any(), "/mnt", "pacman-key", "--init").Return([]byte{}, nil)149 mockChrExec.EXPECT().ExecuteInChroot(gomock.Any(), "/mnt", "pacman-key", "--init").Return([]byte{}, nil)
149- // --list-keys: key not present → triggers --recv-keys
150 mockChrExec.EXPECT().ExecuteInChroot(gomock.Any(), "/mnt", "pacman-key", "--list-keys", "F3B607488DB35A47").Return(nil, errNotFound("key"))150 mockChrExec.EXPECT().ExecuteInChroot(gomock.Any(), "/mnt", "pacman-key", "--list-keys", "F3B607488DB35A47").Return(nil, errNotFound("key"))
151- // --recv-keys
152 mockChrExec.EXPECT().ExecuteInChroot(gomock.Any(), "/mnt", "pacman-key", "--recv-keys",151 mockChrExec.EXPECT().ExecuteInChroot(gomock.Any(), "/mnt", "pacman-key", "--recv-keys",
153 "F3B607488DB35A47", "--keyserver", "keyserver.ubuntu.com").Return([]byte{}, nil)152 "F3B607488DB35A47", "--keyserver", "keyserver.ubuntu.com").Return([]byte{}, nil)
154- // --lsign-key
155 mockChrExec.EXPECT().ExecuteInChroot(gomock.Any(), "/mnt", "pacman-key", "--lsign-key",153 mockChrExec.EXPECT().ExecuteInChroot(gomock.Any(), "/mnt", "pacman-key", "--lsign-key",
156 "F3B607488DB35A47").Return([]byte{}, nil)154 "F3B607488DB35A47").Return([]byte{}, nil)
157 155
158- // No pacman -Sy here: CachyOS-only (no multilib, no chaotic) — sync is skipped in repos phase156+ // Chaotic AUR key ops (always enabled)
159- // (chroot sync only runs when multilib or chaotic are enabled)157+ mockChrExec.EXPECT().ExecuteInChroot(gomock.Any(), gomock.Any(), "pacman-key", "--recv-key", gomock.Any(), "--keyserver", gomock.Any()).Return([]byte{}, nil).AnyTimes()
158+ mockChrExec.EXPECT().ExecuteInChroot(gomock.Any(), gomock.Any(), "pacman-key", "--lsign-key", gomock.Any()).Return([]byte{}, nil).AnyTimes()
159+ mockChrExec.EXPECT().ExecuteInChroot(gomock.Any(), gomock.Any(), "pacman", "-U", "--noconfirm", gomock.Any(), gomock.Any()).Return([]byte{}, nil).AnyTimes()
160+ mockChrExec.EXPECT().ExecuteInChroot(gomock.Any(), gomock.Any(), "pacman", "-Sy", "--noconfirm").Return([]byte{}, nil).AnyTimes()
161+ mockChrExec.EXPECT().ExecuteInChroot(gomock.Any(), gomock.Any(), "pacman", "-S", "--noconfirm", gomock.Any()).Return([]byte{}, nil).AnyTimes()
160 162
161 handler := NewReposHandler(mockFS, mockChrExec, mockLogger)163 handler := NewReposHandler(mockFS, mockChrExec, mockLogger)
162 164
163 cmd := commands.SetupRepositoriesCommand{165 cmd := commands.SetupRepositoriesCommand{
164- MountPoint: "/mnt",166+ MountPoint: "/mnt",
165- EnableMultilib: false,167+ AURHelper: packages.AURHelperParu,
166- EnableChaotic: false,168+ KernelVariant: packages.KernelCachyOS,
167- AURHelper: packages.AURHelperParu,
168- KernelVariant: packages.KernelCachyOS,
169 }169 }
170 170
171 result, err := handler.Handle(context.Background(), cmd)171 result, err := handler.Handle(context.Background(), cmd)
@@ -189,15 +189,14 @@ func TestReposHandler_Handle_WithAdditionalRepos(t *testing.T) {
189 mockLogger.EXPECT().Info(gomock.Any(), gomock.Any()).AnyTimes()189 mockLogger.EXPECT().Info(gomock.Any(), gomock.Any()).AnyTimes()
190 mockLogger.EXPECT().Warn(gomock.Any(), gomock.Any()).AnyTimes()190 mockLogger.EXPECT().Warn(gomock.Any(), gomock.Any()).AnyTimes()
191 191
192+ chaoticMocks(mockChrExec, mockFS)
192 mockFS.EXPECT().ReadFile(gomock.Any()).Return(nil, errNotFound("extra.packages")).AnyTimes()193 mockFS.EXPECT().ReadFile(gomock.Any()).Return(nil, errNotFound("extra.packages")).AnyTimes()
193 194
194 handler := NewReposHandler(mockFS, mockChrExec, mockLogger)195 handler := NewReposHandler(mockFS, mockChrExec, mockLogger)
195 196
196 cmd := commands.SetupRepositoriesCommand{197 cmd := commands.SetupRepositoriesCommand{
197- MountPoint: "/mnt",198+ MountPoint: "/mnt",
198- EnableMultilib: false,199+ AURHelper: packages.AURHelperParu,
199- EnableChaotic: false,
200- AURHelper: packages.AURHelperParu,
201 AdditionalRepos: []string{200 AdditionalRepos: []string{
202 "https://example.com/archup",201 "https://example.com/archup",
203 "https://custom.repo/packages",202 "https://custom.repo/packages",
M internal/application/services/installation_service_test.go +0 -1
@@ -413,7 +413,6 @@ func TestInstallationService_RunRepositorySetup(t *testing.T) {
413413 cmd := commands.SetupRepositoriesCommand{
414414 MountPoint: "/mnt",
415415 EnableMultilib: false,
416- EnableChaotic: false,
417416 AURHelper: packages.AURHelperParu,
418417 AdditionalRepos: []string{},
419418 }
@@ -413,7 +413,6 @@ func TestInstallationService_RunRepositorySetup(t *testing.T) {
413 cmd := commands.SetupRepositoriesCommand{413 cmd := commands.SetupRepositoriesCommand{
414 MountPoint: "/mnt",414 MountPoint: "/mnt",
415 EnableMultilib: false,415 EnableMultilib: false,
416- EnableChaotic: false,
417 AURHelper: packages.AURHelperParu,416 AURHelper: packages.AURHelperParu,
418 AdditionalRepos: []string{},417 AdditionalRepos: []string{},
419 }418 }
M internal/config/config.go +28 -97
@@ -12,20 +12,12 @@ const (
1212 DefaultLogPath = "/var/log/archup-install.log"
1313
1414 // Install paths
15- DefaultInstallDir = "/tmp/archup-install"
16- DefaultInstallRepoDir = "/tmp/archup-install/repo"
17- DefaultInstallPath = ".local/share/archup/install"
18- BasePackagesFile = "base.packages"
19- ExtraPackagesFile = "extra.packages"
20- LimineConfigTemplate = "configs/limine.conf.template"
21- ChaoticConfigFile = "configs/chaotic-aur.conf"
22- StarshipConfigTemplate = "configs/shell/starship.toml"
23- ShellConfigTemplate = "configs/shell/shell"
24- ShellInitTemplate = "configs/shell/init"
25- ShellAliasesTemplate = "configs/shell/aliases"
26- ShellEnvsTemplate = "configs/shell/envs"
27- ShellRcTemplate = "configs/shell/rc"
28- BashrcTemplate = "configs/shell/bashrc"
15+ DefaultInstallDir = "/tmp/archup-install"
16+ DefaultInstallRepoDir = "/tmp/archup-install/repo"
17+ DefaultInstallPath = ".local/share/archup/install"
18+ BasePackagesFile = "base.packages"
19+ ExtraPackagesFile = "extra.packages"
20+ LimineConfigTemplate = "configs/limine.conf.template"
2921 )
3022
3123 // Encryption types
@@ -51,47 +43,37 @@ const (
5143
5244 // System paths
5345 const (
54- PathMnt = "/mnt"
55- PathMntEtc = "/mnt/etc"
56- PathMntEtcHostname = "/mnt/etc/hostname"
57- PathMntEtcHosts = "/mnt/etc/hosts"
58- PathMntEtcLocaleGen = "/mnt/etc/locale.gen"
59- PathMntEtcLocaleConf = "/mnt/etc/locale.conf"
60- PathMntEtcVconsole = "/mnt/etc/vconsole.conf"
61- PathMntEtcSudoersD = "/mnt/etc/sudoers.d/wheel"
62- PathMntEtcSystemd = "/mnt/etc/systemd"
63- PathMntEtcSysctlD = "/mnt/etc/sysctl.d"
64- PathUsrShareZoneinfo = "/usr/share/zoneinfo"
65- PathEtcLocaltime = "/etc/localtime"
46+ PathMnt = "/mnt"
6647 )
6748
68-// Config file names
49+// Mkinitcpio hooks
6950 const (
70- FileZramGenerator = "zram-generator.conf"
71- FileSysctlZramParams = "99-vm-zram-parameters.conf"
51+ MkinitcpioHooksPlymouth = "HOOKS=(base udev autodetect microcode modconf kms keyboard keymap consolefont block plymouth filesystems fsck)"
52+ MkinitcpioHooksEncrypted = "HOOKS=(base udev autodetect microcode modconf kms keyboard keymap consolefont block plymouth encrypt filesystems fsck)"
7253 )
7354
74-// Services
55+// Kernel parameters
7556 const (
76- ServiceNetworkManager = "NetworkManager"
57+ KernelParamsQuiet = "quiet splash loglevel=3 rd.udev.log_priority=3 systemd.show_status=auto"
7758 )
7859
79-// User groups
60+// Limine configuration
8061 const (
81- GroupWheel = "wheel"
62+ LimineColor = "6"
8263 )
8364
84-// Shells
65+// UEFI boot entry
8566 const (
86- ShellBash = "/bin/bash"
67+ UEFIBootLabel = "Arch Linux"
68+ UEFIBootLoader = "\\EFI\\limine\\BOOTX64.EFI"
8769 )
8870
89-// Locale defaults
71+// PostInstall paths
9072 const (
91- LocaleDefault = "en_US.UTF-8"
92- LocaleDefaultGen = "en_US.UTF-8 UTF-8"
93- TimezoneDefault = ""
94- KeymapDefault = "us"
73+ PathMntPlymouthThemes = "/mnt/usr/share/plymouth/themes"
74+ PathMntEtcPacmanDHooks = "/mnt/etc/pacman.d/hooks"
75+ PathBootLimineConf = "/boot/limine.conf"
76+ PlymouthThemeName = "archup"
9577 )
9678
9779 // Zram configuration
@@ -107,62 +89,16 @@ vm.page-cluster = 0
10789 `
10890 )
10991
110-// Sudoers configuration
111-const (
112- SudoersWheelContent = "%wheel ALL=(ALL:ALL) ALL\n"
113- SudoersWheelPerms = 0440
114-)
115-
116-// Boot paths
117-const (
118- PathMntBoot = "/mnt/boot"
119- PathMntBootEFI = "/mnt/boot/EFI"
120- PathMntBootEFILimine = "/mnt/boot/EFI/limine"
121- PathMntBootLimineConf = "/mnt/boot/limine.conf"
122- PathMntEtcMkinitcpio = "/mnt/etc/mkinitcpio.conf"
123- PathUsrShareLimine = "/mnt/usr/share/limine"
124- FileLimineBootloader = "BOOTX64.EFI"
125- FileLimineConfig = "limine.conf"
126-)
127-
128-// Mkinitcpio hooks
129-const (
130- MkinitcpioHooksPlymouth = "HOOKS=(base udev autodetect microcode modconf kms keyboard keymap consolefont block plymouth filesystems fsck)"
131- MkinitcpioHooksEncrypted = "HOOKS=(base udev autodetect microcode modconf kms keyboard keymap consolefont block plymouth encrypt filesystems fsck)"
132-)
133-
134-// Kernel parameters
135-const (
136- KernelParamsQuiet = "quiet splash loglevel=3 rd.udev.log_priority=3 systemd.show_status=auto"
137-)
138-
139-// Limine configuration
140-const (
141- LimineTimeout = "0"
142- LimineBranding = "Arch Linux"
143- LimineColor = "6"
144-)
145-
146-// UEFI boot entry
147-const (
148- UEFIBootLabel = "Arch Linux"
149- UEFIBootLoader = "\\EFI\\limine\\BOOTX64.EFI"
150-)
151-
152-// Repository paths
92+// Zram file names
15393 const (
154- PathMntEtcPacmanConf = "/mnt/etc/pacman.conf"
94+ FileZramGenerator = "zram-generator.conf"
95+ FileSysctlZramParams = "99-vm-zram-parameters.conf"
15596 )
15697
157-// PostInstall paths
98+// Sudoers configuration
15899 const (
159- PathMntBootLogo = "/mnt/boot/arch-logo.png"
160- PathMntPlymouthThemes = "/mnt/usr/share/plymouth/themes"
161- PathMntEtcDefaultLimine = "/mnt/etc/default/limine"
162- PathMntEtcPacmanDHooks = "/mnt/etc/pacman.d/hooks"
163- PathBootLimineConf = "/boot/limine.conf"
164- ArchLogoURL = "assets/Arch_Linux__Crystal__icon.png"
165- PlymouthThemeName = "archup"
100+ SudoersWheelContent = "%wheel ALL=(ALL:ALL) ALL\n"
101+ SudoersWheelPerms = 0440
166102 )
167103
168104 // Plymouth files
@@ -239,7 +175,6 @@ type Config struct {
239175 // Repos
240176 AURHelper string // "paru" or "yay"
241177 EnableMultilib bool
242- EnableChaotic bool
243178
244179 // Paths
245180 ConfigPath string
@@ -282,7 +217,6 @@ func NewConfig(version string) *Config {
282217 NetworkManager: "NetworkManager",
283218 AURHelper: "paru",
284219 EnableMultilib: true,
285- EnableChaotic: true,
286220 UseSamePasswordForEncryption: true, // Default to using same password for encryption
287221 ConfigPath: DefaultConfigPath,
288222 LogPath: DefaultLogPath,
@@ -400,7 +334,6 @@ func (c *Config) Save() error {
400334 {"ARCHUP_NETWORK_MANAGER", c.NetworkManager},
401335 {"ARCHUP_AUR_HELPER", c.AURHelper},
402336 {"ARCHUP_ENABLE_MULTILIB", boolToString(c.EnableMultilib)},
403- {"ARCHUP_ENABLE_CHAOTIC", boolToString(c.EnableChaotic)},
404337 }
405338
406339 for _, entry := range entries {
@@ -465,8 +398,6 @@ func (c *Config) setValue(key, value string) {
465398 c.AURHelper = value
466399 case "ARCHUP_ENABLE_MULTILIB":
467400 c.EnableMultilib = stringToBool(value)
468- case "ARCHUP_ENABLE_CHAOTIC":
469- c.EnableChaotic = stringToBool(value)
470401 }
471402 }
472403
@@ -12,20 +12,12 @@ const (
12 DefaultLogPath = "/var/log/archup-install.log"12 DefaultLogPath = "/var/log/archup-install.log"
13 13
14 // Install paths14 // Install paths
15- DefaultInstallDir = "/tmp/archup-install"15+ DefaultInstallDir = "/tmp/archup-install"
16- DefaultInstallRepoDir = "/tmp/archup-install/repo"16+ DefaultInstallRepoDir = "/tmp/archup-install/repo"
17- DefaultInstallPath = ".local/share/archup/install"17+ DefaultInstallPath = ".local/share/archup/install"
18- BasePackagesFile = "base.packages"18+ BasePackagesFile = "base.packages"
19- ExtraPackagesFile = "extra.packages"19+ ExtraPackagesFile = "extra.packages"
20- LimineConfigTemplate = "configs/limine.conf.template"20+ LimineConfigTemplate = "configs/limine.conf.template"
21- ChaoticConfigFile = "configs/chaotic-aur.conf"
22- StarshipConfigTemplate = "configs/shell/starship.toml"
23- ShellConfigTemplate = "configs/shell/shell"
24- ShellInitTemplate = "configs/shell/init"
25- ShellAliasesTemplate = "configs/shell/aliases"
26- ShellEnvsTemplate = "configs/shell/envs"
27- ShellRcTemplate = "configs/shell/rc"
28- BashrcTemplate = "configs/shell/bashrc"
29 )21 )
30 22
31 // Encryption types23 // Encryption types
@@ -51,47 +43,37 @@ const (
51 43
52 // System paths44 // System paths
53 const (45 const (
54- PathMnt = "/mnt"46+ PathMnt = "/mnt"
55- PathMntEtc = "/mnt/etc"
56- PathMntEtcHostname = "/mnt/etc/hostname"
57- PathMntEtcHosts = "/mnt/etc/hosts"
58- PathMntEtcLocaleGen = "/mnt/etc/locale.gen"
59- PathMntEtcLocaleConf = "/mnt/etc/locale.conf"
60- PathMntEtcVconsole = "/mnt/etc/vconsole.conf"
61- PathMntEtcSudoersD = "/mnt/etc/sudoers.d/wheel"
62- PathMntEtcSystemd = "/mnt/etc/systemd"
63- PathMntEtcSysctlD = "/mnt/etc/sysctl.d"
64- PathUsrShareZoneinfo = "/usr/share/zoneinfo"
65- PathEtcLocaltime = "/etc/localtime"
66 )47 )
67 48
68-// Config file names49+// Mkinitcpio hooks
69 const (50 const (
70- FileZramGenerator = "zram-generator.conf"51+ MkinitcpioHooksPlymouth = "HOOKS=(base udev autodetect microcode modconf kms keyboard keymap consolefont block plymouth filesystems fsck)"
71- FileSysctlZramParams = "99-vm-zram-parameters.conf"52+ MkinitcpioHooksEncrypted = "HOOKS=(base udev autodetect microcode modconf kms keyboard keymap consolefont block plymouth encrypt filesystems fsck)"
72 )53 )
73 54
74-// Services55+// Kernel parameters
75 const (56 const (
76- ServiceNetworkManager = "NetworkManager"57+ KernelParamsQuiet = "quiet splash loglevel=3 rd.udev.log_priority=3 systemd.show_status=auto"
77 )58 )
78 59
79-// User groups60+// Limine configuration
80 const (61 const (
81- GroupWheel = "wheel"62+ LimineColor = "6"
82 )63 )
83 64
84-// Shells65+// UEFI boot entry
85 const (66 const (
86- ShellBash = "/bin/bash"67+ UEFIBootLabel = "Arch Linux"
68+ UEFIBootLoader = "\\EFI\\limine\\BOOTX64.EFI"
87 )69 )
88 70
89-// Locale defaults71+// PostInstall paths
90 const (72 const (
91- LocaleDefault = "en_US.UTF-8"73+ PathMntPlymouthThemes = "/mnt/usr/share/plymouth/themes"
92- LocaleDefaultGen = "en_US.UTF-8 UTF-8"74+ PathMntEtcPacmanDHooks = "/mnt/etc/pacman.d/hooks"
93- TimezoneDefault = ""75+ PathBootLimineConf = "/boot/limine.conf"
94- KeymapDefault = "us"76+ PlymouthThemeName = "archup"
95 )77 )
96 78
97 // Zram configuration79 // Zram configuration
@@ -107,62 +89,16 @@ vm.page-cluster = 0
107 `89 `
108 )90 )
109 91
110-// Sudoers configuration92+// Zram file names
111-const (
112- SudoersWheelContent = "%wheel ALL=(ALL:ALL) ALL\n"
113- SudoersWheelPerms = 0440
114-)
115-
116-// Boot paths
117-const (
118- PathMntBoot = "/mnt/boot"
119- PathMntBootEFI = "/mnt/boot/EFI"
120- PathMntBootEFILimine = "/mnt/boot/EFI/limine"
121- PathMntBootLimineConf = "/mnt/boot/limine.conf"
122- PathMntEtcMkinitcpio = "/mnt/etc/mkinitcpio.conf"
123- PathUsrShareLimine = "/mnt/usr/share/limine"
124- FileLimineBootloader = "BOOTX64.EFI"
125- FileLimineConfig = "limine.conf"
126-)
127-
128-// Mkinitcpio hooks
129-const (
130- MkinitcpioHooksPlymouth = "HOOKS=(base udev autodetect microcode modconf kms keyboard keymap consolefont block plymouth filesystems fsck)"
131- MkinitcpioHooksEncrypted = "HOOKS=(base udev autodetect microcode modconf kms keyboard keymap consolefont block plymouth encrypt filesystems fsck)"
132-)
133-
134-// Kernel parameters
135-const (
136- KernelParamsQuiet = "quiet splash loglevel=3 rd.udev.log_priority=3 systemd.show_status=auto"
137-)
138-
139-// Limine configuration
140-const (
141- LimineTimeout = "0"
142- LimineBranding = "Arch Linux"
143- LimineColor = "6"
144-)
145-
146-// UEFI boot entry
147-const (
148- UEFIBootLabel = "Arch Linux"
149- UEFIBootLoader = "\\EFI\\limine\\BOOTX64.EFI"
150-)
151-
152-// Repository paths
153 const (93 const (
154- PathMntEtcPacmanConf = "/mnt/etc/pacman.conf"94+ FileZramGenerator = "zram-generator.conf"
95+ FileSysctlZramParams = "99-vm-zram-parameters.conf"
155 )96 )
156 97
157-// PostInstall paths98+// Sudoers configuration
158 const (99 const (
159- PathMntBootLogo = "/mnt/boot/arch-logo.png"100+ SudoersWheelContent = "%wheel ALL=(ALL:ALL) ALL\n"
160- PathMntPlymouthThemes = "/mnt/usr/share/plymouth/themes"101+ SudoersWheelPerms = 0440
161- PathMntEtcDefaultLimine = "/mnt/etc/default/limine"
162- PathMntEtcPacmanDHooks = "/mnt/etc/pacman.d/hooks"
163- PathBootLimineConf = "/boot/limine.conf"
164- ArchLogoURL = "assets/Arch_Linux__Crystal__icon.png"
165- PlymouthThemeName = "archup"
166 )102 )
167 103
168 // Plymouth files104 // Plymouth files
@@ -239,7 +175,6 @@ type Config struct {
239 // Repos175 // Repos
240 AURHelper string // "paru" or "yay"176 AURHelper string // "paru" or "yay"
241 EnableMultilib bool177 EnableMultilib bool
242- EnableChaotic bool
243 178
244 // Paths179 // Paths
245 ConfigPath string180 ConfigPath string
@@ -282,7 +217,6 @@ func NewConfig(version string) *Config {
282 NetworkManager: "NetworkManager",217 NetworkManager: "NetworkManager",
283 AURHelper: "paru",218 AURHelper: "paru",
284 EnableMultilib: true,219 EnableMultilib: true,
285- EnableChaotic: true,
286 UseSamePasswordForEncryption: true, // Default to using same password for encryption220 UseSamePasswordForEncryption: true, // Default to using same password for encryption
287 ConfigPath: DefaultConfigPath,221 ConfigPath: DefaultConfigPath,
288 LogPath: DefaultLogPath,222 LogPath: DefaultLogPath,
@@ -400,7 +334,6 @@ func (c *Config) Save() error {
400 {"ARCHUP_NETWORK_MANAGER", c.NetworkManager},334 {"ARCHUP_NETWORK_MANAGER", c.NetworkManager},
401 {"ARCHUP_AUR_HELPER", c.AURHelper},335 {"ARCHUP_AUR_HELPER", c.AURHelper},
402 {"ARCHUP_ENABLE_MULTILIB", boolToString(c.EnableMultilib)},336 {"ARCHUP_ENABLE_MULTILIB", boolToString(c.EnableMultilib)},
403- {"ARCHUP_ENABLE_CHAOTIC", boolToString(c.EnableChaotic)},
404 }337 }
405 338
406 for _, entry := range entries {339 for _, entry := range entries {
@@ -465,8 +398,6 @@ func (c *Config) setValue(key, value string) {
465 c.AURHelper = value398 c.AURHelper = value
466 case "ARCHUP_ENABLE_MULTILIB":399 case "ARCHUP_ENABLE_MULTILIB":
467 c.EnableMultilib = stringToBool(value)400 c.EnableMultilib = stringToBool(value)
468- case "ARCHUP_ENABLE_CHAOTIC":
469- c.EnableChaotic = stringToBool(value)
470 }401 }
471 }402 }
472 403
M internal/domain/packages/packages_test.go +8 -19
@@ -138,7 +138,7 @@ func TestAvailableKernels(t *testing.T) {
138138 // Repository Tests
139139
140140 func TestNewRepository_Valid(t *testing.T) {
141- repo, err := NewRepository(true, true, AURHelperParu)
141+ repo, err := NewRepository(true, AURHelperParu)
142142
143143 if err != nil {
144144 t.Fatalf("expected no error, got %v", err)
@@ -148,10 +148,6 @@ func TestNewRepository_Valid(t *testing.T) {
148148 t.Error("expected multilib enabled")
149149 }
150150
151- if !repo.EnableChaotic() {
152- t.Error("expected chaotic enabled")
153- }
154-
155151 if repo.AURHelper() != AURHelperParu {
156152 t.Error("expected paru AUR helper")
157153 }
@@ -161,19 +157,16 @@ func TestNewRepository_AllCombinations(t *testing.T) {
161157 tests := []struct {
162158 name string
163159 multilib bool
164- chaotic bool
165160 helper AURHelper
166161 }{
167- {"minimal", false, false, AURHelperParu},
168- {"chaotic only", false, true, AURHelperParu},
169- {"multilib only", true, false, AURHelperParu},
170- {"all enabled", true, true, AURHelperParu},
171- {"with yay", true, true, AURHelperYay},
162+ {"minimal", false, AURHelperParu},
163+ {"multilib", true, AURHelperParu},
164+ {"with yay", true, AURHelperYay},
172165 }
173166
174167 for _, tt := range tests {
175168 t.Run(tt.name, func(t *testing.T) {
176- repo, err := NewRepository(tt.multilib, tt.chaotic, tt.helper)
169+ repo, err := NewRepository(tt.multilib, tt.helper)
177170
178171 if err != nil {
179172 t.Fatalf("expected no error, got %v", err)
@@ -183,10 +176,6 @@ func TestNewRepository_AllCombinations(t *testing.T) {
183176 t.Errorf("expected multilib=%v", tt.multilib)
184177 }
185178
186- if repo.EnableChaotic() != tt.chaotic {
187- t.Errorf("expected chaotic=%v", tt.chaotic)
188- }
189-
190179 if repo.AURHelper() != tt.helper {
191180 t.Errorf("expected helper %v", tt.helper)
192181 }
@@ -211,9 +200,9 @@ func TestAURHelper_String(t *testing.T) {
211200 }
212201
213202 func TestRepository_Equals(t *testing.T) {
214- r1, _ := NewRepository(true, true, AURHelperParu)
215- r2, _ := NewRepository(true, true, AURHelperParu)
216- r3, _ := NewRepository(true, true, AURHelperYay)
203+ r1, _ := NewRepository(true, AURHelperParu)
204+ r2, _ := NewRepository(true, AURHelperParu)
205+ r3, _ := NewRepository(true, AURHelperYay)
217206
218207 if !r1.Equals(r2) {
219208 t.Error("expected equal repositories")
@@ -138,7 +138,7 @@ func TestAvailableKernels(t *testing.T) {
138 // Repository Tests138 // Repository Tests
139 139
140 func TestNewRepository_Valid(t *testing.T) {140 func TestNewRepository_Valid(t *testing.T) {
141- repo, err := NewRepository(true, true, AURHelperParu)141+ repo, err := NewRepository(true, AURHelperParu)
142 142
143 if err != nil {143 if err != nil {
144 t.Fatalf("expected no error, got %v", err)144 t.Fatalf("expected no error, got %v", err)
@@ -148,10 +148,6 @@ func TestNewRepository_Valid(t *testing.T) {
148 t.Error("expected multilib enabled")148 t.Error("expected multilib enabled")
149 }149 }
150 150
151- if !repo.EnableChaotic() {
152- t.Error("expected chaotic enabled")
153- }
154-
155 if repo.AURHelper() != AURHelperParu {151 if repo.AURHelper() != AURHelperParu {
156 t.Error("expected paru AUR helper")152 t.Error("expected paru AUR helper")
157 }153 }
@@ -161,19 +157,16 @@ func TestNewRepository_AllCombinations(t *testing.T) {
161 tests := []struct {157 tests := []struct {
162 name string158 name string
163 multilib bool159 multilib bool
164- chaotic bool
165 helper AURHelper160 helper AURHelper
166 }{161 }{
167- {"minimal", false, false, AURHelperParu},162+ {"minimal", false, AURHelperParu},
168- {"chaotic only", false, true, AURHelperParu},163+ {"multilib", true, AURHelperParu},
169- {"multilib only", true, false, AURHelperParu},164+ {"with yay", true, AURHelperYay},
170- {"all enabled", true, true, AURHelperParu},
171- {"with yay", true, true, AURHelperYay},
172 }165 }
173 166
174 for _, tt := range tests {167 for _, tt := range tests {
175 t.Run(tt.name, func(t *testing.T) {168 t.Run(tt.name, func(t *testing.T) {
176- repo, err := NewRepository(tt.multilib, tt.chaotic, tt.helper)169+ repo, err := NewRepository(tt.multilib, tt.helper)
177 170
178 if err != nil {171 if err != nil {
179 t.Fatalf("expected no error, got %v", err)172 t.Fatalf("expected no error, got %v", err)
@@ -183,10 +176,6 @@ func TestNewRepository_AllCombinations(t *testing.T) {
183 t.Errorf("expected multilib=%v", tt.multilib)176 t.Errorf("expected multilib=%v", tt.multilib)
184 }177 }
185 178
186- if repo.EnableChaotic() != tt.chaotic {
187- t.Errorf("expected chaotic=%v", tt.chaotic)
188- }
189-
190 if repo.AURHelper() != tt.helper {179 if repo.AURHelper() != tt.helper {
191 t.Errorf("expected helper %v", tt.helper)180 t.Errorf("expected helper %v", tt.helper)
192 }181 }
@@ -211,9 +200,9 @@ func TestAURHelper_String(t *testing.T) {
211 }200 }
212 201
213 func TestRepository_Equals(t *testing.T) {202 func TestRepository_Equals(t *testing.T) {
214- r1, _ := NewRepository(true, true, AURHelperParu)203+ r1, _ := NewRepository(true, AURHelperParu)
215- r2, _ := NewRepository(true, true, AURHelperParu)204+ r2, _ := NewRepository(true, AURHelperParu)
216- r3, _ := NewRepository(true, true, AURHelperYay)205+ r3, _ := NewRepository(true, AURHelperYay)
217 206
218 if !r1.Equals(r2) {207 if !r1.Equals(r2) {
219 t.Error("expected equal repositories")208 t.Error("expected equal repositories")
M internal/domain/packages/repository.go +4 -11
@@ -23,22 +23,21 @@ func (a AURHelper) String() string {
2323 }
2424 }
2525
26-// Repository is an immutable value object for repository configuration
26+// Repository is an immutable value object for repository configuration.
27+// Chaotic-AUR is always enabled.
2728 type Repository struct {
2829 enableMultilib bool
29- enableChaotic bool
3030 aurHelper AURHelper
3131 }
3232
3333 // NewRepository creates a new Repository value object
34-func NewRepository(enableMultilib, enableChaotic bool, aurHelper AURHelper) (*Repository, error) {
34+func NewRepository(enableMultilib bool, aurHelper AURHelper) (*Repository, error) {
3535 if aurHelper < AURHelperParu || aurHelper > AURHelperYay {
3636 return nil, errors.New("invalid AUR helper")
3737 }
3838
3939 return &Repository{
4040 enableMultilib: enableMultilib,
41- enableChaotic: enableChaotic,
4241 aurHelper: aurHelper,
4342 }, nil
4443 }
@@ -48,11 +47,6 @@ func (r *Repository) EnableMultilib() bool {
4847 return r.enableMultilib
4948 }
5049
51-// EnableChaotic returns true if chaotic-aur is enabled
52-func (r *Repository) EnableChaotic() bool {
53- return r.enableChaotic
54-}
55-
5650 // AURHelper returns the AUR helper choice
5751 func (r *Repository) AURHelper() AURHelper {
5852 return r.aurHelper
@@ -61,7 +55,7 @@ func (r *Repository) AURHelper() AURHelper {
6155 // String returns human-readable representation
6256 func (r *Repository) String() string {
6357 return "Repository(multilib=" + boolToStr(r.enableMultilib) +
64- ", chaotic=" + boolToStr(r.enableChaotic) +
58+ ", chaotic=true" +
6559 ", aur=" + r.aurHelper.String() + ")"
6660 }
6761
@@ -71,7 +65,6 @@ func (r *Repository) Equals(other *Repository) bool {
7165 return false
7266 }
7367 return r.enableMultilib == other.enableMultilib &&
74- r.enableChaotic == other.enableChaotic &&
7568 r.aurHelper == other.aurHelper
7669 }
7770
@@ -23,22 +23,21 @@ func (a AURHelper) String() string {
23 }23 }
24 }24 }
25 25
26-// Repository is an immutable value object for repository configuration26+// Repository is an immutable value object for repository configuration.
27+// Chaotic-AUR is always enabled.
27 type Repository struct {28 type Repository struct {
28 enableMultilib bool29 enableMultilib bool
29- enableChaotic bool
30 aurHelper AURHelper30 aurHelper AURHelper
31 }31 }
32 32
33 // NewRepository creates a new Repository value object33 // NewRepository creates a new Repository value object
34-func NewRepository(enableMultilib, enableChaotic bool, aurHelper AURHelper) (*Repository, error) {34+func NewRepository(enableMultilib bool, aurHelper AURHelper) (*Repository, error) {
35 if aurHelper < AURHelperParu || aurHelper > AURHelperYay {35 if aurHelper < AURHelperParu || aurHelper > AURHelperYay {
36 return nil, errors.New("invalid AUR helper")36 return nil, errors.New("invalid AUR helper")
37 }37 }
38 38
39 return &Repository{39 return &Repository{
40 enableMultilib: enableMultilib,40 enableMultilib: enableMultilib,
41- enableChaotic: enableChaotic,
42 aurHelper: aurHelper,41 aurHelper: aurHelper,
43 }, nil42 }, nil
44 }43 }
@@ -48,11 +47,6 @@ func (r *Repository) EnableMultilib() bool {
48 return r.enableMultilib47 return r.enableMultilib
49 }48 }
50 49
51-// EnableChaotic returns true if chaotic-aur is enabled
52-func (r *Repository) EnableChaotic() bool {
53- return r.enableChaotic
54-}
55-
56 // AURHelper returns the AUR helper choice50 // AURHelper returns the AUR helper choice
57 func (r *Repository) AURHelper() AURHelper {51 func (r *Repository) AURHelper() AURHelper {
58 return r.aurHelper52 return r.aurHelper
@@ -61,7 +55,7 @@ func (r *Repository) AURHelper() AURHelper {
61 // String returns human-readable representation55 // String returns human-readable representation
62 func (r *Repository) String() string {56 func (r *Repository) String() string {
63 return "Repository(multilib=" + boolToStr(r.enableMultilib) +57 return "Repository(multilib=" + boolToStr(r.enableMultilib) +
64- ", chaotic=" + boolToStr(r.enableChaotic) +58+ ", chaotic=true" +
65 ", aur=" + r.aurHelper.String() + ")"59 ", aur=" + r.aurHelper.String() + ")"
66 }60 }
67 61
@@ -71,7 +65,6 @@ func (r *Repository) Equals(other *Repository) bool {
71 return false65 return false
72 }66 }
73 return r.enableMultilib == other.enableMultilib &&67 return r.enableMultilib == other.enableMultilib &&
74- r.enableChaotic == other.enableChaotic &&
75 r.aurHelper == other.aurHelper68 r.aurHelper == other.aurHelper
76 }69 }
77 70
M internal/interfaces/tui/app.go +0 -1
@@ -384,7 +384,6 @@ func (a *App) handleReposInput(msg tea.KeyMsg) (tea.Model, tea.Cmd) {
384384 return a, nil
385385 case "enter":
386386 a.formData.AURHelper = a.reposModel.SelectedAURHelper()
387- a.formData.EnableChaotic = a.reposModel.SelectedChaoticEnabled()
388387 a.currentScreen = ScreenDankLinux
389388 return a, nil
390389 }
@@ -384,7 +384,6 @@ func (a *App) handleReposInput(msg tea.KeyMsg) (tea.Model, tea.Cmd) {
384 return a, nil384 return a, nil
385 case "enter":385 case "enter":
386 a.formData.AURHelper = a.reposModel.SelectedAURHelper()386 a.formData.AURHelper = a.reposModel.SelectedAURHelper()
387- a.formData.EnableChaotic = a.reposModel.SelectedChaoticEnabled()
388 a.currentScreen = ScreenDankLinux387 a.currentScreen = ScreenDankLinux
389 return a, nil388 return a, nil
390 }389 }
M internal/interfaces/tui/handlers/install_handler.go +0 -1
@@ -198,7 +198,6 @@ func CreateInstallationCommand(app AppContext, formData models.FormData) tea.Cmd
198198 repoCmd := commands.SetupRepositoriesCommand{
199199 MountPoint: "/mnt",
200200 EnableMultilib: true,
201- EnableChaotic: formData.EnableChaotic,
202201 AURHelper: parseAURHelper(formData.AURHelper),
203202 KernelVariant: parseKernelVariant(formData.KernelVariant),
204203 }
@@ -198,7 +198,6 @@ func CreateInstallationCommand(app AppContext, formData models.FormData) tea.Cmd
198 repoCmd := commands.SetupRepositoriesCommand{198 repoCmd := commands.SetupRepositoriesCommand{
199 MountPoint: "/mnt",199 MountPoint: "/mnt",
200 EnableMultilib: true,200 EnableMultilib: true,
201- EnableChaotic: formData.EnableChaotic,
202 AURHelper: parseAURHelper(formData.AURHelper),201 AURHelper: parseAURHelper(formData.AURHelper),
203 KernelVariant: parseKernelVariant(formData.KernelVariant),202 KernelVariant: parseKernelVariant(formData.KernelVariant),
204 }203 }
M internal/interfaces/tui/models/form.go +0 -2
@@ -40,7 +40,6 @@ type FormData struct {
4040 AURHelper string
4141 Microcode bool
4242 InstallDankLinux bool
43- EnableChaotic bool
4443 }
4544
4645 // FormModelImpl implements FormModel interface
@@ -313,7 +312,6 @@ func (fm *FormModelImpl) extractData() {
313312 Locale: fm.fields[5].Value(),
314313 Keymap: fm.fields[6].Value(),
315314 InstallDankLinux: fm.data.InstallDankLinux,
316- EnableChaotic: fm.data.EnableChaotic,
317315 }
318316 }
319317
@@ -40,7 +40,6 @@ type FormData struct {
40 AURHelper string40 AURHelper string
41 Microcode bool41 Microcode bool
42 InstallDankLinux bool42 InstallDankLinux bool
43- EnableChaotic bool
44 }43 }
45 44
46 // FormModelImpl implements FormModel interface45 // FormModelImpl implements FormModel interface
@@ -313,7 +312,6 @@ func (fm *FormModelImpl) extractData() {
313 Locale: fm.fields[5].Value(),312 Locale: fm.fields[5].Value(),
314 Keymap: fm.fields[6].Value(),313 Keymap: fm.fields[6].Value(),
315 InstallDankLinux: fm.data.InstallDankLinux,314 InstallDankLinux: fm.data.InstallDankLinux,
316- EnableChaotic: fm.data.EnableChaotic,
317 }315 }
318 }316 }
319 317
M internal/interfaces/tui/models/repos.go +12 -91
@@ -8,23 +8,12 @@ type AURHelperOption struct {
88 Recommended bool
99 }
1010
11-// ChaoticOption represents a selectable Chaotic-AUR option.
12-type ChaoticOption struct {
13- Enabled bool
14- Label string
15- Description string
16- Recommended bool
17-}
18-
19-// ReposModelImpl holds AUR helper and Chaotic-AUR selection state.
20-// Navigation: ↑/↓ moves a single cursor across all items (crossing sections).
21-// ←/→ selects the item under the cursor (radio within its section).
11+// ReposModelImpl holds AUR helper selection state.
12+// Chaotic-AUR is always enabled — not user-configurable.
2213 type ReposModelImpl struct {
23- aurOptions []AURHelperOption
24- aurSelected int // which AUR item is selected (confirmed)
25- chaoticOptions []ChaoticOption
26- chaoticSelected int // which Chaotic item is selected (confirmed)
27- cursor int // flat index across all items (0..totalItems-1)
14+ aurOptions []AURHelperOption
15+ aurSelected int // which AUR item is selected (confirmed)
16+ cursor int // flat index across all items
2817 }
2918
3019 // NewReposModel creates a new repos selection model.
@@ -41,32 +30,13 @@ func NewReposModel() *ReposModelImpl {
4130 }
4231 }
4332
44- chaoticOptions := []ChaoticOption{
45- {Enabled: true, Label: "Enabled", Description: "Pre-built AUR packages; faster installs (recommended)", Recommended: true},
46- {Enabled: false, Label: "Disabled", Description: "Build AUR packages from source"},
47- }
48- chaoticSelected := 0
49- for i, o := range chaoticOptions {
50- if o.Recommended {
51- chaoticSelected = i
52- break
53- }
54- }
55-
5633 return &ReposModelImpl{
57- aurOptions: aurOptions,
58- aurSelected: aurSelected,
59- chaoticOptions: chaoticOptions,
60- chaoticSelected: chaoticSelected,
61- cursor: 0,
34+ aurOptions: aurOptions,
35+ aurSelected: aurSelected,
36+ cursor: 0,
6237 }
6338 }
6439
65-// totalItems returns the total number of items across all sections.
66-func (rm *ReposModelImpl) totalItems() int {
67- return len(rm.aurOptions) + len(rm.chaoticOptions)
68-}
69-
7040 // CursorIndex returns the current flat cursor position.
7141 func (rm *ReposModelImpl) CursorIndex() int { return rm.cursor }
7242
@@ -76,21 +46,6 @@ func (rm *ReposModelImpl) AUROptions() []AURHelperOption { return rm.aurOptions
7646 // AURSelectedIndex returns the confirmed AUR selection index.
7747 func (rm *ReposModelImpl) AURSelectedIndex() int { return rm.aurSelected }
7848
79-// ChaoticOptions returns the selectable Chaotic-AUR options.
80-func (rm *ReposModelImpl) ChaoticOptions() []ChaoticOption { return rm.chaoticOptions }
81-
82-// ChaoticSelectedIndex returns the confirmed Chaotic-AUR selection index.
83-func (rm *ReposModelImpl) ChaoticSelectedIndex() int { return rm.chaoticSelected }
84-
85-// FocusSection returns which section the cursor is in (0=AUR, 1=Chaotic).
86-// Kept for view compatibility.
87-func (rm *ReposModelImpl) FocusSection() int {
88- if rm.cursor < len(rm.aurOptions) {
89- return 0
90- }
91- return 1
92-}
93-
9449 // SelectedAURHelper returns the selected AUR helper value string.
9550 func (rm *ReposModelImpl) SelectedAURHelper() string {
9651 if len(rm.aurOptions) == 0 {
@@ -102,57 +57,23 @@ func (rm *ReposModelImpl) SelectedAURHelper() string {
10257 return rm.aurOptions[rm.aurSelected].Value
10358 }
10459
105-// SelectedChaoticEnabled returns whether Chaotic-AUR is enabled.
106-func (rm *ReposModelImpl) SelectedChaoticEnabled() bool {
107- if len(rm.chaoticOptions) == 0 {
108- return true
109- }
110- if rm.chaoticSelected < 0 || rm.chaoticSelected >= len(rm.chaoticOptions) {
111- return rm.chaoticOptions[0].Enabled
112- }
113- return rm.chaoticOptions[rm.chaoticSelected].Enabled
114-}
115-
116-// MoveUp moves the cursor up (crosses section boundaries, no wrap).
60+// MoveUp moves the cursor up.
11761 func (rm *ReposModelImpl) MoveUp() {
11862 if rm.cursor > 0 {
11963 rm.cursor--
12064 }
12165 }
12266
123-// MoveDown moves the cursor down (crosses section boundaries, no wrap).
67+// MoveDown moves the cursor down.
12468 func (rm *ReposModelImpl) MoveDown() {
125- if rm.cursor < rm.totalItems()-1 {
69+ if rm.cursor < len(rm.aurOptions)-1 {
12670 rm.cursor++
12771 }
12872 }
12973
130-// Select selects (radio) the item currently under the cursor within its section.
74+// Select confirms the item currently under the cursor.
13175 func (rm *ReposModelImpl) Select() {
13276 if rm.cursor < len(rm.aurOptions) {
13377 rm.aurSelected = rm.cursor
134- } else {
135- rm.chaoticSelected = rm.cursor - len(rm.aurOptions)
13678 }
13779 }
138-
139-// NextSection moves focus to the next section (kept for compatibility).
140-func (rm *ReposModelImpl) NextSection() {
141- rm.focusToSection(rm.FocusSection() + 1)
142-}
143-
144-// PrevSection moves focus to the previous section (kept for compatibility).
145-func (rm *ReposModelImpl) PrevSection() {
146- rm.focusToSection(rm.FocusSection() - 1)
147-}
148-
149-func (rm *ReposModelImpl) focusToSection(s int) {
150- if s <= 0 {
151- rm.cursor = 0
152- } else {
153- rm.cursor = len(rm.aurOptions)
154- }
155-}
156-
157-// SectionCount returns the total number of sections.
158-func (rm *ReposModelImpl) SectionCount() int { return 2 }
@@ -8,23 +8,12 @@ type AURHelperOption struct {
8 Recommended bool8 Recommended bool
9 }9 }
10 10
11-// ChaoticOption represents a selectable Chaotic-AUR option.11+// ReposModelImpl holds AUR helper selection state.
12-type ChaoticOption struct {12+// Chaotic-AUR is always enabled — not user-configurable.
13- Enabled bool
14- Label string
15- Description string
16- Recommended bool
17-}
18-
19-// ReposModelImpl holds AUR helper and Chaotic-AUR selection state.
20-// Navigation: ↑/↓ moves a single cursor across all items (crossing sections).
21-// ←/→ selects the item under the cursor (radio within its section).
22 type ReposModelImpl struct {13 type ReposModelImpl struct {
23- aurOptions []AURHelperOption14+ aurOptions []AURHelperOption
24- aurSelected int // which AUR item is selected (confirmed)15+ aurSelected int // which AUR item is selected (confirmed)
25- chaoticOptions []ChaoticOption16+ cursor int // flat index across all items
26- chaoticSelected int // which Chaotic item is selected (confirmed)
27- cursor int // flat index across all items (0..totalItems-1)
28 }17 }
29 18
30 // NewReposModel creates a new repos selection model.19 // NewReposModel creates a new repos selection model.
@@ -41,32 +30,13 @@ func NewReposModel() *ReposModelImpl {
41 }30 }
42 }31 }
43 32
44- chaoticOptions := []ChaoticOption{
45- {Enabled: true, Label: "Enabled", Description: "Pre-built AUR packages; faster installs (recommended)", Recommended: true},
46- {Enabled: false, Label: "Disabled", Description: "Build AUR packages from source"},
47- }
48- chaoticSelected := 0
49- for i, o := range chaoticOptions {
50- if o.Recommended {
51- chaoticSelected = i
52- break
53- }
54- }
55-
56 return &ReposModelImpl{33 return &ReposModelImpl{
57- aurOptions: aurOptions,34+ aurOptions: aurOptions,
58- aurSelected: aurSelected,35+ aurSelected: aurSelected,
59- chaoticOptions: chaoticOptions,36+ cursor: 0,
60- chaoticSelected: chaoticSelected,
61- cursor: 0,
62 }37 }
63 }38 }
64 39
65-// totalItems returns the total number of items across all sections.
66-func (rm *ReposModelImpl) totalItems() int {
67- return len(rm.aurOptions) + len(rm.chaoticOptions)
68-}
69-
70 // CursorIndex returns the current flat cursor position.40 // CursorIndex returns the current flat cursor position.
71 func (rm *ReposModelImpl) CursorIndex() int { return rm.cursor }41 func (rm *ReposModelImpl) CursorIndex() int { return rm.cursor }
72 42
@@ -76,21 +46,6 @@ func (rm *ReposModelImpl) AUROptions() []AURHelperOption { return rm.aurOptions
76 // AURSelectedIndex returns the confirmed AUR selection index.46 // AURSelectedIndex returns the confirmed AUR selection index.
77 func (rm *ReposModelImpl) AURSelectedIndex() int { return rm.aurSelected }47 func (rm *ReposModelImpl) AURSelectedIndex() int { return rm.aurSelected }
78 48
79-// ChaoticOptions returns the selectable Chaotic-AUR options.
80-func (rm *ReposModelImpl) ChaoticOptions() []ChaoticOption { return rm.chaoticOptions }
81-
82-// ChaoticSelectedIndex returns the confirmed Chaotic-AUR selection index.
83-func (rm *ReposModelImpl) ChaoticSelectedIndex() int { return rm.chaoticSelected }
84-
85-// FocusSection returns which section the cursor is in (0=AUR, 1=Chaotic).
86-// Kept for view compatibility.
87-func (rm *ReposModelImpl) FocusSection() int {
88- if rm.cursor < len(rm.aurOptions) {
89- return 0
90- }
91- return 1
92-}
93-
94 // SelectedAURHelper returns the selected AUR helper value string.49 // SelectedAURHelper returns the selected AUR helper value string.
95 func (rm *ReposModelImpl) SelectedAURHelper() string {50 func (rm *ReposModelImpl) SelectedAURHelper() string {
96 if len(rm.aurOptions) == 0 {51 if len(rm.aurOptions) == 0 {
@@ -102,57 +57,23 @@ func (rm *ReposModelImpl) SelectedAURHelper() string {
102 return rm.aurOptions[rm.aurSelected].Value57 return rm.aurOptions[rm.aurSelected].Value
103 }58 }
104 59
105-// SelectedChaoticEnabled returns whether Chaotic-AUR is enabled.60+// MoveUp moves the cursor up.
106-func (rm *ReposModelImpl) SelectedChaoticEnabled() bool {
107- if len(rm.chaoticOptions) == 0 {
108- return true
109- }
110- if rm.chaoticSelected < 0 || rm.chaoticSelected >= len(rm.chaoticOptions) {
111- return rm.chaoticOptions[0].Enabled
112- }
113- return rm.chaoticOptions[rm.chaoticSelected].Enabled
114-}
115-
116-// MoveUp moves the cursor up (crosses section boundaries, no wrap).
117 func (rm *ReposModelImpl) MoveUp() {61 func (rm *ReposModelImpl) MoveUp() {
118 if rm.cursor > 0 {62 if rm.cursor > 0 {
119 rm.cursor--63 rm.cursor--
120 }64 }
121 }65 }
122 66
123-// MoveDown moves the cursor down (crosses section boundaries, no wrap).67+// MoveDown moves the cursor down.
124 func (rm *ReposModelImpl) MoveDown() {68 func (rm *ReposModelImpl) MoveDown() {
125- if rm.cursor < rm.totalItems()-1 {69+ if rm.cursor < len(rm.aurOptions)-1 {
126 rm.cursor++70 rm.cursor++
127 }71 }
128 }72 }
129 73
130-// Select selects (radio) the item currently under the cursor within its section.74+// Select confirms the item currently under the cursor.
131 func (rm *ReposModelImpl) Select() {75 func (rm *ReposModelImpl) Select() {
132 if rm.cursor < len(rm.aurOptions) {76 if rm.cursor < len(rm.aurOptions) {
133 rm.aurSelected = rm.cursor77 rm.aurSelected = rm.cursor
134- } else {
135- rm.chaoticSelected = rm.cursor - len(rm.aurOptions)
136 }78 }
137 }79 }
138-
139-// NextSection moves focus to the next section (kept for compatibility).
140-func (rm *ReposModelImpl) NextSection() {
141- rm.focusToSection(rm.FocusSection() + 1)
142-}
143-
144-// PrevSection moves focus to the previous section (kept for compatibility).
145-func (rm *ReposModelImpl) PrevSection() {
146- rm.focusToSection(rm.FocusSection() - 1)
147-}
148-
149-func (rm *ReposModelImpl) focusToSection(s int) {
150- if s <= 0 {
151- rm.cursor = 0
152- } else {
153- rm.cursor = len(rm.aurOptions)
154- }
155-}
156-
157-// SectionCount returns the total number of sections.
158-func (rm *ReposModelImpl) SectionCount() int { return 2 }
M internal/interfaces/tui/views/repos.go +5 -38
@@ -7,7 +7,8 @@ import (
77 "github.com/charmbracelet/lipgloss"
88 )
99
10-// RenderReposSelection renders the AUR helper and Chaotic-AUR selection screen.
10+// RenderReposSelection renders the AUR helper selection screen.
11+// Chaotic-AUR is always enabled and not shown as an option.
1112 func RenderReposSelection(rm *models.ReposModelImpl) string {
1213 var b strings.Builder
1314
@@ -16,7 +17,6 @@ func RenderReposSelection(rm *models.ReposModelImpl) string {
1617 section := lipgloss.NewStyle().Bold(true).Foreground(lipgloss.Color("14"))
1718 desc := lipgloss.NewStyle().Foreground(lipgloss.Color("8")).Faint(true)
1819
19- // cursor = highlighted row; selected = confirmed choice (shown with [x])
2020 cursorStyle := lipgloss.NewStyle().Foreground(lipgloss.Color("10")).Bold(true)
2121 selectedMark := lipgloss.NewStyle().Foreground(lipgloss.Color("10")).Bold(true)
2222 normalMark := lipgloss.NewStyle().Foreground(lipgloss.Color("8"))
@@ -25,54 +25,19 @@ func RenderReposSelection(rm *models.ReposModelImpl) string {
2525 b.WriteString(title.Render("Repository Configuration"))
2626 b.WriteString("\n\n")
2727
28- // AUR Helper section
2928 b.WriteString(section.Render("AUR Helper"))
3029 b.WriteString("\n")
3130
3231 for i, option := range rm.AUROptions() {
33- flatIdx := i
34- isCursor := rm.CursorIndex() == flatIdx
32+ isCursor := rm.CursorIndex() == i
3533 isSelected := rm.AURSelectedIndex() == i
3634
37- // Selection marker
3835 if isSelected {
3936 b.WriteString(selectedMark.Render(" [x] "))
4037 } else {
4138 b.WriteString(normalMark.Render(" [ ] "))
4239 }
4340
44- // Label
45- label := option.Label
46- if isCursor {
47- b.WriteString(cursorStyle.Render("> " + label))
48- } else {
49- b.WriteString(label)
50- }
51- b.WriteString("\n")
52- b.WriteString(desc.Render(" " + option.Description))
53- b.WriteString("\n")
54- }
55-
56- b.WriteString("\n")
57-
58- // Chaotic-AUR section
59- b.WriteString(section.Render("Chaotic-AUR"))
60- b.WriteString("\n")
61-
62- aurLen := len(rm.AUROptions())
63- for i, option := range rm.ChaoticOptions() {
64- flatIdx := aurLen + i
65- isCursor := rm.CursorIndex() == flatIdx
66- isSelected := rm.ChaoticSelectedIndex() == i
67-
68- // Selection marker
69- if isSelected {
70- b.WriteString(selectedMark.Render(" [x] "))
71- } else {
72- b.WriteString(normalMark.Render(" [ ] "))
73- }
74-
75- // Label
7641 label := option.Label
7742 if isCursor {
7843 b.WriteString(cursorStyle.Render("> " + label))
@@ -85,6 +50,8 @@ func RenderReposSelection(rm *models.ReposModelImpl) string {
8550 }
8651
8752 b.WriteString("\n")
53+ b.WriteString(desc.Render(" Chaotic-AUR is always enabled (pre-built AUR packages)"))
54+ b.WriteString("\n\n")
8855 b.WriteString(info.Render("↑/↓ move • ←/→ or space select • enter confirm • esc back"))
8956
9057 return b.String()
@@ -7,7 +7,8 @@ import (
7 "github.com/charmbracelet/lipgloss"7 "github.com/charmbracelet/lipgloss"
8 )8 )
9 9
10-// RenderReposSelection renders the AUR helper and Chaotic-AUR selection screen.10+// RenderReposSelection renders the AUR helper selection screen.
11+// Chaotic-AUR is always enabled and not shown as an option.
11 func RenderReposSelection(rm *models.ReposModelImpl) string {12 func RenderReposSelection(rm *models.ReposModelImpl) string {
12 var b strings.Builder13 var b strings.Builder
13 14
@@ -16,7 +17,6 @@ func RenderReposSelection(rm *models.ReposModelImpl) string {
16 section := lipgloss.NewStyle().Bold(true).Foreground(lipgloss.Color("14"))17 section := lipgloss.NewStyle().Bold(true).Foreground(lipgloss.Color("14"))
17 desc := lipgloss.NewStyle().Foreground(lipgloss.Color("8")).Faint(true)18 desc := lipgloss.NewStyle().Foreground(lipgloss.Color("8")).Faint(true)
18 19
19- // cursor = highlighted row; selected = confirmed choice (shown with [x])
20 cursorStyle := lipgloss.NewStyle().Foreground(lipgloss.Color("10")).Bold(true)20 cursorStyle := lipgloss.NewStyle().Foreground(lipgloss.Color("10")).Bold(true)
21 selectedMark := lipgloss.NewStyle().Foreground(lipgloss.Color("10")).Bold(true)21 selectedMark := lipgloss.NewStyle().Foreground(lipgloss.Color("10")).Bold(true)
22 normalMark := lipgloss.NewStyle().Foreground(lipgloss.Color("8"))22 normalMark := lipgloss.NewStyle().Foreground(lipgloss.Color("8"))
@@ -25,54 +25,19 @@ func RenderReposSelection(rm *models.ReposModelImpl) string {
25 b.WriteString(title.Render("Repository Configuration"))25 b.WriteString(title.Render("Repository Configuration"))
26 b.WriteString("\n\n")26 b.WriteString("\n\n")
27 27
28- // AUR Helper section
29 b.WriteString(section.Render("AUR Helper"))28 b.WriteString(section.Render("AUR Helper"))
30 b.WriteString("\n")29 b.WriteString("\n")
31 30
32 for i, option := range rm.AUROptions() {31 for i, option := range rm.AUROptions() {
33- flatIdx := i32+ isCursor := rm.CursorIndex() == i
34- isCursor := rm.CursorIndex() == flatIdx
35 isSelected := rm.AURSelectedIndex() == i33 isSelected := rm.AURSelectedIndex() == i
36 34
37- // Selection marker
38 if isSelected {35 if isSelected {
39 b.WriteString(selectedMark.Render(" [x] "))36 b.WriteString(selectedMark.Render(" [x] "))
40 } else {37 } else {
41 b.WriteString(normalMark.Render(" [ ] "))38 b.WriteString(normalMark.Render(" [ ] "))
42 }39 }
43 40
44- // Label
45- label := option.Label
46- if isCursor {
47- b.WriteString(cursorStyle.Render("> " + label))
48- } else {
49- b.WriteString(label)
50- }
51- b.WriteString("\n")
52- b.WriteString(desc.Render(" " + option.Description))
53- b.WriteString("\n")
54- }
55-
56- b.WriteString("\n")
57-
58- // Chaotic-AUR section
59- b.WriteString(section.Render("Chaotic-AUR"))
60- b.WriteString("\n")
61-
62- aurLen := len(rm.AUROptions())
63- for i, option := range rm.ChaoticOptions() {
64- flatIdx := aurLen + i
65- isCursor := rm.CursorIndex() == flatIdx
66- isSelected := rm.ChaoticSelectedIndex() == i
67-
68- // Selection marker
69- if isSelected {
70- b.WriteString(selectedMark.Render(" [x] "))
71- } else {
72- b.WriteString(normalMark.Render(" [ ] "))
73- }
74-
75- // Label
76 label := option.Label41 label := option.Label
77 if isCursor {42 if isCursor {
78 b.WriteString(cursorStyle.Render("> " + label))43 b.WriteString(cursorStyle.Render("> " + label))
@@ -85,6 +50,8 @@ func RenderReposSelection(rm *models.ReposModelImpl) string {
85 }50 }
86 51
87 b.WriteString("\n")52 b.WriteString("\n")
53+ b.WriteString(desc.Render(" Chaotic-AUR is always enabled (pre-built AUR packages)"))
54+ b.WriteString("\n\n")
88 b.WriteString(info.Render("↑/↓ move • ←/→ or space select • enter confirm • esc back"))55 b.WriteString(info.Render("↑/↓ move • ←/→ or space select • enter confirm • esc back"))
89 56
90 return b.String()57 return b.String()

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