Skip to content

Commit 316466d

Browse files
refactor: Replace hardcoded language config with API-based dynamic loading
- Add GetLanguageTools() API client for /api/v3/languages/tools - Replace embedded YAML with BuildLanguagesConfigFromAPI() - Clean tool-to-API-language mapping instead of complex switch logic - Update integration tests with richer API-sourced extensions Eliminates tech debt and provides dynamic file extension updates.
1 parent 86a2cb2 commit 316466d

File tree

8 files changed

+171
-58
lines changed

8 files changed

+171
-58
lines changed

cmd/analyze.go

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@ import (
77
"codacy/cli-v2/tools"
88
"codacy/cli-v2/tools/lizard"
99
reviveTool "codacy/cli-v2/tools/revive"
10+
"codacy/cli-v2/utils/logger"
1011
"encoding/json"
1112
"fmt"
1213
"log"
@@ -18,6 +19,7 @@ import (
1819

1920
codacyclient "codacy/cli-v2/codacy-client"
2021

22+
"github.com/sirupsen/logrus"
2123
"github.com/spf13/cobra"
2224
"gopkg.in/yaml.v3"
2325
)
@@ -373,6 +375,9 @@ func runTool(workDirectory string, toolName string, pathsToCheck []string, outpu
373375
func validatePaths(paths []string) error {
374376
for _, path := range paths {
375377
if _, err := os.Stat(path); os.IsNotExist(err) {
378+
logger.Error("Analysis failed because path does not exist", logrus.Fields{
379+
"path": path,
380+
})
376381
return fmt.Errorf("❌ Error: cannot find file or directory '%s'", path)
377382
}
378383
}

cmd/configsetup/setup.go

Lines changed: 20 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,8 @@ import (
1818
"codacy/cli-v2/tools/pylint"
1919
reviveTool "codacy/cli-v2/tools/revive"
2020
"codacy/cli-v2/utils"
21+
22+
"gopkg.in/yaml.v3"
2123
)
2224

2325
// Configuration file names - extracted as constants to avoid duplication
@@ -194,33 +196,24 @@ func (r *reviveConfigCreator) GetConfigFileName() string { return "revive.toml"
194196
func (r *reviveConfigCreator) GetToolName() string { return "Revive" }
195197

196198
func CreateLanguagesConfigFileLocal(toolsConfigDir string) error {
197-
content := `tools:
198-
- name: pylint
199-
languages: [Python]
200-
extensions: [.py]
201-
- name: eslint
202-
languages: [JavaScript, TypeScript, JSX, TSX]
203-
extensions: [.js, .jsx, .ts, .tsx]
204-
- name: pmd
205-
languages: [Java, JavaScript, JSP, Velocity, XML, Apex, Scala, Ruby, VisualForce]
206-
extensions: [.java, .js, .jsp, .vm, .xml, .cls, .trigger, .scala, .rb, .page, .component]
207-
- name: trivy
208-
languages: [Multiple]
209-
extensions: []
210-
- name: dartanalyzer
211-
languages: [Dart]
212-
extensions: [.dart]
213-
- name: lizard
214-
languages: [C, CPP, Java, "C#", JavaScript, TypeScript, VueJS, "Objective-C", Swift, Python, Ruby, "TTCN-3", PHP, Scala, GDScript, Golang, Lua, Rust, Fortran, Kotlin, Solidity, Erlang, Zig, Perl]
215-
extensions: [.c, .cpp, .cc, .h, .hpp, .java, .cs, .js, .jsx, .ts, .tsx, .vue, .m, .swift, .py, .rb, .ttcn, .php, .scala, .gd, .go, .lua, .rs, .f, .f90, .kt, .sol, .erl, .zig, .pl]
216-
- name: semgrep
217-
languages: [C, CPP, "C#", Generic, Go, Java, JavaScript, JSON, Kotlin, Python, TypeScript, Ruby, Rust, JSX, PHP, Scala, Swift, Terraform]
218-
extensions: [.c, .cpp, .h, .hpp, .cs, .go, .java, .js, .json, .kt, .py, .ts, .rb, .rs, .jsx, .php, .scala, .swift, .tf, .tfvars]
219-
- name: codacy-enigma-cli
220-
languages: [Multiple]
221-
extensions: []`
222-
223-
return writeConfigFile(filepath.Join(toolsConfigDir, LanguagesConfigFileName), []byte(content))
199+
// Build tool language configurations from API
200+
configTools, err := tools.BuildLanguagesConfigFromAPI()
201+
if err != nil {
202+
return fmt.Errorf("failed to build languages config from API: %w", err)
203+
}
204+
205+
// Create the config structure
206+
config := domain.LanguagesConfig{
207+
Tools: configTools,
208+
}
209+
210+
// Marshal to YAML
211+
data, err := yaml.Marshal(config)
212+
if err != nil {
213+
return fmt.Errorf("failed to marshal languages config to YAML: %w", err)
214+
}
215+
216+
return writeConfigFile(filepath.Join(toolsConfigDir, LanguagesConfigFileName), data)
224217
}
225218

226219
func CreateGitIgnoreFile() error {

codacy-client/client.go

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -253,3 +253,21 @@ func GetRepositoryLanguages(initFlags domain.InitFlags) ([]domain.Language, erro
253253

254254
return languagesResponse.Languages, nil
255255
}
256+
257+
// GetLanguageTools fetches the default language file extensions from the API
258+
func GetLanguageTools() ([]domain.LanguageTool, error) {
259+
baseURL := fmt.Sprintf("%s/api/v3/languages/tools", CodacyApiBase)
260+
261+
bodyResponse, err := getRequest(baseURL, "")
262+
if err != nil {
263+
return nil, fmt.Errorf("failed to get language tools: %w", err)
264+
}
265+
266+
var languageToolsResponse domain.LanguageToolsResponse
267+
err = json.Unmarshal(bodyResponse, &languageToolsResponse)
268+
if err != nil {
269+
return nil, fmt.Errorf("failed to unmarshal language tools response: %w", err)
270+
}
271+
272+
return languageToolsResponse.Data, nil
273+
}

domain/language.go

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,17 @@ type LanguagesResponse struct {
1414
Languages []Language `json:"languages"`
1515
}
1616

17+
// LanguageTool represents a language tool with its file extensions from the API
18+
type LanguageTool struct {
19+
Name string `json:"name"`
20+
FileExtensions []string `json:"fileExtensions"`
21+
}
22+
23+
// LanguageToolsResponse represents the structure of the language tools API response
24+
type LanguageToolsResponse struct {
25+
Data []LanguageTool `json:"data"`
26+
}
27+
1728
// ToolLanguageInfo contains language and extension information for a tool
1829
type ToolLanguageInfo struct {
1930
Name string `yaml:"name"`

integration-tests/config-discover/expected/tools-configs/languages-config.yaml

Lines changed: 7 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -7,19 +7,22 @@ tools:
77
extensions: [.dart]
88
- name: eslint
99
languages: [JavaScript, TypeScript, JSX, TSX]
10-
extensions: [.js, .jsx, .ts, .tsx]
10+
extensions: [.js, .jsm, .jsx, .mjs, .ts, .tsx, .vue]
1111
- name: lizard
1212
languages: [C, CPP, Java, C#, JavaScript, TypeScript, VueJS, Objective-C, Swift, Python, Ruby, TTCN-3, PHP, Scala, GDScript, Golang, Lua, Rust, Fortran, Kotlin, Solidity, Erlang, Zig, Perl]
13-
extensions: [.c, .cpp, .cc, .h, .hpp, .java, .cs, .js, .jsx, .ts, .tsx, .vue, .m, .swift, .py, .rb, .ttcn, .php, .scala, .gd, .go, .lua, .rs, .f, .f90, .kt, .sol, .erl, .zig, .pl]
13+
extensions: [.c, .cc, .cpp, .cxx, .gemspec, .go, .h, .hpp, .ino, .java, .jbuilder, .js, .jsm, .jsx, .kt, .kts, .mjs, .opal, .php, .podspec, .py, .rake, .rb, .rlib, .rs, .scala, .swift, .ts, .tsx, .vue]
1414
- name: pmd
1515
languages: [Java, JavaScript, JSP, Velocity, XML, Apex, Scala, Ruby, VisualForce]
16-
extensions: [.java, .js, .jsp, .vm, .xml, .cls, .trigger, .scala, .rb, .page, .component]
16+
extensions: [.gemspec, .java, .jbuilder, .js, .jsm, .jsx, .mjs, .opal, .podspec, .pom, .rake, .rb, .scala, .vue, .wsdl, .xml, .xsl]
1717
- name: pylint
1818
languages: [Python]
1919
extensions: [.py]
20+
- name: revive
21+
languages: [Go]
22+
extensions: [.go]
2023
- name: semgrep
2124
languages: [C, CPP, C#, Generic, Go, Java, JavaScript, JSON, Kotlin, Python, TypeScript, Ruby, Rust, JSX, PHP, Scala, Swift, Terraform]
22-
extensions: [.c, .cpp, .h, .hpp, .cs, .go, .java, .js, .json, .kt, .py, .ts, .rb, .rs, .jsx, .php, .scala, .swift, .tf, .tfvars]
25+
extensions: [.c, .cc, .cpp, .cxx, .gemspec, .go, .h, .hpp, .ino, .java, .jbuilder, .js, .jsm, .json, .jsx, .kt, .kts, .mjs, .opal, .php, .podspec, .py, .rake, .rb, .rlib, .rs, .scala, .swift, .tf, .ts, .tsx, .vue]
2326
- name: trivy
2427
languages: [Multiple]
2528
extensions: []

integration-tests/init-with-token/expected/tools-configs/languages-config.yaml

Lines changed: 21 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -1,19 +1,28 @@
11
tools:
2+
- name: codacy-enigma-cli
3+
languages: [Multiple]
4+
extensions: []
5+
- name: dartanalyzer
6+
languages: [Dart]
7+
extensions: [.dart]
8+
- name: eslint
9+
languages: [JavaScript, TypeScript, JSX, TSX]
10+
extensions: [.js, .jsm, .jsx, .mjs, .ts, .tsx, .vue]
11+
- name: lizard
12+
languages: [C, CPP, Java, C#, JavaScript, TypeScript, VueJS, Objective-C, Swift, Python, Ruby, TTCN-3, PHP, Scala, GDScript, Golang, Lua, Rust, Fortran, Kotlin, Solidity, Erlang, Zig, Perl]
13+
extensions: [.c, .cc, .cpp, .cxx, .gemspec, .go, .h, .hpp, .ino, .java, .jbuilder, .js, .jsm, .jsx, .kt, .kts, .mjs, .opal, .php, .podspec, .py, .rake, .rb, .rlib, .rs, .scala, .swift, .ts, .tsx, .vue]
14+
- name: pmd
15+
languages: [Java, JavaScript, JSP, Velocity, XML, Apex, Scala, Ruby, VisualForce]
16+
extensions: [.gemspec, .java, .jbuilder, .js, .jsm, .jsx, .mjs, .opal, .podspec, .pom, .rake, .rb, .scala, .vue, .wsdl, .xml, .xsl]
217
- name: pylint
318
languages: [Python]
419
extensions: [.py]
5-
- name: lizard
6-
languages: [Java, JavaScript, Python]
7-
extensions: [.java, .js, .jsm, .jsx, .mjs, .py, .vue]
8-
- name: pmd
9-
languages: [Java, JavaScript]
10-
extensions: [.java, .js, .jsm, .jsx, .mjs, .vue]
11-
- name: eslint
12-
languages: [JavaScript]
13-
extensions: [.js, .jsm, .jsx, .mjs, .vue]
20+
- name: revive
21+
languages: [Go]
22+
extensions: [.go]
23+
- name: semgrep
24+
languages: [C, CPP, C#, Generic, Go, Java, JavaScript, JSON, Kotlin, Python, TypeScript, Ruby, Rust, JSX, PHP, Scala, Swift, Terraform]
25+
extensions: [.c, .cc, .cpp, .cxx, .gemspec, .go, .h, .hpp, .ino, .java, .jbuilder, .js, .jsm, .json, .jsx, .kt, .kts, .mjs, .opal, .php, .podspec, .py, .rake, .rb, .rlib, .rs, .scala, .swift, .tf, .ts, .tsx, .vue]
1426
- name: trivy
1527
languages: [Multiple]
1628
extensions: []
17-
- name: semgrep
18-
languages: [Java, JavaScript, JSON, Python]
19-
extensions: [.java, .js, .jsm, .json, .jsx, .mjs, .py, .vue]
Lines changed: 18 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -1,25 +1,28 @@
11
tools:
2-
- name: pylint
3-
languages: [Python]
4-
extensions: [.py]
5-
- name: eslint
6-
languages: [JavaScript, TypeScript, JSX, TSX]
7-
extensions: [.js, .jsx, .ts, .tsx]
8-
- name: pmd
9-
languages: [Java, JavaScript, JSP, Velocity, XML, Apex, Scala, Ruby, VisualForce]
10-
extensions: [.java, .js, .jsp, .vm, .xml, .cls, .trigger, .scala, .rb, .page, .component]
11-
- name: trivy
2+
- name: codacy-enigma-cli
123
languages: [Multiple]
134
extensions: []
145
- name: dartanalyzer
156
languages: [Dart]
167
extensions: [.dart]
8+
- name: eslint
9+
languages: [JavaScript, TypeScript, JSX, TSX]
10+
extensions: [.js, .jsm, .jsx, .mjs, .ts, .tsx, .vue]
1711
- name: lizard
18-
languages: [C, CPP, Java, "C#", JavaScript, TypeScript, VueJS, "Objective-C", Swift, Python, Ruby, "TTCN-3", PHP, Scala, GDScript, Golang, Lua, Rust, Fortran, Kotlin, Solidity, Erlang, Zig, Perl]
19-
extensions: [.c, .cpp, .cc, .h, .hpp, .java, .cs, .js, .jsx, .ts, .tsx, .vue, .m, .swift, .py, .rb, .ttcn, .php, .scala, .gd, .go, .lua, .rs, .f, .f90, .kt, .sol, .erl, .zig, .pl]
12+
languages: [C, CPP, Java, C#, JavaScript, TypeScript, VueJS, Objective-C, Swift, Python, Ruby, TTCN-3, PHP, Scala, GDScript, Golang, Lua, Rust, Fortran, Kotlin, Solidity, Erlang, Zig, Perl]
13+
extensions: [.c, .cc, .cpp, .cxx, .gemspec, .go, .h, .hpp, .ino, .java, .jbuilder, .js, .jsm, .jsx, .kt, .kts, .mjs, .opal, .php, .podspec, .py, .rake, .rb, .rlib, .rs, .scala, .swift, .ts, .tsx, .vue]
14+
- name: pmd
15+
languages: [Java, JavaScript, JSP, Velocity, XML, Apex, Scala, Ruby, VisualForce]
16+
extensions: [.gemspec, .java, .jbuilder, .js, .jsm, .jsx, .mjs, .opal, .podspec, .pom, .rake, .rb, .scala, .vue, .wsdl, .xml, .xsl]
17+
- name: pylint
18+
languages: [Python]
19+
extensions: [.py]
20+
- name: revive
21+
languages: [Go]
22+
extensions: [.go]
2023
- name: semgrep
21-
languages: [C, CPP, "C#", Generic, Go, Java, JavaScript, JSON, Kotlin, Python, TypeScript, Ruby, Rust, JSX, PHP, Scala, Swift, Terraform]
22-
extensions: [.c, .cpp, .h, .hpp, .cs, .go, .java, .js, .json, .kt, .py, .ts, .rb, .rs, .jsx, .php, .scala, .swift, .tf, .tfvars]
23-
- name: codacy-enigma-cli
24+
languages: [C, CPP, C#, Generic, Go, Java, JavaScript, JSON, Kotlin, Python, TypeScript, Ruby, Rust, JSX, PHP, Scala, Swift, Terraform]
25+
extensions: [.c, .cc, .cpp, .cxx, .gemspec, .go, .h, .hpp, .ino, .java, .jbuilder, .js, .jsm, .json, .jsx, .kt, .kts, .mjs, .opal, .php, .podspec, .py, .rake, .rb, .rlib, .rs, .scala, .swift, .tf, .ts, .tsx, .vue]
26+
- name: trivy
2427
languages: [Multiple]
2528
extensions: []

tools/language_config.go

Lines changed: 71 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -68,6 +68,77 @@ func GetDefaultToolLanguageMapping() map[string]domain.ToolLanguageInfo {
6868
return DefaultToolLanguageMap
6969
}
7070

71+
// BuildLanguagesConfigFromAPI builds the tool language configuration from API data
72+
func BuildLanguagesConfigFromAPI() ([]domain.ToolLanguageInfo, error) {
73+
// Fetch language tools from API
74+
languageTools, err := codacyclient.GetLanguageTools()
75+
if err != nil {
76+
return nil, fmt.Errorf("failed to fetch language tools from API: %w", err)
77+
}
78+
79+
// Create a map of language names to file extensions from API
80+
languageExtensionsMap := make(map[string][]string)
81+
for _, langTool := range languageTools {
82+
languageExtensionsMap[strings.ToLower(langTool.Name)] = langTool.FileExtensions
83+
}
84+
85+
// Simple mapping: tool → real API languages it supports
86+
toolToAPILanguages := map[string][]string{
87+
"pylint": {"python"},
88+
"eslint": {"javascript", "typescript"},
89+
"pmd": {"java", "javascript", "jsp", "velocity", "xml", "apex", "scala", "ruby", "visualforce"},
90+
"trivy": {}, // Special case - works with multiple languages
91+
"dartanalyzer": {"dart"},
92+
"lizard": {"c", "cpp", "java", "csharp", "javascript", "typescript", "objective c", "swift", "python", "ruby", "php", "scala", "go", "rust", "fortran", "kotlin"},
93+
"semgrep": {"c", "cpp", "csharp", "go", "java", "javascript", "json", "kotlin", "python", "typescript", "ruby", "rust", "php", "scala", "swift", "terraform"},
94+
"codacy-enigma-cli": {}, // Special case - works with multiple languages
95+
"revive": {"go"},
96+
}
97+
98+
// Build tool configurations
99+
var configTools []domain.ToolLanguageInfo
100+
for _, toolInfo := range DefaultToolLanguageMap {
101+
// Get API languages this tool supports
102+
apiLanguages, exists := toolToAPILanguages[toolInfo.Name]
103+
if !exists {
104+
// Fallback to original configuration if tool not in mapping
105+
configTools = append(configTools, toolInfo)
106+
continue
107+
}
108+
109+
updatedTool := domain.ToolLanguageInfo{
110+
Name: toolInfo.Name,
111+
Languages: toolInfo.Languages, // Keep original language names for display
112+
Extensions: []string{},
113+
}
114+
115+
// Build extensions set from API data
116+
extensionsSet := make(map[string]struct{})
117+
for _, apiLang := range apiLanguages {
118+
if extensions, exists := languageExtensionsMap[apiLang]; exists {
119+
for _, ext := range extensions {
120+
extensionsSet[ext] = struct{}{}
121+
}
122+
}
123+
}
124+
125+
// Convert set to sorted slice
126+
for ext := range extensionsSet {
127+
updatedTool.Extensions = append(updatedTool.Extensions, ext)
128+
}
129+
slices.Sort(updatedTool.Extensions)
130+
131+
// If no extensions found from API, fallback to hardcoded extensions
132+
if len(updatedTool.Extensions) == 0 {
133+
updatedTool.Extensions = toolInfo.Extensions
134+
}
135+
136+
configTools = append(configTools, updatedTool)
137+
}
138+
139+
return configTools, nil
140+
}
141+
71142
// CreateLanguagesConfigFile creates languages-config.yaml based on API response
72143
func CreateLanguagesConfigFile(apiTools []domain.Tool, toolsConfigDir string, toolIDMap map[string]string, initFlags domain.InitFlags) error {
73144
// Build a list of tool language info for enabled tools

0 commit comments

Comments
 (0)