|
| @@ -13,6 +12,37 @@ import ( |
| 13 | | "github.com/bnema/archup/internal/system" | 12 | | "github.com/bnema/archup/internal/system" |
| 14 | | ) | 13 | | ) |
| 15 | | | 14 | | |
| | | 15 | + | // Pacman configuration constants |
| | | 16 | + | const ( |
| | | 17 | + | pacmanMultilibSectionDisabled = "#[multilib]" |
| | | 18 | + | pacmanMultilibSectionEnabled = "[multilib]" |
| | | 19 | + | pacmanMultilibIncludeDisabled = "#Include = /etc/pacman.d/mirrorlist" |
| | | 20 | + | pacmanMultilibIncludeEnabled = "Include = /etc/pacman.d/mirrorlist" |
| | | 21 | + | pacmanSectionPrefix = "[" |
| | | 22 | + | ) |
| | | 23 | + | |
| | | 24 | + | // AUR helper constants and validation |
| | | 25 | + | const ( |
| | | 26 | + | AURHelperParu = "paru" |
| | | 27 | + | AURHelperYay = "yay" |
| | | 28 | + | ) |
| | | 29 | + | |
| | | 30 | + | // ValidAURHelpers contains the list of supported AUR helpers |
| | | 31 | + | var ValidAURHelpers = []string{AURHelperParu, AURHelperYay} |
| | | 32 | + | |
| | | 33 | + | // IsValidAURHelper validates if the given AUR helper is supported |
| | | 34 | + | func IsValidAURHelper(helper string) bool { |
| | | 35 | + | if helper == "" { |
| | | 36 | + | return true // Empty is valid (no AUR helper) |
| | | 37 | + | } |
| | | 38 | + | for _, valid := range ValidAURHelpers { |
| | | 39 | + | if helper == valid { |
| | | 40 | + | return true |
| | | 41 | + | } |
| | | 42 | + | } |
| | | 43 | + | return false |
| | | 44 | + | } |
| | | 45 | + | |
| 16 | | // ReposPhase handles repository configuration | 46 | | // ReposPhase handles repository configuration |
| 17 | | type ReposPhase struct { | 47 | | type ReposPhase struct { |
| 18 | | *BasePhase | 48 | | *BasePhase |
|
| @@ -29,6 +59,12 @@ func NewReposPhase(cfg *config.Config, log *logger.Logger) *ReposPhase { |
| 29 | | | 59 | | |
| 30 | | // PreCheck validates repos prerequisites | 60 | | // PreCheck validates repos prerequisites |
| 31 | | func (p *ReposPhase) PreCheck() error { | 61 | | func (p *ReposPhase) PreCheck() error { |
| | | 62 | + | // Validate AUR helper first (config validation before system checks) |
| | | 63 | + | switch { |
| | | 64 | + | case p.config.AURHelper != "" && !IsValidAURHelper(p.config.AURHelper): |
| | | 65 | + | return fmt.Errorf("invalid AUR helper: %s (must be one of: %v)", p.config.AURHelper, ValidAURHelpers) |
| | | 66 | + | } |
| | | 67 | + | |
| 32 | | // Verify /mnt is mounted | 68 | | // Verify /mnt is mounted |
| 33 | | result := system.RunSimple("mountpoint", "-q", config.PathMnt) | 69 | | result := system.RunSimple("mountpoint", "-q", config.PathMnt) |
| 34 | | switch { | 70 | | switch { |
|
| @@ -47,7 +83,7 @@ func (p *ReposPhase) PreCheck() error { |
| 47 | | | 83 | | |
| 48 | | // Execute runs the repos phase | 84 | | // Execute runs the repos phase |
| 49 | | func (p *ReposPhase) Execute(progressChan chan<- ProgressUpdate) PhaseResult { | 85 | | func (p *ReposPhase) Execute(progressChan chan<- ProgressUpdate) PhaseResult { |
| 50 | - | p.SendProgress(progressChan, "Starting repository configuration...", 1, 4) | 86 | + | p.SendProgress(progressChan, "Starting repository configuration...", 1, 5) |
| 51 | | | 87 | | |
| 52 | | // Step 1: Enable multilib (optional) | 88 | | // Step 1: Enable multilib (optional) |
| 53 | | switch { | 89 | | switch { |
|
| @@ -61,9 +97,18 @@ func (p *ReposPhase) Execute(progressChan chan<- ProgressUpdate) PhaseResult { |
| 61 | | p.SendOutput(progressChan, "[SKIP] Multilib disabled") | 97 | | p.SendOutput(progressChan, "[SKIP] Multilib disabled") |
| 62 | | } | 98 | | } |
| 63 | | | 99 | | |
| 64 | - | p.SendProgress(progressChan, "Installing extra packages...", 2, 4) | 100 | + | // Step 2: Sync package databases after enabling multilib |
| | | 101 | + | p.SendProgress(progressChan, "Syncing package databases...", 2, 5) |
| | | 102 | + | switch err := p.syncDatabases(progressChan); { |
| | | 103 | + | case err != nil: |
| | | 104 | + | p.logger.Warn("Database sync failed", "error", err) |
| | | 105 | + | p.SendOutput(progressChan, fmt.Sprintf("[WARN] Database sync failed: %v", err)) |
| | | 106 | + | // Don't fail the phase, just warn |
| | | 107 | + | } |
| 65 | | | 108 | | |
| 66 | - | // Step 2: Install extra packages (always, from official repos) | 109 | + | p.SendProgress(progressChan, "Installing extra packages...", 3, 5) |
| | | 110 | + | |
| | | 111 | + | // Step 3: Install extra packages (always, from official repos) |
| 67 | | switch err := p.installExtraPackages(progressChan); { | 112 | | switch err := p.installExtraPackages(progressChan); { |
| 68 | | case err != nil: | 113 | | case err != nil: |
| 69 | | // Don't fail the phase, just warn | 114 | | // Don't fail the phase, just warn |
|
| @@ -71,9 +116,9 @@ func (p *ReposPhase) Execute(progressChan chan<- ProgressUpdate) PhaseResult { |
| 71 | | p.SendOutput(progressChan, fmt.Sprintf("[WARN] Some extra packages failed: %v", err)) | 116 | | p.SendOutput(progressChan, fmt.Sprintf("[WARN] Some extra packages failed: %v", err)) |
| 72 | | } | 117 | | } |
| 73 | | | 118 | | |
| 74 | - | p.SendProgress(progressChan, "Configuring Chaotic-AUR...", 3, 4) | 119 | + | p.SendProgress(progressChan, "Configuring Chaotic-AUR...", 4, 5) |
| 75 | | | 120 | | |
| 76 | - | // Step 3: Enable Chaotic-AUR (optional) | 121 | + | // Step 4: Enable Chaotic-AUR (optional) |
| 77 | | switch { | 122 | | switch { |
| 78 | | case p.config.EnableChaotic: | 123 | | case p.config.EnableChaotic: |
| 79 | | switch err := p.enableChaoticAUR(progressChan); { | 124 | | switch err := p.enableChaoticAUR(progressChan); { |
|
| @@ -85,9 +130,9 @@ func (p *ReposPhase) Execute(progressChan chan<- ProgressUpdate) PhaseResult { |
| 85 | | p.SendOutput(progressChan, "[SKIP] Chaotic-AUR disabled") | 130 | | p.SendOutput(progressChan, "[SKIP] Chaotic-AUR disabled") |
| 86 | | } | 131 | | } |
| 87 | | | 132 | | |
| 88 | - | p.SendProgress(progressChan, "Installing AUR helper...", 4, 4) | 133 | + | p.SendProgress(progressChan, "Installing AUR helper...", 5, 5) |
| 89 | | | 134 | | |
| 90 | - | // Step 4: Install AUR helper (optional) | 135 | + | // Step 5: Install AUR helper (optional) |
| 91 | | switch { | 136 | | switch { |
| 92 | | case p.config.AURHelper != "": | 137 | | case p.config.AURHelper != "": |
| 93 | | switch err := p.installAURHelper(progressChan); { | 138 | | switch err := p.installAURHelper(progressChan); { |
|
| @@ -99,12 +144,25 @@ func (p *ReposPhase) Execute(progressChan chan<- ProgressUpdate) PhaseResult { |
| 99 | | p.SendOutput(progressChan, "[SKIP] AUR helper disabled") | 144 | | p.SendOutput(progressChan, "[SKIP] AUR helper disabled") |
| 100 | | } | 145 | | } |
| 101 | | | 146 | | |
| 102 | - | p.SendProgress(progressChan, "Repository configuration complete", 4, 4) | 147 | + | p.SendProgress(progressChan, "Repository configuration complete", 5, 5) |
| 103 | | p.SendComplete(progressChan, "Repositories configured successfully") | 148 | | p.SendComplete(progressChan, "Repositories configured successfully") |
| 104 | | | 149 | | |
| 105 | | return PhaseResult{Success: true, Message: "Repos configuration complete"} | 150 | | return PhaseResult{Success: true, Message: "Repos configuration complete"} |
| 106 | | } | 151 | | } |
| 107 | | | 152 | | |
| | | 153 | + | // syncDatabases syncs pacman package databases |
| | | 154 | + | func (p *ReposPhase) syncDatabases(progressChan chan<- ProgressUpdate) error { |
| | | 155 | + | p.SendOutput(progressChan, "Syncing package databases...") |
| | | 156 | + | |
| | | 157 | + | switch err := system.ChrootExec(p.logger.LogPath(), config.PathMnt, "pacman -Sy --noconfirm"); { |
| | | 158 | + | case err != nil: |
| | | 159 | + | return fmt.Errorf("failed to sync databases: %w", err) |
| | | 160 | + | } |
| | | 161 | + | |
| | | 162 | + | p.SendOutput(progressChan, "[OK] Package databases synced") |
| | | 163 | + | return nil |
| | | 164 | + | } |
| | | 165 | + | |
| 108 | | // enableMultilib enables multilib repository | 166 | | // enableMultilib enables multilib repository |
| 109 | | func (p *ReposPhase) enableMultilib(progressChan chan<- ProgressUpdate) error { | 167 | | func (p *ReposPhase) enableMultilib(progressChan chan<- ProgressUpdate) error { |
| 110 | | p.SendOutput(progressChan, "Enabling multilib repository...") | 168 | | p.SendOutput(progressChan, "Enabling multilib repository...") |
|
| @@ -118,19 +176,40 @@ func (p *ReposPhase) enableMultilib(progressChan chan<- ProgressUpdate) error { |
| 118 | | | 176 | | |
| 119 | | contentStr := string(content) | 177 | | contentStr := string(content) |
| 120 | | | 178 | | |
| 121 | - | // Check if already enabled | 179 | + | // Check if already fully enabled (both section and Include line uncommented) |
| | | 180 | + | hasMultilibSection := strings.Contains(contentStr, "[multilib]") && !strings.Contains(contentStr, "#[multilib]") |
| | | 181 | + | hasMultilibInclude := strings.Contains(contentStr, pacmanMultilibIncludeEnabled) |
| | | 182 | + | |
| 122 | | switch { | 183 | | switch { |
| 123 | - | case strings.Contains(contentStr, "[multilib]") && !strings.Contains(contentStr, "#[multilib]"): | 184 | + | case hasMultilibSection && hasMultilibInclude: |
| 124 | | p.SendOutput(progressChan, "[OK] Multilib already enabled") | 185 | | p.SendOutput(progressChan, "[OK] Multilib already enabled") |
| 125 | | return nil | 186 | | return nil |
| 126 | | } | 187 | | } |
| 127 | | | 188 | | |
| 128 | - | // Uncomment multilib section | 189 | + | // Uncomment multilib section and its Include line in context-aware manner |
| 129 | - | multilibRegex := regexp.MustCompile(`(?m)^#\[multilib\]`) | 190 | + | // This ensures we only uncomment the Include that's right after [multilib] |
| 130 | - | contentStr = multilibRegex.ReplaceAllString(contentStr, "[multilib]") | 191 | + | lines := strings.Split(contentStr, "\n") |
| | | 192 | + | inMultilib := false |
| | | 193 | + | for i, line := range lines { |
| | | 194 | + | trimmedLine := strings.TrimSpace(line) |
| 131 | | | 195 | | |
| 132 | - | includeRegex := regexp.MustCompile(`(?m)^#Include = /etc/pacman.d/mirrorlist`) | 196 | + | switch { |
| 133 | - | contentStr = includeRegex.ReplaceAllString(contentStr, "Include = /etc/pacman.d/mirrorlist") | 197 | + | case trimmedLine == pacmanMultilibSectionDisabled: |
| | | 198 | + | lines[i] = pacmanMultilibSectionEnabled |
| | | 199 | + | inMultilib = true |
| | | 200 | + | case trimmedLine == pacmanMultilibSectionEnabled: |
| | | 201 | + | // Already enabled, track that we're in multilib section |
| | | 202 | + | inMultilib = true |
| | | 203 | + | case inMultilib && trimmedLine == pacmanMultilibIncludeDisabled: |
| | | 204 | + | // Uncomment the Include line, preserving indentation |
| | | 205 | + | lines[i] = strings.Replace(line, "#Include", "Include", 1) |
| | | 206 | + | inMultilib = false // Only uncomment the first Include after [multilib] |
| | | 207 | + | case inMultilib && strings.HasPrefix(trimmedLine, pacmanSectionPrefix) && trimmedLine != pacmanMultilibSectionEnabled: |
| | | 208 | + | // Exited multilib section without finding Include |
| | | 209 | + | inMultilib = false |
| | | 210 | + | } |
| | | 211 | + | } |
| | | 212 | + | contentStr = strings.Join(lines, "\n") |
| 134 | | | 213 | | |
| 135 | | // Write updated config | 214 | | // Write updated config |
| 136 | | switch err := os.WriteFile(config.PathMntEtcPacmanConf, []byte(contentStr), 0644); { | 215 | | switch err := os.WriteFile(config.PathMntEtcPacmanConf, []byte(contentStr), 0644); { |
|
| @@ -201,26 +280,31 @@ func (p *ReposPhase) enableChaoticAUR(progressChan chan<- ProgressUpdate) error |
| 201 | | | 280 | | |
| 202 | | // Import GPG key | 281 | | // Import GPG key |
| 203 | | recvKeyCmd := fmt.Sprintf("pacman-key --recv-key %s --keyserver %s", keyID, keyserver) | 282 | | recvKeyCmd := fmt.Sprintf("pacman-key --recv-key %s --keyserver %s", keyID, keyserver) |
| 204 | - | switch err := system.ChrootExec(p.logger.LogPath(),config.PathMnt, recvKeyCmd); { | 283 | + | switch err := system.ChrootExec(p.logger.LogPath(), config.PathMnt, recvKeyCmd); { |
| 205 | | case err != nil: | 284 | | case err != nil: |
| 206 | - | return fmt.Errorf("failed to receive Chaotic GPG key: %w", err) | 285 | + | p.logger.Warn("Failed to receive Chaotic GPG key, skipping Chaotic-AUR", "error", err) |
| | | 286 | + | p.SendOutput(progressChan, "[WARN] Chaotic-AUR unavailable (GPG key fetch failed)") |
| | | 287 | + | return nil // Don't fail, just skip |
| 207 | | } | 288 | | } |
| 208 | | | 289 | | |
| 209 | | // Locally sign the key | 290 | | // Locally sign the key |
| 210 | | lsignKeyCmd := fmt.Sprintf("pacman-key --lsign-key %s", keyID) | 291 | | lsignKeyCmd := fmt.Sprintf("pacman-key --lsign-key %s", keyID) |
| 211 | - | switch err := system.ChrootExec(p.logger.LogPath(),config.PathMnt, lsignKeyCmd); { | 292 | + | switch err := system.ChrootExec(p.logger.LogPath(), config.PathMnt, lsignKeyCmd); { |
| 212 | | case err != nil: | 293 | | case err != nil: |
| 213 | - | return fmt.Errorf("failed to sign Chaotic GPG key: %w", err) | 294 | + | p.logger.Warn("Failed to sign Chaotic GPG key, skipping Chaotic-AUR", "error", err) |
| | | 295 | + | p.SendOutput(progressChan, "[WARN] Chaotic-AUR unavailable (GPG key signing failed)") |
| | | 296 | + | return nil // Don't fail, just skip |
| 214 | | } | 297 | | } |
| 215 | | | 298 | | |
| 216 | | p.SendOutput(progressChan, "[OK] Chaotic GPG key imported") | 299 | | p.SendOutput(progressChan, "[OK] Chaotic GPG key imported") |
| 217 | | | 300 | | |
| 218 | | // Download and install chaotic-keyring and chaotic-mirrorlist | 301 | | // Download and install chaotic-keyring and chaotic-mirrorlist |
| 219 | | p.SendOutput(progressChan, "Downloading Chaotic-AUR packages...") | 302 | | p.SendOutput(progressChan, "Downloading Chaotic-AUR packages...") |
| 220 | - | if err := system.DownloadAndInstallPackages(p.logger.LogPath(), config.PathMnt, keyringURL, mirrorlistURL); err != nil { | 303 | + | switch err := system.DownloadAndInstallPackages(p.logger.LogPath(), config.PathMnt, keyringURL, mirrorlistURL); { |
| | | 304 | + | case err != nil: |
| 221 | | p.logger.Warn("Chaotic-AUR packages unavailable, skipping", "error", err) | 305 | | p.logger.Warn("Chaotic-AUR packages unavailable, skipping", "error", err) |
| 222 | | p.SendOutput(progressChan, "[WARN] Chaotic-AUR unavailable, skipping (AUR helper will be built from source)") | 306 | | p.SendOutput(progressChan, "[WARN] Chaotic-AUR unavailable, skipping (AUR helper will be built from source)") |
| 223 | - | return nil | 307 | + | return nil // IMPORTANT: Exit early, don't add to pacman.conf |
| 224 | | } | 308 | | } |
| 225 | | | 309 | | |
| 226 | | p.SendOutput(progressChan, "[OK] Chaotic packages installed") | 310 | | p.SendOutput(progressChan, "[OK] Chaotic packages installed") |
|
| @@ -271,6 +355,25 @@ func (p *ReposPhase) enableChaoticAUR(progressChan chan<- ProgressUpdate) error |
| 271 | | return nil | 355 | | return nil |
| 272 | | } | 356 | | } |
| 273 | | | 357 | | |
| | | 358 | + | // installPackagesIndividually tries to install packages one by one for resilience |
| | | 359 | + | func (p *ReposPhase) installPackagesIndividually(packages []string, progressChan chan<- ProgressUpdate) (int, []string) { |
| | | 360 | + | failedPkgs := []string{} |
| | | 361 | + | successCount := 0 |
| | | 362 | + | |
| | | 363 | + | for _, pkg := range packages { |
| | | 364 | + | individualCmd := fmt.Sprintf("pacman -S --noconfirm --needed %s", pkg) |
| | | 365 | + | switch err := system.ChrootExec(p.logger.LogPath(), config.PathMnt, individualCmd); { |
| | | 366 | + | case err != nil: |
| | | 367 | + | p.logger.Warn("Failed to install package", "package", pkg, "error", err) |
| | | 368 | + | failedPkgs = append(failedPkgs, pkg) |
| | | 369 | + | default: |
| | | 370 | + | successCount++ |
| | | 371 | + | } |
| | | 372 | + | } |
| | | 373 | + | |
| | | 374 | + | return successCount, failedPkgs |
| | | 375 | + | } |
| | | 376 | + | |
| 274 | | // installExtraPackages installs packages from extra.packages file | 377 | | // installExtraPackages installs packages from extra.packages file |
| 275 | | func (p *ReposPhase) installExtraPackages(progressChan chan<- ProgressUpdate) error { | 378 | | func (p *ReposPhase) installExtraPackages(progressChan chan<- ProgressUpdate) error { |
| 276 | | p.SendOutput(progressChan, "Installing extra packages...") | 379 | | p.SendOutput(progressChan, "Installing extra packages...") |
|
| @@ -288,16 +391,36 @@ func (p *ReposPhase) installExtraPackages(progressChan chan<- ProgressUpdate) er |
| 288 | | p.logger.Info("Installing extra packages", "count", len(packages), "packages", strings.Join(packages, ", ")) | 391 | | p.logger.Info("Installing extra packages", "count", len(packages), "packages", strings.Join(packages, ", ")) |
| 289 | | p.SendOutput(progressChan, fmt.Sprintf("Installing %d extra packages...", len(packages))) | 392 | | p.SendOutput(progressChan, fmt.Sprintf("Installing %d extra packages...", len(packages))) |
| 290 | | | 393 | | |
| 291 | - | // Install packages | 394 | + | // Try batch installation first |
| 292 | - | installCmd := fmt.Sprintf("pacman -S --noconfirm %s", strings.Join(packages, " ")) | 395 | + | installCmd := fmt.Sprintf("pacman -S --noconfirm --needed %s", strings.Join(packages, " ")) |
| 293 | - | switch err := system.ChrootExec(p.logger.LogPath(),config.PathMnt, installCmd); { | 396 | + | switch err := system.ChrootExec(p.logger.LogPath(), config.PathMnt, installCmd); { |
| 294 | - | case err != nil: | 397 | + | case err == nil: |
| 295 | - | p.logger.Error("Failed to install extra packages", "error", err, "packages", strings.Join(packages, ", ")) | 398 | + | p.logger.Info("Extra packages installed successfully", "count", len(packages)) |
| 296 | - | return fmt.Errorf("failed to install extra packages: %w", err) | 399 | + | p.SendOutput(progressChan, fmt.Sprintf("[OK] Installed %d extra packages", len(packages))) |
| | | 400 | + | return nil |
| | | 401 | + | } |
| | | 402 | + | |
| | | 403 | + | // Batch installation failed, try individual packages for resilience |
| | | 404 | + | p.logger.Warn("Batch installation failed, trying individual packages", "error", err) |
| | | 405 | + | p.SendOutput(progressChan, "[WARN] Batch installation failed, trying individual packages...") |
| | | 406 | + | |
| | | 407 | + | successCount, failedPkgs := p.installPackagesIndividually(packages, progressChan) |
| | | 408 | + | |
| | | 409 | + | // Report results |
| | | 410 | + | switch { |
| | | 411 | + | case len(failedPkgs) > 0: |
| | | 412 | + | p.SendOutput(progressChan, fmt.Sprintf("[WARN] Failed to install: %s", strings.Join(failedPkgs, ", "))) |
| | | 413 | + | p.logger.Warn("Some packages failed to install", "failed_count", len(failedPkgs), "packages", failedPkgs) |
| 297 | | } | 414 | | } |
| 298 | | | 415 | | |
| 299 | - | p.logger.Info("Extra packages installed successfully", "count", len(packages)) | 416 | + | switch { |
| 300 | - | p.SendOutput(progressChan, fmt.Sprintf("[OK] Installed %d extra packages", len(packages))) | 417 | + | case successCount > 0: |
| | | 418 | + | p.SendOutput(progressChan, fmt.Sprintf("[OK] Installed %d/%d packages", successCount, len(packages))) |
| | | 419 | + | p.logger.Info("Partial package installation successful", "success_count", successCount, "total", len(packages)) |
| | | 420 | + | default: |
| | | 421 | + | p.SendOutput(progressChan, "[ERROR] Failed to install any packages") |
| | | 422 | + | return fmt.Errorf("failed to install all %d packages", len(packages)) |
| | | 423 | + | } |
| 301 | | | 424 | | |
| 302 | | return nil | 425 | | return nil |
| 303 | | } | 426 | | } |
|
| @@ -342,25 +465,47 @@ func (p *ReposPhase) loadExtraPackages() ([]string, error) { |
| 342 | | func (p *ReposPhase) installAURHelper(progressChan chan<- ProgressUpdate) error { | 465 | | func (p *ReposPhase) installAURHelper(progressChan chan<- ProgressUpdate) error { |
| 343 | | p.SendOutput(progressChan, fmt.Sprintf("Building %s from AUR...", p.config.AURHelper)) | 466 | | p.SendOutput(progressChan, fmt.Sprintf("Building %s from AUR...", p.config.AURHelper)) |
| 344 | | | 467 | | |
| | | 468 | + | // Ensure build dependencies are installed |
| | | 469 | + | p.SendOutput(progressChan, "Verifying build dependencies...") |
| | | 470 | + | depsCmd := "pacman -S --needed --noconfirm base-devel git" |
| | | 471 | + | switch err := system.ChrootExec(p.logger.LogPath(), config.PathMnt, depsCmd); { |
| | | 472 | + | case err != nil: |
| | | 473 | + | p.logger.Warn("Failed to install build dependencies", "error", err) |
| | | 474 | + | // Don't fail, they might already be installed |
| | | 475 | + | } |
| | | 476 | + | |
| 345 | | // Build AUR helper from source | 477 | | // Build AUR helper from source |
| 346 | | buildCmd := fmt.Sprintf(` | 478 | | buildCmd := fmt.Sprintf(` |
| 347 | | su - %s -c ' | 479 | | su - %s -c ' |
| 348 | - | cd /tmp && | 480 | + | set -e |
| 349 | - | git clone https://aur.archlinux.org/%s.git && | 481 | + | cd /tmp || exit 1 |
| 350 | - | cd %s && | 482 | + | |
| 351 | - | makepkg -si --noconfirm | 483 | + | # Clean up any previous build |
| | | 484 | + | rm -rf %s |
| | | 485 | + | |
| | | 486 | + | # Clone AUR repository |
| | | 487 | + | git clone https://aur.archlinux.org/%s.git || exit 1 |
| | | 488 | + | cd %s || exit 1 |
| | | 489 | + | |
| | | 490 | + | # Build and install |
| | | 491 | + | makepkg -si --noconfirm || exit 1 |
| 352 | | ' | 492 | | ' |
| 353 | - | `, p.config.Username, p.config.AURHelper, p.config.AURHelper) | 493 | + | `, p.config.Username, p.config.AURHelper, p.config.AURHelper, p.config.AURHelper) |
| 354 | | | 494 | | |
| 355 | - | if err := system.ChrootExec(p.logger.LogPath(), config.PathMnt, buildCmd); err != nil { | 495 | + | switch err := system.ChrootExec(p.logger.LogPath(), config.PathMnt, buildCmd); { |
| | | 496 | + | case err != nil: |
| 356 | | return fmt.Errorf("failed to build %s: %w", p.config.AURHelper, err) | 497 | | return fmt.Errorf("failed to build %s: %w", p.config.AURHelper, err) |
| 357 | | } | 498 | | } |
| 358 | | | 499 | | |
| 359 | - | // Cleanup | 500 | + | // Cleanup build directory |
| 360 | - | cleanupCmd := fmt.Sprintf("rm -rf /home/%s/tmp/%s", p.config.Username, p.config.AURHelper) | 501 | + | cleanupCmd := fmt.Sprintf("rm -rf /tmp/%s", p.config.AURHelper) |
| 361 | - | system.ChrootExec(p.logger.LogPath(), config.PathMnt, cleanupCmd) | 502 | + | switch err := system.ChrootExec(p.logger.LogPath(), config.PathMnt, cleanupCmd); { |
| | | 503 | + | case err != nil: |
| | | 504 | + | p.logger.Warn("Failed to cleanup AUR helper build directory", "error", err) |
| | | 505 | + | // Don't fail, just warn |
| | | 506 | + | } |
| 362 | | | 507 | | |
| 363 | - | p.SendOutput(progressChan, fmt.Sprintf("[OK] %s built and installed", p.config.AURHelper)) | 508 | + | p.SendOutput(progressChan, fmt.Sprintf("[OK] %s built and installed successfully", p.config.AURHelper)) |
| 364 | | | 509 | | |
| 365 | | return nil | 510 | | return nil |
| 366 | | } | 511 | | } |