Skip to content

Commit 7039d67

Browse files
fix: languages-config.yaml is created by init command
- Added language configuration file generation during init using API data - Created YAML config in compact format mapping tools to supported languages - Moved language config generation to dedicated file in tools package - Tools now check file extensions during analyze to skip unsupported languages This prevents tools from running on unsupported file types (e.g., pylint on C++ files).
1 parent 5b38545 commit 7039d67

File tree

4 files changed

+233
-2
lines changed

4 files changed

+233
-2
lines changed

cmd/analyze.go

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -120,8 +120,9 @@ func IsToolSupportedForFile(toolName string, filePath string, langConfig *Langua
120120

121121
// FilterToolsByLanguageSupport filters tools by language support for the given files
122122
func FilterToolsByLanguageSupport(tools map[string]*plugins.ToolInfo, files []string) map[string]*plugins.ToolInfo {
123-
if len(files) == 0 {
124-
// If no files specified, return all tools
123+
124+
if len(files) == 0 || files[0] == "." {
125+
// If no files specified or current directory, return all tools
125126
return tools
126127
}
127128

cmd/init.go

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -255,6 +255,20 @@ func buildRepositoryConfigurationFiles(token string) error {
255255
return err
256256
}
257257

258+
// Map UUID to tool shortname for lookup
259+
uuidToName := map[string]string{
260+
ESLint: "eslint",
261+
Trivy: "trivy",
262+
PyLint: "pylint",
263+
PMD: "pmd",
264+
DartAnalyzer: "dartanalyzer",
265+
}
266+
267+
// Generate languages configuration based on API tools response
268+
if err := tools.CreateLanguagesConfigFile(apiTools, toolsConfigDir, uuidToName); err != nil {
269+
return fmt.Errorf("failed to create languages configuration file: %w", err)
270+
}
271+
258272
// Filter out any tools that use configuration file
259273
configuredToolsWithUI := tools.FilterToolsByConfigUsage(apiTools)
260274

tools/language_config.go

Lines changed: 104 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,104 @@
1+
package tools
2+
3+
import (
4+
"fmt"
5+
"os"
6+
"path/filepath"
7+
8+
"codacy/cli-v2/utils"
9+
10+
"gopkg.in/yaml.v3"
11+
)
12+
13+
// ToolLanguageInfo contains language and extension information for a tool
14+
type ToolLanguageInfo struct {
15+
Name string `yaml:"name"`
16+
Languages []string `yaml:"languages,flow"`
17+
Extensions []string `yaml:"extensions,flow"`
18+
}
19+
20+
// LanguagesConfig represents the structure of the languages configuration file
21+
type LanguagesConfig struct {
22+
Tools []ToolLanguageInfo `yaml:"tools"`
23+
}
24+
25+
// CreateLanguagesConfigFile creates languages-config.yaml based on API response
26+
func CreateLanguagesConfigFile(apiTools []Tool, toolsConfigDir string, toolIDMap map[string]string) error {
27+
// Map tool names to their language/extension information
28+
toolLanguageMap := map[string]ToolLanguageInfo{
29+
"cppcheck": {
30+
Name: "cppcheck",
31+
Languages: []string{"C", "CPP"},
32+
Extensions: []string{".c", ".cpp", ".cc", ".h", ".hpp"},
33+
},
34+
"pylint": {
35+
Name: "pylint",
36+
Languages: []string{"Python"},
37+
Extensions: []string{".py"},
38+
},
39+
"eslint": {
40+
Name: "eslint",
41+
Languages: []string{"JavaScript", "TypeScript", "JSX", "TSX"},
42+
Extensions: []string{".js", ".jsx", ".ts", ".tsx"},
43+
},
44+
"pmd": {
45+
Name: "pmd",
46+
Languages: []string{"Java", "JavaScript", "JSP", "Velocity", "XML", "Apex", "Scala", "Ruby", "VisualForce"},
47+
Extensions: []string{".java", ".js", ".jsp", ".vm", ".xml", ".cls", ".trigger", ".scala", ".rb", ".page", ".component"},
48+
},
49+
"trivy": {
50+
Name: "trivy",
51+
Languages: []string{"Multiple"},
52+
Extensions: []string{},
53+
},
54+
"dartanalyzer": {
55+
Name: "dartanalyzer",
56+
Languages: []string{"Dart"},
57+
Extensions: []string{".dart"},
58+
},
59+
}
60+
61+
// Build a list of tool language info for enabled tools
62+
var configTools []ToolLanguageInfo
63+
64+
for _, tool := range apiTools {
65+
shortName, exists := toolIDMap[tool.Uuid]
66+
if !exists {
67+
// Skip tools we don't recognize
68+
continue
69+
}
70+
71+
// Get language info for this tool
72+
langInfo, exists := toolLanguageMap[shortName]
73+
if exists {
74+
configTools = append(configTools, langInfo)
75+
}
76+
}
77+
78+
// If we have no tools or couldn't match any, include all known tools
79+
if len(configTools) == 0 {
80+
for _, langInfo := range toolLanguageMap {
81+
configTools = append(configTools, langInfo)
82+
}
83+
}
84+
85+
// Create the config structure
86+
config := LanguagesConfig{
87+
Tools: configTools,
88+
}
89+
90+
// Marshal to YAML
91+
data, err := yaml.Marshal(config)
92+
if err != nil {
93+
return fmt.Errorf("failed to marshal languages config to YAML: %w", err)
94+
}
95+
96+
// Write the file
97+
configPath := filepath.Join(toolsConfigDir, "languages-config.yaml")
98+
if err := os.WriteFile(configPath, data, utils.DefaultFilePerms); err != nil {
99+
return fmt.Errorf("failed to write languages config file: %w", err)
100+
}
101+
102+
fmt.Println("Created languages configuration file based on enabled tools")
103+
return nil
104+
}

tools/language_config_test.go

Lines changed: 112 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,112 @@
1+
package tools
2+
3+
import (
4+
"os"
5+
"path/filepath"
6+
"strings"
7+
"testing"
8+
)
9+
10+
func TestCreateLanguagesConfigFile(t *testing.T) {
11+
// Create a temporary directory for test
12+
tempDir, err := os.MkdirTemp("", "codacy-test-*")
13+
if err != nil {
14+
t.Fatalf("Failed to create temp dir: %v", err)
15+
}
16+
defer os.RemoveAll(tempDir)
17+
18+
// Define test tool IDs
19+
const (
20+
testESLintID = "eslint-id"
21+
testPyLintID = "pylint-id"
22+
testTrivyID = "trivy-id"
23+
testPMDID = "pmd-id"
24+
testDartAnalyzerID = "dartanalyzer-id"
25+
)
26+
27+
// Create a map of tool IDs to names
28+
toolIDMap := map[string]string{
29+
testESLintID: "eslint",
30+
testPyLintID: "pylint",
31+
testTrivyID: "trivy",
32+
testPMDID: "pmd",
33+
testDartAnalyzerID: "dartanalyzer",
34+
}
35+
36+
// Create mock tool data
37+
mockTools := []Tool{
38+
{
39+
Uuid: testPyLintID, // Pylint
40+
Name: "Pylint",
41+
},
42+
{
43+
Uuid: testESLintID, // ESLint
44+
Name: "ESLint",
45+
},
46+
}
47+
48+
// Call the function under test
49+
err = CreateLanguagesConfigFile(mockTools, tempDir, toolIDMap)
50+
if err != nil {
51+
t.Fatalf("CreateLanguagesConfigFile failed: %v", err)
52+
}
53+
54+
// Verify the file was created
55+
configPath := filepath.Join(tempDir, "languages-config.yaml")
56+
if _, err := os.Stat(configPath); os.IsNotExist(err) {
57+
t.Fatalf("languages-config.yaml was not created")
58+
}
59+
60+
// Read the file content
61+
data, err := os.ReadFile(configPath)
62+
if err != nil {
63+
t.Fatalf("Failed to read generated file: %v", err)
64+
}
65+
66+
// Check content has expected tools
67+
content := string(data)
68+
if !strings.Contains(content, "name: pylint") {
69+
t.Errorf("Expected pylint in config, but it was not found")
70+
}
71+
if !strings.Contains(content, "name: eslint") {
72+
t.Errorf("Expected eslint in config, but it was not found")
73+
}
74+
75+
// Verify other tools are not included
76+
if strings.Contains(content, "name: trivy") {
77+
t.Errorf("Unexpected trivy in config")
78+
}
79+
if strings.Contains(content, "name: pmd") {
80+
t.Errorf("Unexpected pmd in config")
81+
}
82+
83+
// Check for flow style arrays
84+
if !strings.Contains(content, "languages: [") {
85+
t.Errorf("Expected flow-style array for languages, but not found")
86+
}
87+
if !strings.Contains(content, "extensions: [") {
88+
t.Errorf("Expected flow-style array for extensions, but not found")
89+
}
90+
91+
// Test with no tools - should include all tools
92+
emptyTools := []Tool{}
93+
err = CreateLanguagesConfigFile(emptyTools, tempDir, toolIDMap)
94+
if err != nil {
95+
t.Fatalf("CreateLanguagesConfigFile failed with empty tools: %v", err)
96+
}
97+
98+
// Read the file again
99+
data, err = os.ReadFile(configPath)
100+
if err != nil {
101+
t.Fatalf("Failed to read generated file: %v", err)
102+
}
103+
104+
// With empty tools, all tools should be included
105+
content = string(data)
106+
for toolName := range toolIDMap {
107+
shortName := toolIDMap[toolName]
108+
if !strings.Contains(content, "name: "+shortName) {
109+
t.Errorf("Expected %s in config when no tools specified, but it was not found", shortName)
110+
}
111+
}
112+
}

0 commit comments

Comments
 (0)