Skip to content

Commit dec85a5

Browse files
feature: enhance language detection and logging in config command
- Added functionality to detect file extensions in the specified path before language detection. - Implemented logging for recognized file extensions and detected languages using the logger package. - Improved error handling for invalid configurations in the Codacy YAML file. - Refactored the detectLanguagesInPath function to utilize the new detectFileExtensions function for better clarity and maintainability.
1 parent d3659d6 commit dec85a5

File tree

2 files changed

+113
-29
lines changed

2 files changed

+113
-29
lines changed

cmd/config.go

Lines changed: 113 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,9 @@ import (
1616
"codacy/cli-v2/plugins"
1717
"codacy/cli-v2/tools"
1818
"codacy/cli-v2/utils"
19+
"codacy/cli-v2/utils/logger"
1920

21+
"github.com/sirupsen/logrus"
2022
"github.com/spf13/cobra"
2123
"gopkg.in/yaml.v3"
2224
)
@@ -165,8 +167,24 @@ var configDiscoverCmd = &cobra.Command{
165167

166168
fmt.Printf("Discovering languages and tools for path: %s\n", discoverPath)
167169

170+
// Detect file extensions first
171+
extCount, err := detectFileExtensions(discoverPath)
172+
if err != nil {
173+
log.Fatalf("Error detecting file extensions: %v", err)
174+
}
175+
168176
defaultToolLangMap := tools.GetDefaultToolLanguageMapping()
169177

178+
if len(extCount) > 0 {
179+
recognizedExts := getRecognizableExtensions(extCount, defaultToolLangMap)
180+
if len(recognizedExts) > 0 {
181+
logger.Debug("Detected recognizable file extensions", logrus.Fields{
182+
"extensions": recognizedExts,
183+
"path": discoverPath,
184+
})
185+
}
186+
}
187+
170188
detectedLanguages, err := detectLanguagesInPath(discoverPath, defaultToolLangMap)
171189
if err != nil {
172190
log.Fatalf("Error detecting languages: %v", err)
@@ -175,7 +193,10 @@ var configDiscoverCmd = &cobra.Command{
175193
fmt.Println("No known languages detected in the provided path.")
176194
return
177195
}
178-
fmt.Printf("Detected languages: %v\n", getSortedKeys(detectedLanguages))
196+
logger.Debug("Detected languages in path", logrus.Fields{
197+
"languages": getSortedKeys(detectedLanguages),
198+
"path": discoverPath,
199+
})
179200

180201
toolsConfigDir := config.Config.ToolsConfigsDirectory()
181202
if err := updateLanguagesConfig(detectedLanguages, toolsConfigDir, defaultToolLangMap); err != nil {
@@ -192,13 +213,16 @@ var configDiscoverCmd = &cobra.Command{
192213

193214
codacyYAMLPath := config.Config.ProjectConfigFile()
194215
if err := updateCodacyYAML(detectedLanguages, codacyYAMLPath, defaultToolLangMap, configResetInitFlags, currentCliMode); err != nil {
216+
if strings.Contains(err.Error(), "❌ Fatal:") {
217+
fmt.Println(err)
218+
os.Exit(1)
219+
}
195220
log.Fatalf("Error updating %s: %v", codacyYAMLPath, err)
196221
}
197222
fmt.Printf("Updated %s with relevant tools.\n", filepath.Base(codacyYAMLPath))
198223

199224
fmt.Println("\n✅ Successfully discovered languages and updated configurations.")
200225
fmt.Println(" Please review the changes in '.codacy/codacy.yaml' and '.codacy/tools-configs/languages-config.yaml'.")
201-
fmt.Println(" Run 'codacy-cli install' to install any newly added tools.")
202226
},
203227
}
204228

@@ -211,11 +235,12 @@ func getSortedKeys(m map[string]struct{}) []string {
211235
return keys
212236
}
213237

214-
// detectLanguagesInPath walks the directory and detects languages based on file extensions.
238+
// detectLanguagesInPath detects languages based on file extensions found in the path.
215239
func detectLanguagesInPath(rootPath string, toolLangMap map[string]domain.ToolLanguageInfo) (map[string]struct{}, error) {
216240
detectedLangs := make(map[string]struct{})
217241
extToLang := make(map[string][]string)
218242

243+
// Build extension to language mapping
219244
for _, toolInfo := range toolLangMap {
220245
for _, lang := range toolInfo.Languages {
221246
if lang == "Multiple" || lang == "Generic" { // Skip generic language types for direct detection
@@ -227,23 +252,19 @@ func detectLanguagesInPath(rootPath string, toolLangMap map[string]domain.ToolLa
227252
}
228253
}
229254

230-
err := filepath.Walk(rootPath, func(path string, info os.FileInfo, err error) error {
231-
if err != nil {
232-
return err
233-
}
234-
if !info.IsDir() {
235-
ext := strings.ToLower(filepath.Ext(path))
236-
if langs, ok := extToLang[ext]; ok {
237-
for _, lang := range langs {
238-
detectedLangs[lang] = struct{}{}
239-
}
255+
// Get file extensions from the path
256+
extCount, err := detectFileExtensions(rootPath)
257+
if err != nil {
258+
return nil, fmt.Errorf("failed to detect file extensions in path %s: %w", rootPath, err)
259+
}
260+
261+
// Map extensions to languages
262+
for ext := range extCount {
263+
if langs, ok := extToLang[ext]; ok {
264+
for _, lang := range langs {
265+
detectedLangs[lang] = struct{}{}
240266
}
241267
}
242-
return nil
243-
})
244-
245-
if err != nil {
246-
return nil, fmt.Errorf("failed to walk path %s: %w", rootPath, err)
247268
}
248269

249270
return detectedLangs, nil
@@ -355,15 +376,19 @@ func updateCodacyYAML(detectedLanguages map[string]struct{}, codacyYAMLPath stri
355376
return fmt.Errorf("error reading %s: %w", codacyYAMLPath, err)
356377
}
357378
if err := yaml.Unmarshal(content, &configData); err != nil {
358-
return fmt.Errorf("error parsing %s: %w", codacyYAMLPath, err)
379+
if strings.Contains(err.Error(), "cannot unmarshal") {
380+
return fmt.Errorf(
381+
"❌ Fatal: %s contains invalid configuration - run 'codacy-cli config reset' to fix: %v",
382+
filepath.Base(codacyYAMLPath), err)
383+
}
384+
return fmt.Errorf(
385+
"❌ Fatal: %s is broken or has invalid YAML format - run 'codacy-cli config reset' to reinitialize your configuration",
386+
filepath.Base(codacyYAMLPath))
359387
}
360388
} else if os.IsNotExist(err) {
361-
// If codacy.yaml doesn't exist, create a basic structure
362-
configData = make(map[string]interface{})
363-
configData["tools"] = []interface{}{}
364-
configData["runtimes"] = []interface{}{} // Initialize runtimes as well
389+
return fmt.Errorf("codacy.yaml file not found")
365390
} else {
366-
return fmt.Errorf("error statting %s: %w", codacyYAMLPath, err)
391+
return fmt.Errorf("error accessing %s: %w", codacyYAMLPath, err)
367392
}
368393

369394
toolsRaw, _ := configData["tools"].([]interface{})
@@ -533,6 +558,70 @@ func updateCodacyYAML(detectedLanguages map[string]struct{}, codacyYAMLPath stri
533558
return os.WriteFile(codacyYAMLPath, yamlData, utils.DefaultFilePerms)
534559
}
535560

561+
// detectFileExtensions walks the directory and collects all unique file extensions with their counts
562+
func detectFileExtensions(rootPath string) (map[string]int, error) {
563+
extCount := make(map[string]int)
564+
565+
err := filepath.Walk(rootPath, func(path string, info os.FileInfo, err error) error {
566+
if err != nil {
567+
return err
568+
}
569+
if !info.IsDir() {
570+
ext := strings.ToLower(filepath.Ext(path))
571+
if ext != "" {
572+
extCount[ext]++
573+
}
574+
}
575+
return nil
576+
})
577+
578+
if err != nil {
579+
return nil, fmt.Errorf("failed to walk path %s: %w", rootPath, err)
580+
}
581+
582+
return extCount, nil
583+
}
584+
585+
// getRecognizableExtensions returns a sorted list of extensions that are mapped to languages
586+
func getRecognizableExtensions(extCount map[string]int, toolLangMap map[string]domain.ToolLanguageInfo) []string {
587+
// Build set of recognized extensions
588+
recognizedExts := make(map[string]struct{})
589+
for _, toolInfo := range toolLangMap {
590+
for _, ext := range toolInfo.Extensions {
591+
recognizedExts[ext] = struct{}{}
592+
}
593+
}
594+
595+
// Filter and format recognized extensions with counts
596+
type extInfo struct {
597+
ext string
598+
count int
599+
}
600+
var recognizedExtList []extInfo
601+
602+
for ext, count := range extCount {
603+
if _, ok := recognizedExts[ext]; ok {
604+
recognizedExtList = append(recognizedExtList, extInfo{ext, count})
605+
}
606+
}
607+
608+
// Sort by count (descending) and then by extension name
609+
sort.Slice(recognizedExtList, func(i, j int) bool {
610+
if recognizedExtList[i].count != recognizedExtList[j].count {
611+
return recognizedExtList[i].count > recognizedExtList[j].count
612+
}
613+
return recognizedExtList[i].ext < recognizedExtList[j].ext
614+
})
615+
616+
// Format extensions with their counts
617+
result := make([]string, len(recognizedExtList))
618+
for i, info := range recognizedExtList {
619+
result[i] = fmt.Sprintf("%s (%d files)", info.ext, info.count)
620+
}
621+
622+
return result
623+
}
624+
536625
func init() {
537626
// Define flags for the config reset command. These are the same flags used by the init command.
538627
configResetCmd.Flags().StringVar(&configResetInitFlags.ApiToken, "api-token", "", "Optional Codacy API token. If defined, configurations will be fetched from Codacy.")

tools/language_config.go

Lines changed: 0 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -16,11 +16,6 @@ import (
1616

1717
// defaultToolLanguageMap defines the default mapping of tools to their supported languages and file extensions
1818
var defaultToolLanguageMap = map[string]domain.ToolLanguageInfo{
19-
"cppcheck": {
20-
Name: "cppcheck",
21-
Languages: []string{"C", "CPP"},
22-
Extensions: []string{".c", ".cpp", ".cc", ".h", ".hpp"},
23-
},
2419
"pylint": {
2520
Name: "pylint",
2621
Languages: []string{"Python"},

0 commit comments

Comments
 (0)