@@ -123,6 +123,8 @@ func configFileTemplate(tools []domain.Tool) string {
123123 toolsMap := make (map [string ]bool )
124124 toolVersions := make (map [string ]string )
125125
126+ toolsWithLatestVersion , _ , _ := KeepToolsWithLatestVersion (tools )
127+
126128 // Track needed runtimes
127129 needsNode := false
128130 needsPython := false
@@ -131,17 +133,17 @@ func configFileTemplate(tools []domain.Tool) string {
131133
132134 // Default versions
133135 defaultVersions := map [string ]string {
134- ESLint : "8.57.0" ,
135- Trivy : "0.59.1" ,
136- PyLint : "3.3.6" ,
137- PMD : "6.55.0" ,
138- DartAnalyzer : "3.7.2" ,
139- Semgrep : "1.78.0" ,
140- Lizard : "1.17.19" ,
136+ domain . ESLint : "8.57.0" ,
137+ domain . Trivy : "0.59.1" ,
138+ domain . PyLint : "3.3.6" ,
139+ domain . PMD : "6.55.0" ,
140+ domain . DartAnalyzer : "3.7.2" ,
141+ domain . Semgrep : "1.78.0" ,
142+ domain . Lizard : "1.17.19" ,
141143 }
142144
143145 // Build map of enabled tools with their versions
144- for _ , tool := range tools {
146+ for _ , tool := range toolsWithLatestVersion {
145147 toolsMap [tool .Uuid ] = true
146148 if tool .Version != "" {
147149 toolVersions [tool .Uuid ] = tool .Version
@@ -150,13 +152,14 @@ func configFileTemplate(tools []domain.Tool) string {
150152 }
151153
152154 // Check if tool needs a runtime
153- if tool .Uuid == ESLint {
155+ switch tool .Uuid {
156+ case domain .ESLint , domain .ESLint9 :
154157 needsNode = true
155- } else if tool . Uuid == PyLint || tool . Uuid == Lizard {
158+ case domain . PyLint , domain . Lizard :
156159 needsPython = true
157- } else if tool . Uuid == DartAnalyzer {
160+ case domain . DartAnalyzer :
158161 needsDart = true
159- } else if tool . Uuid == PMD {
162+ case domain . PMD , domain . PMD7 :
160163 needsJava = true
161164 }
162165 }
@@ -191,31 +194,21 @@ func configFileTemplate(tools []domain.Tool) string {
191194
192195 // If we have tools from the API (enabled tools), use only those
193196 if len (tools ) > 0 {
194- // Add only the tools that are in the API response (enabled tools)
195- uuidToName := map [string ]string {
196- ESLint : "eslint" ,
197- Trivy : "trivy" ,
198- PyLint : "pylint" ,
199- PMD : "pmd" ,
200- DartAnalyzer : "dartanalyzer" ,
201- Semgrep : "semgrep" ,
202- Lizard : "lizard" ,
203- }
204-
205- for uuid , name := range uuidToName {
197+ for uuid , meta := range domain .SupportedToolsMetadata {
206198 if toolsMap [uuid ] {
207- sb .WriteString (fmt .Sprintf (" - %s@%s\n " , name , toolVersions [uuid ]))
199+ sb .WriteString (fmt .Sprintf (" - %s@%s\n " , meta . Name , toolVersions [uuid ]))
208200 }
209201 }
202+
210203 } else {
211204 // If no tools were specified (local mode), include all defaults
212- sb .WriteString (fmt .Sprintf (" - eslint@%s\n " , defaultVersions [ESLint ]))
213- sb .WriteString (fmt .Sprintf (" - trivy@%s\n " , defaultVersions [Trivy ]))
214- sb .WriteString (fmt .Sprintf (" - pylint@%s\n " , defaultVersions [PyLint ]))
215- sb .WriteString (fmt .Sprintf (" - pmd@%s\n " , defaultVersions [PMD ]))
216- sb .WriteString (fmt .Sprintf (" - dartanalyzer@%s\n " , defaultVersions [DartAnalyzer ]))
217- sb .WriteString (fmt .Sprintf (" - semgrep@%s\n " , defaultVersions [Semgrep ]))
218- sb .WriteString (fmt .Sprintf (" - lizard@%s\n " , defaultVersions [Lizard ]))
205+ sb .WriteString (fmt .Sprintf (" - eslint@%s\n " , defaultVersions [domain . ESLint ]))
206+ sb .WriteString (fmt .Sprintf (" - trivy@%s\n " , defaultVersions [domain . Trivy ]))
207+ sb .WriteString (fmt .Sprintf (" - pylint@%s\n " , defaultVersions [domain . PyLint ]))
208+ sb .WriteString (fmt .Sprintf (" - pmd@%s\n " , defaultVersions [domain . PMD ]))
209+ sb .WriteString (fmt .Sprintf (" - dartanalyzer@%s\n " , defaultVersions [domain . DartAnalyzer ]))
210+ sb .WriteString (fmt .Sprintf (" - semgrep@%s\n " , defaultVersions [domain . Semgrep ]))
211+ sb .WriteString (fmt .Sprintf (" - lizard@%s\n " , defaultVersions [domain . Lizard ]))
219212 }
220213
221214 return sb .String ()
@@ -253,27 +246,31 @@ func buildRepositoryConfigurationFiles(token string) error {
253246 return err
254247 }
255248
256- // Map UUID to tool shortname for lookup
257- uuidToName := map [string ]string {
258- ESLint : "eslint" ,
259- Trivy : "trivy" ,
260- PyLint : "pylint" ,
261- PMD : "pmd" ,
262- DartAnalyzer : "dartanalyzer" ,
263- Lizard : "lizard" ,
264- Semgrep : "semgrep" ,
249+ toolsWithLatestVersion , uuidToName , familyToVersions := KeepToolsWithLatestVersion (apiTools )
250+
251+ for family , versions := range familyToVersions {
252+ if len (versions ) > 1 {
253+ kept := ", "
254+ for _ , tool := range toolsWithLatestVersion {
255+ if domain .SupportedToolsMetadata [tool .Uuid ].Name == family {
256+ kept = tool .Version
257+ break
258+ }
259+ }
260+ fmt .Printf ("⚠️ Multiple versions of '%s' detected: [%s], keeping %s\n " , family , strings .Join (versions , ", " ), kept )
261+ }
265262 }
266263
267264 // Generate languages configuration based on API tools response
268- if err := tools .CreateLanguagesConfigFile (apiTools , toolsConfigDir , uuidToName , initFlags ); err != nil {
265+ if err := tools .CreateLanguagesConfigFile (toolsWithLatestVersion , toolsConfigDir , uuidToName , initFlags ); err != nil {
269266 return fmt .Errorf ("failed to create languages configuration file: %w" , err )
270267 }
271268
272269 // Filter out any tools that use configuration file
273- configuredToolsWithUI := tools .FilterToolsByConfigUsage (apiTools )
270+ configuredToolsWithUI := tools .FilterToolsByConfigUsage (toolsWithLatestVersion )
274271
275272 // Create main config files with all enabled API tools
276- err = createConfigurationFiles (apiTools , false )
273+ err = createConfigurationFiles (toolsWithLatestVersion , false )
277274 if err != nil {
278275 log .Fatal (err )
279276 }
@@ -298,43 +295,48 @@ func buildRepositoryConfigurationFiles(token string) error {
298295func createToolFileConfigurations (tool domain.Tool , patternConfiguration []domain.PatternConfiguration ) error {
299296 toolsConfigDir := config .Config .ToolsConfigDirectory ()
300297 switch tool .Uuid {
301- case ESLint :
298+ case domain . ESLint , domain . ESLint9 :
302299 err := tools .CreateEslintConfig (toolsConfigDir , patternConfiguration )
303300 if err != nil {
304301 return fmt .Errorf ("failed to write eslint config: %v" , err )
305302 }
306303 fmt .Println ("ESLint configuration created based on Codacy settings. Ignoring plugin rules. ESLint plugins are not supported yet." )
307- case Trivy :
304+ case domain . Trivy :
308305 err := createTrivyConfigFile (patternConfiguration , toolsConfigDir )
309306 if err != nil {
310307 return fmt .Errorf ("failed to create Trivy config: %v" , err )
311308 }
312309 fmt .Println ("Trivy configuration created based on Codacy settings" )
313- case PMD :
310+ case domain . PMD :
314311 err := createPMDConfigFile (patternConfiguration , toolsConfigDir )
315312 if err != nil {
316313 return fmt .Errorf ("failed to create PMD config: %v" , err )
317314 }
318- fmt .Println ("PMD configuration created based on Codacy settings" )
319- case PyLint :
315+ case domain .PMD7 :
316+ err := createPMD7ConfigFile (patternConfiguration , toolsConfigDir )
317+ if err != nil {
318+ return fmt .Errorf ("failed to create PMD7 config: %v" , err )
319+ }
320+ fmt .Println ("PMD7 configuration created based on Codacy settings" )
321+ case domain .PyLint :
320322 err := createPylintConfigFile (patternConfiguration , toolsConfigDir )
321323 if err != nil {
322324 return fmt .Errorf ("failed to create Pylint config: %v" , err )
323325 }
324326 fmt .Println ("Pylint configuration created based on Codacy settings" )
325- case DartAnalyzer :
327+ case domain . DartAnalyzer :
326328 err := createDartAnalyzerConfigFile (patternConfiguration , toolsConfigDir )
327329 if err != nil {
328330 return fmt .Errorf ("failed to create Dart Analyzer config: %v" , err )
329331 }
330332 fmt .Println ("Dart configuration created based on Codacy settings" )
331- case Semgrep :
333+ case domain . Semgrep :
332334 err := createSemgrepConfigFile (patternConfiguration , toolsConfigDir )
333335 if err != nil {
334336 return fmt .Errorf ("failed to create Semgrep config: %v" , err )
335337 }
336338 fmt .Println ("Semgrep configuration created based on Codacy settings" )
337- case Lizard :
339+ case domain . Lizard :
338340 err := createLizardConfigFile (toolsConfigDir , patternConfiguration )
339341 if err != nil {
340342 return fmt .Errorf ("failed to create Lizard config: %v" , err )
@@ -345,7 +347,12 @@ func createToolFileConfigurations(tool domain.Tool, patternConfiguration []domai
345347}
346348
347349func createPMDConfigFile (config []domain.PatternConfiguration , toolsConfigDir string ) error {
348- pmdConfigurationString := tools .CreatePmdConfig (config )
350+ pmdConfigurationString := tools .CreatePmd6Config (config )
351+ return os .WriteFile (filepath .Join (toolsConfigDir , "ruleset.xml" ), []byte (pmdConfigurationString ), utils .DefaultFilePerms )
352+ }
353+
354+ func createPMD7ConfigFile (config []domain.PatternConfiguration , toolsConfigDir string ) error {
355+ pmdConfigurationString := tools .CreatePmd7Config (config )
349356 return os .WriteFile (filepath .Join (toolsConfigDir , "ruleset.xml" ), []byte (pmdConfigurationString ), utils .DefaultFilePerms )
350357}
351358
@@ -429,37 +436,39 @@ func createLizardConfigFile(toolsConfigDir string, patternConfiguration []domain
429436
430437// buildDefaultConfigurationFiles creates default configuration files for all tools
431438func buildDefaultConfigurationFiles (toolsConfigDir string ) error {
432- for _ , tool := range AvailableTools {
439+ for tool := range domain . SupportedToolsMetadata {
433440 patternsConfig , err := codacyclient .GetDefaultToolPatternsConfig (initFlags , tool )
434441 if err != nil {
435442 return fmt .Errorf ("failed to get default tool patterns config: %w" , err )
436443 }
437444 switch tool {
438- case ESLint :
445+ case domain . ESLint :
439446 if err := tools .CreateEslintConfig (toolsConfigDir , patternsConfig ); err != nil {
440447 return fmt .Errorf ("failed to create eslint config file: %v" , err )
441448 }
442- case Trivy :
449+ case domain . Trivy :
443450 if err := createTrivyConfigFile (patternsConfig , toolsConfigDir ); err != nil {
444451 return fmt .Errorf ("failed to create default Trivy configuration: %w" , err )
445452 }
446- case PMD :
453+ case domain . PMD :
447454 if err := createPMDConfigFile (patternsConfig , toolsConfigDir ); err != nil {
448455 return fmt .Errorf ("failed to create default PMD configuration: %w" , err )
449456 }
450- case PyLint :
457+ case domain .PMD7 , domain .ESLint9 :
458+ continue
459+ case domain .PyLint :
451460 if err := createPylintConfigFile (patternsConfig , toolsConfigDir ); err != nil {
452461 return fmt .Errorf ("failed to create default Pylint configuration: %w" , err )
453462 }
454- case DartAnalyzer :
463+ case domain . DartAnalyzer :
455464 if err := createDartAnalyzerConfigFile (patternsConfig , toolsConfigDir ); err != nil {
456465 return fmt .Errorf ("failed to create default Dart Analyzer configuration: %w" , err )
457466 }
458- case Semgrep :
467+ case domain . Semgrep :
459468 if err := createSemgrepConfigFile (patternsConfig , toolsConfigDir ); err != nil {
460469 return fmt .Errorf ("failed to create default Semgrep configuration: %w" , err )
461470 }
462- case Lizard :
471+ case domain . Lizard :
463472 if err := createLizardConfigFile (toolsConfigDir , patternsConfig ); err != nil {
464473 return fmt .Errorf ("failed to create default Lizard configuration: %w" , err )
465474 }
@@ -468,23 +477,50 @@ func buildDefaultConfigurationFiles(toolsConfigDir string) error {
468477 return nil
469478}
470479
471- const (
472- ESLint string = "f8b29663-2cb2-498d-b923-a10c6a8c05cd"
473- Trivy string = "2fd7fbe0-33f9-4ab3-ab73-e9b62404e2cb"
474- PMD string = "9ed24812-b6ee-4a58-9004-0ed183c45b8f"
475- PyLint string = "31677b6d-4ae0-4f56-8041-606a8d7a8e61"
476- DartAnalyzer string = "d203d615-6cf1-41f9-be5f-e2f660f7850f"
477- Semgrep string = "6792c561-236d-41b7-ba5e-9d6bee0d548b"
478- Lizard string = "76348462-84b3-409a-90d3-955e90abfb87"
479- )
480+ // KeepToolsWithLatestVersion filters the tools to keep only the latest version of each tool family.
481+ func KeepToolsWithLatestVersion (tools []domain.Tool ) (
482+ toolsWithLatestVersion []domain.Tool ,
483+ uuidToName map [string ]string ,
484+ familyToVersions map [string ][]string ,
485+ ) {
486+ latestTools := map [string ]domain.Tool {}
487+ uuidToName = map [string ]string {}
488+ seen := map [string ][]domain.Tool {}
489+ familyToVersions = map [string ][]string {}
490+
491+ for _ , tool := range tools {
492+ meta , ok := domain .SupportedToolsMetadata [tool .Uuid ]
493+ if ! ok {
494+ continue
495+ }
496+
497+ // Track all tools seen per family
498+ seen [meta .Name ] = append (seen [meta .Name ], tool )
499+
500+ // Pick the best version
501+ current , exists := latestTools [meta .Name ]
502+ if ! exists || domain .SupportedToolsMetadata [current .Uuid ].Priority > meta .Priority {
503+ latestTools [meta .Name ] = tool
504+ uuidToName [tool .Uuid ] = meta .Name
505+ }
506+ }
507+
508+ // Populate final list and version map for logging
509+ for family , tools := range seen {
510+ var versions []string
511+ for _ , t := range tools {
512+ v := t .Version
513+ if v == "" {
514+ v = "(unknown)"
515+ }
516+ versions = append (versions , v )
517+ }
518+ familyToVersions [family ] = versions
519+ }
520+
521+ for _ , tool := range latestTools {
522+ toolsWithLatestVersion = append (toolsWithLatestVersion , tool )
523+ }
480524
481- // AvailableTools lists all tool UUIDs supported by Codacy CLI.
482- var AvailableTools = []string {
483- ESLint ,
484- Trivy ,
485- PMD ,
486- PyLint ,
487- DartAnalyzer ,
488- Semgrep ,
489- Lizard ,
525+ return
490526}
0 commit comments