Skip to content

Commit d811240

Browse files
Merge pull request #93 from codacy/pluto-1402
feature: using API repo languages to create languages-config file - PLUTO-1402
2 parents 90d3e58 + 7eaf6f7 commit d811240

File tree

11 files changed

+432
-96
lines changed

11 files changed

+432
-96
lines changed

.codacy/codacy.yaml

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -3,9 +3,9 @@ runtimes:
33
44
55
tools:
6-
- eslint@9.3.0
6+
- eslint@8.57.0
77
88
99
10+
1011
11-

cmd/init.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -268,7 +268,7 @@ func buildRepositoryConfigurationFiles(token string) error {
268268
}
269269

270270
// Generate languages configuration based on API tools response
271-
if err := tools.CreateLanguagesConfigFile(apiTools, toolsConfigDir, uuidToName); err != nil {
271+
if err := tools.CreateLanguagesConfigFile(apiTools, toolsConfigDir, uuidToName, token, initFlags.provider, initFlags.organization, initFlags.repository); err != nil {
272272
return fmt.Errorf("failed to create languages configuration file: %w", err)
273273
}
274274

go.mod

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -34,7 +34,7 @@ require (
3434
github.com/nwaples/rardecode/v2 v2.0.0-beta.2 // indirect
3535
github.com/pierrec/lz4/v4 v4.1.15 // indirect
3636
github.com/spf13/pflag v1.0.5 // indirect
37-
github.com/stretchr/testify v1.9.0
37+
github.com/stretchr/testify v1.10.0
3838
github.com/therootcompany/xz v1.0.1 // indirect
3939
github.com/ulikunitz/xz v0.5.10 // indirect
4040
go4.org v0.0.0-20200411211856-f5505b9728dd // indirect

go.sum

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -129,6 +129,8 @@ github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81P
129129
github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
130130
github.com/stretchr/testify v1.9.0 h1:HtqpIVDClZ4nwg75+f6Lvsy/wHu+3BoSGCbBAcpTsTg=
131131
github.com/stretchr/testify v1.9.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY=
132+
github.com/stretchr/testify v1.10.0 h1:Xv5erBjTwe/5IxqUQTdXv5kgmIvbHo3QQyRwhJsOfJA=
133+
github.com/stretchr/testify v1.10.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY=
132134
github.com/therootcompany/xz v1.0.1 h1:CmOtsn1CbtmyYiusbfmhmkpAAETj0wBIH6kCYaX+xzw=
133135
github.com/therootcompany/xz v1.0.1/go.mod h1:3K3UH1yCKgBneZYhuQUvJ9HPD19UEXEI0BWbMn8qNMY=
134136
github.com/ulikunitz/xz v0.5.6/go.mod h1:2bypXElzHzzJZwzH67Y6wb67pO62Rzfn7BSiF4ABRW8=

tools/eslintRunner_test.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -25,7 +25,7 @@ func TestRunEslintToFile(t *testing.T) {
2525

2626
repositoryToAnalyze := filepath.Join(testDirectory, "src")
2727
expectedSarifFile := filepath.Join(testDirectory, "expected.sarif")
28-
eslintInstallationDirectory := filepath.Join(homeDirectory, ".cache/codacy/tools/eslint@9.3.0")
28+
eslintInstallationDirectory := filepath.Join(homeDirectory, ".cache/codacy/tools/eslint@8.57.0")
2929
nodeBinary := "node"
3030

3131
RunEslint(repositoryToAnalyze, eslintInstallationDirectory, nodeBinary, nil, false, tempResultFile, "sarif")

tools/language_config.go

Lines changed: 131 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,15 +1,23 @@
11
package tools
22

33
import (
4+
"encoding/json"
45
"fmt"
6+
"io"
7+
"net/http"
58
"os"
69
"path/filepath"
10+
"slices"
11+
"strings"
12+
"time"
713

814
"codacy/cli-v2/utils"
915

1016
"gopkg.in/yaml.v3"
1117
)
1218

19+
var CodacyApiBase = "https://app.codacy.com"
20+
1321
// ToolLanguageInfo contains language and extension information for a tool
1422
type ToolLanguageInfo struct {
1523
Name string `yaml:"name"`
@@ -23,7 +31,7 @@ type LanguagesConfig struct {
2331
}
2432

2533
// CreateLanguagesConfigFile creates languages-config.yaml based on API response
26-
func CreateLanguagesConfigFile(apiTools []Tool, toolsConfigDir string, toolIDMap map[string]string) error {
34+
func CreateLanguagesConfigFile(apiTools []Tool, toolsConfigDir string, toolIDMap map[string]string, apiToken string, provider string, organization string, repository string) error {
2735
// Map tool names to their language/extension information
2836
toolLanguageMap := map[string]ToolLanguageInfo{
2937
"cppcheck": {
@@ -61,6 +69,11 @@ func CreateLanguagesConfigFile(apiTools []Tool, toolsConfigDir string, toolIDMap
6169
// Build a list of tool language info for enabled tools
6270
var configTools []ToolLanguageInfo
6371

72+
repositoryLanguages, err := getRepositoryLanguages(apiToken, provider, organization, repository)
73+
if err != nil {
74+
return fmt.Errorf("failed to get repository languages: %w", err)
75+
}
76+
6477
for _, tool := range apiTools {
6578
shortName, exists := toolIDMap[tool.Uuid]
6679
if !exists {
@@ -71,7 +84,36 @@ func CreateLanguagesConfigFile(apiTools []Tool, toolsConfigDir string, toolIDMap
7184
// Get language info for this tool
7285
langInfo, exists := toolLanguageMap[shortName]
7386
if exists {
74-
configTools = append(configTools, langInfo)
87+
// Special case for Trivy - always include it
88+
if shortName == "trivy" {
89+
configTools = append(configTools, langInfo)
90+
continue
91+
}
92+
93+
// Filter languages based on repository languages
94+
var filteredLanguages []string
95+
var filteredExtensionsSet = make(map[string]struct{})
96+
for _, lang := range langInfo.Languages {
97+
lowerLang := strings.ToLower(lang)
98+
if extensions, exists := repositoryLanguages[lowerLang]; exists && len(extensions) > 0 {
99+
filteredLanguages = append(filteredLanguages, lang)
100+
for _, ext := range extensions {
101+
filteredExtensionsSet[ext] = struct{}{}
102+
}
103+
}
104+
}
105+
filteredExtensions := make([]string, 0, len(filteredExtensionsSet))
106+
for ext := range filteredExtensionsSet {
107+
filteredExtensions = append(filteredExtensions, ext)
108+
}
109+
slices.Sort(filteredExtensions)
110+
langInfo.Languages = filteredLanguages
111+
langInfo.Extensions = filteredExtensions
112+
113+
// Only add tool if it has languages that exist in the repository
114+
if len(filteredLanguages) > 0 {
115+
configTools = append(configTools, langInfo)
116+
}
75117
}
76118
}
77119

@@ -102,3 +144,90 @@ func CreateLanguagesConfigFile(apiTools []Tool, toolsConfigDir string, toolIDMap
102144
fmt.Println("Created languages configuration file based on enabled tools")
103145
return nil
104146
}
147+
148+
func getRepositoryLanguages(apiToken string, provider string, organization string, repository string) (map[string][]string, error) {
149+
client := &http.Client{
150+
Timeout: 10 * time.Second,
151+
}
152+
153+
url := fmt.Sprintf("%s/api/v3/organizations/%s/%s/repositories/%s/settings/languages",
154+
CodacyApiBase,
155+
provider,
156+
organization,
157+
repository)
158+
159+
// Create a new GET request
160+
req, err := http.NewRequest("GET", url, nil)
161+
if err != nil {
162+
return nil, fmt.Errorf("failed to create request: %w", err)
163+
}
164+
165+
// Set the API token header
166+
req.Header.Set("api-token", apiToken)
167+
168+
// Send the request
169+
resp, err := client.Do(req)
170+
if err != nil {
171+
return nil, fmt.Errorf("failed to send request: %w", err)
172+
}
173+
defer resp.Body.Close()
174+
175+
if resp.StatusCode != 200 {
176+
return nil, fmt.Errorf("failed to get repository languages: status code %d", resp.StatusCode)
177+
}
178+
179+
// Read the response body
180+
body, err := io.ReadAll(resp.Body)
181+
if err != nil {
182+
return nil, fmt.Errorf("failed to read response body: %w", err)
183+
}
184+
185+
// Define the response structure
186+
type LanguageResponse struct {
187+
Name string `json:"name"`
188+
CodacyDefaults []string `json:"codacyDefaults"`
189+
Extensions []string `json:"extensions"`
190+
Enabled bool `json:"enabled"`
191+
Detected bool `json:"detected"`
192+
}
193+
194+
type LanguagesResponse struct {
195+
Languages []LanguageResponse `json:"languages"`
196+
}
197+
198+
var response LanguagesResponse
199+
if err := json.Unmarshal(body, &response); err != nil {
200+
return nil, fmt.Errorf("failed to unmarshal response: %w", err)
201+
}
202+
203+
// Create map to store language name -> combined extensions
204+
result := make(map[string][]string)
205+
206+
// Filter and process languages
207+
for _, lang := range response.Languages {
208+
if lang.Enabled && lang.Detected {
209+
// Combine and deduplicate extensions
210+
extensions := make(map[string]struct{})
211+
for _, ext := range lang.CodacyDefaults {
212+
extensions[ext] = struct{}{}
213+
}
214+
for _, ext := range lang.Extensions {
215+
extensions[ext] = struct{}{}
216+
}
217+
218+
// Convert map to slice
219+
extSlice := make([]string, 0, len(extensions))
220+
for ext := range extensions {
221+
extSlice = append(extSlice, ext)
222+
}
223+
224+
// Sort extensions for consistent ordering in the config file
225+
slices.Sort(extSlice)
226+
227+
// Add to result map with lowercase key for case-insensitive matching
228+
result[strings.ToLower(lang.Name)] = extSlice
229+
}
230+
}
231+
232+
return result, nil
233+
}

0 commit comments

Comments
 (0)