feat(verify): add shell configuration and theme verification utilities
- commit
- 935f0cf0e769b082b288f1773c01cbd24edde0a2
- author
- bnema · · 2025-10-28 19:45 UTC
- committer
- bnema · · 2025-10-28 20:39 UTC
- parent
- f11c38c
- signature
- unsigned
2 files · +260 -0
A internal/verify/shell.go +87 -0
| @@ -0,0 +1,87 @@ | |||
| 1 | + | package verify | |
| 2 | + | ||
| 3 | + | import ( | |
| 4 | + | "fmt" | |
| 5 | + | "path/filepath" | |
| 6 | + | "strings" | |
| 7 | + | ||
| 8 | + | "github.com/bnema/archup/internal/config" | |
| 9 | + | "github.com/bnema/archup/internal/interfaces" | |
| 10 | + | ) | |
| 11 | + | ||
| 12 | + | // ShellConfig holds verification results for shell configuration | |
| 13 | + | type ShellConfig struct { | |
| 14 | + | BashrcExists bool | |
| 15 | + | ShellFilesOK bool | |
| 16 | + | BashrcSyntaxOK bool | |
| 17 | + | Warnings []string | |
| 18 | + | Errors []string | |
| 19 | + | } | |
| 20 | + | ||
| 21 | + | // ValidateShellConfigs performs comprehensive shell configuration verification | |
| 22 | + | func ValidateShellConfigs( | |
| 23 | + | fs interfaces.FileSystem, | |
| 24 | + | chrExec interfaces.ChrootExecutor, | |
| 25 | + | logPath string, | |
| 26 | + | userHome string, | |
| 27 | + | ) ShellConfig { | |
| 28 | + | result := ShellConfig{ | |
| 29 | + | Warnings: []string{}, | |
| 30 | + | Errors: []string{}, | |
| 31 | + | } | |
| 32 | + | ||
| 33 | + | // Check .bashrc exists | |
| 34 | + | bashrcPath := filepath.Join(userHome, ".bashrc") | |
| 35 | + | _, err := fs.Stat(bashrcPath) | |
| 36 | + | if fs.IsNotExist(err) { | |
| 37 | + | result.BashrcExists = false | |
| 38 | + | result.Warnings = append(result.Warnings, ".bashrc not found") | |
| 39 | + | } else if err != nil { | |
| 40 | + | result.Warnings = append(result.Warnings, fmt.Sprintf(".bashrc stat error: %v", err)) | |
| 41 | + | } else { | |
| 42 | + | result.BashrcExists = true | |
| 43 | + | } | |
| 44 | + | ||
| 45 | + | // Check shell config directory exists | |
| 46 | + | archupDefaultBash := filepath.Join(userHome, ".local", "share", "archup", "default", "bash") | |
| 47 | + | _, err = fs.Stat(archupDefaultBash) | |
| 48 | + | if fs.IsNotExist(err) { | |
| 49 | + | result.Warnings = append(result.Warnings, "shell config directory missing") | |
| 50 | + | } else if err == nil { | |
| 51 | + | result.ShellFilesOK = true | |
| 52 | + | } else { | |
| 53 | + | result.Warnings = append(result.Warnings, fmt.Sprintf("shell config directory stat error: %v", err)) | |
| 54 | + | } | |
| 55 | + | ||
| 56 | + | return result | |
| 57 | + | } | |
| 58 | + | ||
| 59 | + | // ValidateBashSyntax tests bash configuration syntax by loading bash in chroot | |
| 60 | + | func ValidateBashSyntax( | |
| 61 | + | chrExec interfaces.ChrootExecutor, | |
| 62 | + | logPath string, | |
| 63 | + | ) error { | |
| 64 | + | // Test bash syntax: bash -n ~/.bashrc in chroot | |
| 65 | + | cmd := "bash -n /root/.bashrc" | |
| 66 | + | if err := chrExec.ChrootExec(logPath, config.PathMnt, cmd); err != nil { | |
| 67 | + | return fmt.Errorf("bash syntax check failed: %w", err) | |
| 68 | + | } | |
| 69 | + | return nil | |
| 70 | + | } | |
| 71 | + | ||
| 72 | + | // GetShellConfigWarnings returns formatted warning messages | |
| 73 | + | func GetShellConfigWarnings(result ShellConfig) string { | |
| 74 | + | if len(result.Warnings) == 0 && len(result.Errors) == 0 { | |
| 75 | + | return "" | |
| 76 | + | } | |
| 77 | + | ||
| 78 | + | var msgs []string | |
| 79 | + | for _, w := range result.Warnings { | |
| 80 | + | msgs = append(msgs, fmt.Sprintf("[WARN] %s", w)) | |
| 81 | + | } | |
| 82 | + | for _, e := range result.Errors { | |
| 83 | + | msgs = append(msgs, fmt.Sprintf("[ERROR] %s", e)) | |
| 84 | + | } | |
| 85 | + | ||
| 86 | + | return strings.Join(msgs, "\n") | |
| 87 | + | } | |
| @@ -0,0 +1,87 @@ | |||||
| 1 | + | package verify | |||
| 2 | + | ||||
| 3 | + | import ( | |||
| 4 | + | "fmt" | |||
| 5 | + | "path/filepath" | |||
| 6 | + | "strings" | |||
| 7 | + | ||||
| 8 | + | "github.com/bnema/archup/internal/config" | |||
| 9 | + | "github.com/bnema/archup/internal/interfaces" | |||
| 10 | + | ) | |||
| 11 | + | ||||
| 12 | + | // ShellConfig holds verification results for shell configuration | |||
| 13 | + | type ShellConfig struct { | |||
| 14 | + | BashrcExists bool | |||
| 15 | + | ShellFilesOK bool | |||
| 16 | + | BashrcSyntaxOK bool | |||
| 17 | + | Warnings []string | |||
| 18 | + | Errors []string | |||
| 19 | + | } | |||
| 20 | + | ||||
| 21 | + | // ValidateShellConfigs performs comprehensive shell configuration verification | |||
| 22 | + | func ValidateShellConfigs( | |||
| 23 | + | fs interfaces.FileSystem, | |||
| 24 | + | chrExec interfaces.ChrootExecutor, | |||
| 25 | + | logPath string, | |||
| 26 | + | userHome string, | |||
| 27 | + | ) ShellConfig { | |||
| 28 | + | result := ShellConfig{ | |||
| 29 | + | Warnings: []string{}, | |||
| 30 | + | Errors: []string{}, | |||
| 31 | + | } | |||
| 32 | + | ||||
| 33 | + | // Check .bashrc exists | |||
| 34 | + | bashrcPath := filepath.Join(userHome, ".bashrc") | |||
| 35 | + | _, err := fs.Stat(bashrcPath) | |||
| 36 | + | if fs.IsNotExist(err) { | |||
| 37 | + | result.BashrcExists = false | |||
| 38 | + | result.Warnings = append(result.Warnings, ".bashrc not found") | |||
| 39 | + | } else if err != nil { | |||
| 40 | + | result.Warnings = append(result.Warnings, fmt.Sprintf(".bashrc stat error: %v", err)) | |||
| 41 | + | } else { | |||
| 42 | + | result.BashrcExists = true | |||
| 43 | + | } | |||
| 44 | + | ||||
| 45 | + | // Check shell config directory exists | |||
| 46 | + | archupDefaultBash := filepath.Join(userHome, ".local", "share", "archup", "default", "bash") | |||
| 47 | + | _, err = fs.Stat(archupDefaultBash) | |||
| 48 | + | if fs.IsNotExist(err) { | |||
| 49 | + | result.Warnings = append(result.Warnings, "shell config directory missing") | |||
| 50 | + | } else if err == nil { | |||
| 51 | + | result.ShellFilesOK = true | |||
| 52 | + | } else { | |||
| 53 | + | result.Warnings = append(result.Warnings, fmt.Sprintf("shell config directory stat error: %v", err)) | |||
| 54 | + | } | |||
| 55 | + | ||||
| 56 | + | return result | |||
| 57 | + | } | |||
| 58 | + | ||||
| 59 | + | // ValidateBashSyntax tests bash configuration syntax by loading bash in chroot | |||
| 60 | + | func ValidateBashSyntax( | |||
| 61 | + | chrExec interfaces.ChrootExecutor, | |||
| 62 | + | logPath string, | |||
| 63 | + | ) error { | |||
| 64 | + | // Test bash syntax: bash -n ~/.bashrc in chroot | |||
| 65 | + | cmd := "bash -n /root/.bashrc" | |||
| 66 | + | if err := chrExec.ChrootExec(logPath, config.PathMnt, cmd); err != nil { | |||
| 67 | + | return fmt.Errorf("bash syntax check failed: %w", err) | |||
| 68 | + | } | |||
| 69 | + | return nil | |||
| 70 | + | } | |||
| 71 | + | ||||
| 72 | + | // GetShellConfigWarnings returns formatted warning messages | |||
| 73 | + | func GetShellConfigWarnings(result ShellConfig) string { | |||
| 74 | + | if len(result.Warnings) == 0 && len(result.Errors) == 0 { | |||
| 75 | + | return "" | |||
| 76 | + | } | |||
| 77 | + | ||||
| 78 | + | var msgs []string | |||
| 79 | + | for _, w := range result.Warnings { | |||
| 80 | + | msgs = append(msgs, fmt.Sprintf("[WARN] %s", w)) | |||
| 81 | + | } | |||
| 82 | + | for _, e := range result.Errors { | |||
| 83 | + | msgs = append(msgs, fmt.Sprintf("[ERROR] %s", e)) | |||
| 84 | + | } | |||
| 85 | + | ||||
| 86 | + | return strings.Join(msgs, "\n") | |||
| 87 | + | } | |||
A internal/verify/themes.go +173 -0
| @@ -0,0 +1,173 @@ | |||
| 1 | + | package verify | |
| 2 | + | ||
| 3 | + | import ( | |
| 4 | + | "fmt" | |
| 5 | + | "path/filepath" | |
| 6 | + | "strings" | |
| 7 | + | ||
| 8 | + | "github.com/bnema/archup/internal/config" | |
| 9 | + | "github.com/bnema/archup/internal/interfaces" | |
| 10 | + | ) | |
| 11 | + | ||
| 12 | + | // ThemeVerification holds results of theme verification | |
| 13 | + | type ThemeVerification struct { | |
| 14 | + | BleuThemeCloned bool | |
| 15 | + | ThemeFilesOK map[string]bool // Tool name -> exists | |
| 16 | + | ConfigFilesOK map[string]bool // Tool name -> config file exists | |
| 17 | + | BatCacheBuilt bool | |
| 18 | + | StarshipValid bool | |
| 19 | + | Warnings []string | |
| 20 | + | Errors []string | |
| 21 | + | } | |
| 22 | + | ||
| 23 | + | // NewThemeVerification creates a new theme verification result | |
| 24 | + | func NewThemeVerification() *ThemeVerification { | |
| 25 | + | return &ThemeVerification{ | |
| 26 | + | ThemeFilesOK: make(map[string]bool), | |
| 27 | + | ConfigFilesOK: make(map[string]bool), | |
| 28 | + | Warnings: []string{}, | |
| 29 | + | Errors: []string{}, | |
| 30 | + | } | |
| 31 | + | } | |
| 32 | + | ||
| 33 | + | // ValidateBleuThemeClone verifies the bleu-theme repository was successfully cloned | |
| 34 | + | func ValidateBleuThemeClone(fs interfaces.FileSystem, userHome string) bool { | |
| 35 | + | bleuThemeDir := filepath.Join(userHome, ".local", "share", "archup", "themes", "bleu") | |
| 36 | + | gitDir := filepath.Join(bleuThemeDir, ".git") | |
| 37 | + | ||
| 38 | + | // Check if .git directory exists (indicates successful clone) | |
| 39 | + | _, err := fs.Stat(gitDir) | |
| 40 | + | return err == nil | |
| 41 | + | } | |
| 42 | + | ||
| 43 | + | // ValidateThemeFiles checks if expected theme files exist after cloning | |
| 44 | + | func ValidateThemeFiles(fs interfaces.FileSystem, userHome string) *ThemeVerification { | |
| 45 | + | result := NewThemeVerification() | |
| 46 | + | themeDir := filepath.Join(userHome, ".local", "share", "archup", "themes", "bleu") | |
| 47 | + | ||
| 48 | + | // Check clone first | |
| 49 | + | result.BleuThemeCloned = ValidateBleuThemeClone(fs, userHome) | |
| 50 | + | if !result.BleuThemeCloned { | |
| 51 | + | result.Warnings = append(result.Warnings, "bleu-theme not cloned successfully") | |
| 52 | + | return result | |
| 53 | + | } | |
| 54 | + | ||
| 55 | + | // Theme file paths to check | |
| 56 | + | themeFiles := map[string]string{ | |
| 57 | + | "starship": filepath.Join(themeDir, "starship", "bleu.toml"), | |
| 58 | + | "eza": filepath.Join(themeDir, "eza", "theme.yml"), | |
| 59 | + | "bat": filepath.Join(themeDir, "bat", "bleu.tmTheme"), | |
| 60 | + | "btop": filepath.Join(themeDir, "btop", "bleu.theme"), | |
| 61 | + | "yazi": filepath.Join(themeDir, "yazi", "bleu.toml"), | |
| 62 | + | "fzf": filepath.Join(themeDir, "fzf", "bleu.sh"), | |
| 63 | + | } | |
| 64 | + | ||
| 65 | + | for tool, path := range themeFiles { | |
| 66 | + | _, err := fs.Stat(path) | |
| 67 | + | exists := err == nil | |
| 68 | + | result.ThemeFilesOK[tool] = exists | |
| 69 | + | if !exists { | |
| 70 | + | result.Warnings = append(result.Warnings, fmt.Sprintf("%s theme file missing: %s", tool, path)) | |
| 71 | + | } | |
| 72 | + | } | |
| 73 | + | ||
| 74 | + | return result | |
| 75 | + | } | |
| 76 | + | ||
| 77 | + | // ValidateConfigFiles checks if configuration files were successfully copied | |
| 78 | + | func ValidateConfigFiles(fs interfaces.FileSystem, userHome string) *ThemeVerification { | |
| 79 | + | result := NewThemeVerification() | |
| 80 | + | ||
| 81 | + | configFiles := map[string]string{ | |
| 82 | + | "starship": filepath.Join(userHome, ".local", "share", "archup", "default", "starship.toml"), | |
| 83 | + | "eza": filepath.Join(userHome, ".config", "eza", "theme.yml"), | |
| 84 | + | "bat": filepath.Join(userHome, ".config", "bat", "themes", "bleu.tmTheme"), | |
| 85 | + | "btop": filepath.Join(userHome, ".config", "btop", "themes", "bleu.theme"), | |
| 86 | + | "yazi": filepath.Join(userHome, ".config", "yazi", "flavors", "bleu", "flavor.toml"), | |
| 87 | + | } | |
| 88 | + | ||
| 89 | + | for tool, path := range configFiles { | |
| 90 | + | _, err := fs.Stat(path) | |
| 91 | + | exists := err == nil | |
| 92 | + | result.ConfigFilesOK[tool] = exists | |
| 93 | + | if !exists { | |
| 94 | + | result.Warnings = append(result.Warnings, fmt.Sprintf("%s config not found: %s", tool, path)) | |
| 95 | + | } | |
| 96 | + | } | |
| 97 | + | ||
| 98 | + | return result | |
| 99 | + | } | |
| 100 | + | ||
| 101 | + | // ValidateBatCache checks if bat cache was built successfully | |
| 102 | + | // This would require running `bat --list-themes` in chroot | |
| 103 | + | func ValidateBatCache(chrExec interfaces.ChrootExecutor, logPath string) bool { | |
| 104 | + | cmd := "bat --list-themes | grep -i bleu" | |
| 105 | + | err := chrExec.ChrootExec(logPath, config.PathMnt, cmd) | |
| 106 | + | return err == nil | |
| 107 | + | } | |
| 108 | + | ||
| 109 | + | // ValidateStarshipConfig tests if starship can load its configuration | |
| 110 | + | func ValidateStarshipConfig(chrExec interfaces.ChrootExecutor, logPath string) bool { | |
| 111 | + | cmd := "starship config" | |
| 112 | + | err := chrExec.ChrootExec(logPath, config.PathMnt, cmd) | |
| 113 | + | return err == nil | |
| 114 | + | } | |
| 115 | + | ||
| 116 | + | // GetThemeVerificationSummary returns a formatted summary of theme verification | |
| 117 | + | func GetThemeVerificationSummary(result *ThemeVerification) string { | |
| 118 | + | var summary []string | |
| 119 | + | ||
| 120 | + | if result.BleuThemeCloned { | |
| 121 | + | summary = append(summary, "[OK] Bleu-theme repository cloned") | |
| 122 | + | } else { | |
| 123 | + | summary = append(summary, "[WARN] Bleu-theme repository not cloned") | |
| 124 | + | } | |
| 125 | + | ||
| 126 | + | // Theme files summary | |
| 127 | + | missingThemes := []string{} | |
| 128 | + | for tool, ok := range result.ThemeFilesOK { | |
| 129 | + | if !ok { | |
| 130 | + | missingThemes = append(missingThemes, tool) | |
| 131 | + | } | |
| 132 | + | } | |
| 133 | + | if len(missingThemes) > 0 { | |
| 134 | + | summary = append(summary, fmt.Sprintf("[WARN] Missing theme files: %s", strings.Join(missingThemes, ", "))) | |
| 135 | + | } else if len(result.ThemeFilesOK) > 0 { | |
| 136 | + | summary = append(summary, "[OK] All theme files found") | |
| 137 | + | } | |
| 138 | + | ||
| 139 | + | // Config files summary | |
| 140 | + | missingConfigs := []string{} | |
| 141 | + | for tool, ok := range result.ConfigFilesOK { | |
| 142 | + | if !ok { | |
| 143 | + | missingConfigs = append(missingConfigs, tool) | |
| 144 | + | } | |
| 145 | + | } | |
| 146 | + | if len(missingConfigs) > 0 { | |
| 147 | + | summary = append(summary, fmt.Sprintf("[WARN] Missing config files: %s", strings.Join(missingConfigs, ", "))) | |
| 148 | + | } else if len(result.ConfigFilesOK) > 0 { | |
| 149 | + | summary = append(summary, "[OK] All config files found") | |
| 150 | + | } | |
| 151 | + | ||
| 152 | + | // Bat cache | |
| 153 | + | if result.BatCacheBuilt { | |
| 154 | + | summary = append(summary, "[OK] Bat cache built successfully") | |
| 155 | + | } | |
| 156 | + | ||
| 157 | + | // Starship config | |
| 158 | + | if result.StarshipValid { | |
| 159 | + | summary = append(summary, "[OK] Starship config valid") | |
| 160 | + | } | |
| 161 | + | ||
| 162 | + | // Warnings | |
| 163 | + | for _, w := range result.Warnings { | |
| 164 | + | summary = append(summary, fmt.Sprintf("[WARN] %s", w)) | |
| 165 | + | } | |
| 166 | + | ||
| 167 | + | // Errors | |
| 168 | + | for _, e := range result.Errors { | |
| 169 | + | summary = append(summary, fmt.Sprintf("[ERROR] %s", e)) | |
| 170 | + | } | |
| 171 | + | ||
| 172 | + | return strings.Join(summary, "\n") | |
| 173 | + | } | |
| @@ -0,0 +1,173 @@ | |||||
| 1 | + | package verify | |||
| 2 | + | ||||
| 3 | + | import ( | |||
| 4 | + | "fmt" | |||
| 5 | + | "path/filepath" | |||
| 6 | + | "strings" | |||
| 7 | + | ||||
| 8 | + | "github.com/bnema/archup/internal/config" | |||
| 9 | + | "github.com/bnema/archup/internal/interfaces" | |||
| 10 | + | ) | |||
| 11 | + | ||||
| 12 | + | // ThemeVerification holds results of theme verification | |||
| 13 | + | type ThemeVerification struct { | |||
| 14 | + | BleuThemeCloned bool | |||
| 15 | + | ThemeFilesOK map[string]bool // Tool name -> exists | |||
| 16 | + | ConfigFilesOK map[string]bool // Tool name -> config file exists | |||
| 17 | + | BatCacheBuilt bool | |||
| 18 | + | StarshipValid bool | |||
| 19 | + | Warnings []string | |||
| 20 | + | Errors []string | |||
| 21 | + | } | |||
| 22 | + | ||||
| 23 | + | // NewThemeVerification creates a new theme verification result | |||
| 24 | + | func NewThemeVerification() *ThemeVerification { | |||
| 25 | + | return &ThemeVerification{ | |||
| 26 | + | ThemeFilesOK: make(map[string]bool), | |||
| 27 | + | ConfigFilesOK: make(map[string]bool), | |||
| 28 | + | Warnings: []string{}, | |||
| 29 | + | Errors: []string{}, | |||
| 30 | + | } | |||
| 31 | + | } | |||
| 32 | + | ||||
| 33 | + | // ValidateBleuThemeClone verifies the bleu-theme repository was successfully cloned | |||
| 34 | + | func ValidateBleuThemeClone(fs interfaces.FileSystem, userHome string) bool { | |||
| 35 | + | bleuThemeDir := filepath.Join(userHome, ".local", "share", "archup", "themes", "bleu") | |||
| 36 | + | gitDir := filepath.Join(bleuThemeDir, ".git") | |||
| 37 | + | ||||
| 38 | + | // Check if .git directory exists (indicates successful clone) | |||
| 39 | + | _, err := fs.Stat(gitDir) | |||
| 40 | + | return err == nil | |||
| 41 | + | } | |||
| 42 | + | ||||
| 43 | + | // ValidateThemeFiles checks if expected theme files exist after cloning | |||
| 44 | + | func ValidateThemeFiles(fs interfaces.FileSystem, userHome string) *ThemeVerification { | |||
| 45 | + | result := NewThemeVerification() | |||
| 46 | + | themeDir := filepath.Join(userHome, ".local", "share", "archup", "themes", "bleu") | |||
| 47 | + | ||||
| 48 | + | // Check clone first | |||
| 49 | + | result.BleuThemeCloned = ValidateBleuThemeClone(fs, userHome) | |||
| 50 | + | if !result.BleuThemeCloned { | |||
| 51 | + | result.Warnings = append(result.Warnings, "bleu-theme not cloned successfully") | |||
| 52 | + | return result | |||
| 53 | + | } | |||
| 54 | + | ||||
| 55 | + | // Theme file paths to check | |||
| 56 | + | themeFiles := map[string]string{ | |||
| 57 | + | "starship": filepath.Join(themeDir, "starship", "bleu.toml"), | |||
| 58 | + | "eza": filepath.Join(themeDir, "eza", "theme.yml"), | |||
| 59 | + | "bat": filepath.Join(themeDir, "bat", "bleu.tmTheme"), | |||
| 60 | + | "btop": filepath.Join(themeDir, "btop", "bleu.theme"), | |||
| 61 | + | "yazi": filepath.Join(themeDir, "yazi", "bleu.toml"), | |||
| 62 | + | "fzf": filepath.Join(themeDir, "fzf", "bleu.sh"), | |||
| 63 | + | } | |||
| 64 | + | ||||
| 65 | + | for tool, path := range themeFiles { | |||
| 66 | + | _, err := fs.Stat(path) | |||
| 67 | + | exists := err == nil | |||
| 68 | + | result.ThemeFilesOK[tool] = exists | |||
| 69 | + | if !exists { | |||
| 70 | + | result.Warnings = append(result.Warnings, fmt.Sprintf("%s theme file missing: %s", tool, path)) | |||
| 71 | + | } | |||
| 72 | + | } | |||
| 73 | + | ||||
| 74 | + | return result | |||
| 75 | + | } | |||
| 76 | + | ||||
| 77 | + | // ValidateConfigFiles checks if configuration files were successfully copied | |||
| 78 | + | func ValidateConfigFiles(fs interfaces.FileSystem, userHome string) *ThemeVerification { | |||
| 79 | + | result := NewThemeVerification() | |||
| 80 | + | ||||
| 81 | + | configFiles := map[string]string{ | |||
| 82 | + | "starship": filepath.Join(userHome, ".local", "share", "archup", "default", "starship.toml"), | |||
| 83 | + | "eza": filepath.Join(userHome, ".config", "eza", "theme.yml"), | |||
| 84 | + | "bat": filepath.Join(userHome, ".config", "bat", "themes", "bleu.tmTheme"), | |||
| 85 | + | "btop": filepath.Join(userHome, ".config", "btop", "themes", "bleu.theme"), | |||
| 86 | + | "yazi": filepath.Join(userHome, ".config", "yazi", "flavors", "bleu", "flavor.toml"), | |||
| 87 | + | } | |||
| 88 | + | ||||
| 89 | + | for tool, path := range configFiles { | |||
| 90 | + | _, err := fs.Stat(path) | |||
| 91 | + | exists := err == nil | |||
| 92 | + | result.ConfigFilesOK[tool] = exists | |||
| 93 | + | if !exists { | |||
| 94 | + | result.Warnings = append(result.Warnings, fmt.Sprintf("%s config not found: %s", tool, path)) | |||
| 95 | + | } | |||
| 96 | + | } | |||
| 97 | + | ||||
| 98 | + | return result | |||
| 99 | + | } | |||
| 100 | + | ||||
| 101 | + | // ValidateBatCache checks if bat cache was built successfully | |||
| 102 | + | // This would require running `bat --list-themes` in chroot | |||
| 103 | + | func ValidateBatCache(chrExec interfaces.ChrootExecutor, logPath string) bool { | |||
| 104 | + | cmd := "bat --list-themes | grep -i bleu" | |||
| 105 | + | err := chrExec.ChrootExec(logPath, config.PathMnt, cmd) | |||
| 106 | + | return err == nil | |||
| 107 | + | } | |||
| 108 | + | ||||
| 109 | + | // ValidateStarshipConfig tests if starship can load its configuration | |||
| 110 | + | func ValidateStarshipConfig(chrExec interfaces.ChrootExecutor, logPath string) bool { | |||
| 111 | + | cmd := "starship config" | |||
| 112 | + | err := chrExec.ChrootExec(logPath, config.PathMnt, cmd) | |||
| 113 | + | return err == nil | |||
| 114 | + | } | |||
| 115 | + | ||||
| 116 | + | // GetThemeVerificationSummary returns a formatted summary of theme verification | |||
| 117 | + | func GetThemeVerificationSummary(result *ThemeVerification) string { | |||
| 118 | + | var summary []string | |||
| 119 | + | ||||
| 120 | + | if result.BleuThemeCloned { | |||
| 121 | + | summary = append(summary, "[OK] Bleu-theme repository cloned") | |||
| 122 | + | } else { | |||
| 123 | + | summary = append(summary, "[WARN] Bleu-theme repository not cloned") | |||
| 124 | + | } | |||
| 125 | + | ||||
| 126 | + | // Theme files summary | |||
| 127 | + | missingThemes := []string{} | |||
| 128 | + | for tool, ok := range result.ThemeFilesOK { | |||
| 129 | + | if !ok { | |||
| 130 | + | missingThemes = append(missingThemes, tool) | |||
| 131 | + | } | |||
| 132 | + | } | |||
| 133 | + | if len(missingThemes) > 0 { | |||
| 134 | + | summary = append(summary, fmt.Sprintf("[WARN] Missing theme files: %s", strings.Join(missingThemes, ", "))) | |||
| 135 | + | } else if len(result.ThemeFilesOK) > 0 { | |||
| 136 | + | summary = append(summary, "[OK] All theme files found") | |||
| 137 | + | } | |||
| 138 | + | ||||
| 139 | + | // Config files summary | |||
| 140 | + | missingConfigs := []string{} | |||
| 141 | + | for tool, ok := range result.ConfigFilesOK { | |||
| 142 | + | if !ok { | |||
| 143 | + | missingConfigs = append(missingConfigs, tool) | |||
| 144 | + | } | |||
| 145 | + | } | |||
| 146 | + | if len(missingConfigs) > 0 { | |||
| 147 | + | summary = append(summary, fmt.Sprintf("[WARN] Missing config files: %s", strings.Join(missingConfigs, ", "))) | |||
| 148 | + | } else if len(result.ConfigFilesOK) > 0 { | |||
| 149 | + | summary = append(summary, "[OK] All config files found") | |||
| 150 | + | } | |||
| 151 | + | ||||
| 152 | + | // Bat cache | |||
| 153 | + | if result.BatCacheBuilt { | |||
| 154 | + | summary = append(summary, "[OK] Bat cache built successfully") | |||
| 155 | + | } | |||
| 156 | + | ||||
| 157 | + | // Starship config | |||
| 158 | + | if result.StarshipValid { | |||
| 159 | + | summary = append(summary, "[OK] Starship config valid") | |||
| 160 | + | } | |||
| 161 | + | ||||
| 162 | + | // Warnings | |||
| 163 | + | for _, w := range result.Warnings { | |||
| 164 | + | summary = append(summary, fmt.Sprintf("[WARN] %s", w)) | |||
| 165 | + | } | |||
| 166 | + | ||||
| 167 | + | // Errors | |||
| 168 | + | for _, e := range result.Errors { | |||
| 169 | + | summary = append(summary, fmt.Sprintf("[ERROR] %s", e)) | |||
| 170 | + | } | |||
| 171 | + | ||||
| 172 | + | return strings.Join(summary, "\n") | |||
| 173 | + | } | |||