@@ -153,17 +153,6 @@ func createConfigurationFiles(tools []domain.Tool, cliLocalMode bool) error {
153153 return nil
154154}
155155
156- // Map tool UUIDs to their names
157- var toolNameMap = map [string ]string {
158- ESLint : "eslint" ,
159- Trivy : "trivy" ,
160- PyLint : "pylint" ,
161- PMD : "pmd" ,
162- DartAnalyzer : "dartanalyzer" ,
163- Semgrep : "semgrep" ,
164- Lizard : "lizard" ,
165- }
166-
167156// RuntimePluginConfig holds the structure of the runtime plugin.yaml file
168157type RuntimePluginConfig struct {
169158 Name string `yaml:"name"`
@@ -176,6 +165,8 @@ func configFileTemplate(tools []domain.Tool) string {
176165 toolsMap := make (map [string ]bool )
177166 toolVersions := make (map [string ]string )
178167
168+ toolsWithLatestVersion , _ , _ := KeepToolsWithLatestVersion (tools )
169+
179170 // Track needed runtimes
180171 neededRuntimes := make (map [string ]bool )
181172
@@ -189,23 +180,23 @@ func configFileTemplate(tools []domain.Tool) string {
189180 runtimeDependencies := plugins .GetToolRuntimeDependencies ()
190181
191182 // Build map of enabled tools with their versions
192- for _ , tool := range tools {
183+ for _ , tool := range toolsWithLatestVersion {
193184 toolsMap [tool .Uuid ] = true
194185 if tool .Version != "" {
195186 toolVersions [tool .Uuid ] = tool .Version
196187 } else {
197- toolName := toolNameMap [tool .Uuid ]
198- if defaultVersion , ok := defaultVersions [toolName ]; ok {
199- toolVersions [tool .Uuid ] = defaultVersion
188+ if meta , ok := domain .SupportedToolsMetadata [tool .Uuid ]; ok {
189+ if defaultVersion , ok := defaultVersions [meta .Name ]; ok {
190+ toolVersions [tool .Uuid ] = defaultVersion
191+ }
200192 }
201193 }
202194
203195 // Get the tool's runtime dependency
204- toolName := toolNameMap [tool .Uuid ]
205- if toolName != "" {
206- if runtime , ok := runtimeDependencies [toolName ]; ok {
196+ if meta , ok := domain .SupportedToolsMetadata [tool .Uuid ]; ok {
197+ if runtime , ok := runtimeDependencies [meta .Name ]; ok {
207198 // Handle special case for dartanalyzer which can use either dart or flutter
208- if toolName == "dartanalyzer" {
199+ if meta . Name == "dartanalyzer" {
209200 // For now, default to dart runtime
210201 neededRuntimes ["dart" ] = true
211202 } else {
@@ -250,38 +241,33 @@ func configFileTemplate(tools []domain.Tool) string {
250241 }
251242 }
252243 }
253-
254- // Create a sorted slice of runtimes
255244 var sortedRuntimes []string
256245 for runtime := range neededRuntimes {
257246 sortedRuntimes = append (sortedRuntimes , runtime )
258247 }
259248 sort .Strings (sortedRuntimes )
260-
261- // Write sorted runtimes
262249 for _ , runtime := range sortedRuntimes {
263250 sb .WriteString (fmt .Sprintf (" - %s@%s\n " , runtime , runtimeVersions [runtime ]))
264251 }
265252 }
266253
267254 sb .WriteString ("tools:\n " )
268255
269- // If we have tools from the API (enabled tools), use only those
270256 if len (tools ) > 0 {
271257 // Create a sorted slice of tool names
272258 var sortedTools []string
273- for uuid , name := range toolNameMap {
259+ for uuid , meta := range domain . SupportedToolsMetadata {
274260 if toolsMap [uuid ] {
275- sortedTools = append (sortedTools , name )
261+ sortedTools = append (sortedTools , meta . Name )
276262 }
277263 }
278264 sort .Strings (sortedTools )
279265
280266 // Write sorted tools
281267 for _ , name := range sortedTools {
282268 // Find the UUID for this tool name to get its version
283- for uuid , toolName := range toolNameMap {
284- if toolName == name && toolsMap [uuid ] {
269+ for uuid , meta := range domain . SupportedToolsMetadata {
270+ if meta . Name == name && toolsMap [uuid ] {
285271 version := toolVersions [uuid ]
286272 sb .WriteString (fmt .Sprintf (" - %s@%s\n " , name , version ))
287273 break
@@ -353,27 +339,31 @@ func buildRepositoryConfigurationFiles(token string) error {
353339 return err
354340 }
355341
356- // Map UUID to tool shortname for lookup
357- uuidToName := map [string ]string {
358- ESLint : "eslint" ,
359- Trivy : "trivy" ,
360- PyLint : "pylint" ,
361- PMD : "pmd" ,
362- DartAnalyzer : "dartanalyzer" ,
363- Lizard : "lizard" ,
364- Semgrep : "semgrep" ,
342+ toolsWithLatestVersion , uuidToName , familyToVersions := KeepToolsWithLatestVersion (apiTools )
343+
344+ for family , versions := range familyToVersions {
345+ if len (versions ) > 1 {
346+ kept := ", "
347+ for _ , tool := range toolsWithLatestVersion {
348+ if domain .SupportedToolsMetadata [tool .Uuid ].Name == family {
349+ kept = tool .Version
350+ break
351+ }
352+ }
353+ fmt .Printf ("⚠️ Multiple versions of '%s' detected: [%s], keeping %s\n " , family , strings .Join (versions , ", " ), kept )
354+ }
365355 }
366356
367357 // Generate languages configuration based on API tools response
368- if err := tools .CreateLanguagesConfigFile (apiTools , toolsConfigDir , uuidToName , initFlags ); err != nil {
358+ if err := tools .CreateLanguagesConfigFile (toolsWithLatestVersion , toolsConfigDir , uuidToName , initFlags ); err != nil {
369359 return fmt .Errorf ("failed to create languages configuration file: %w" , err )
370360 }
371361
372362 // Filter out any tools that use configuration file
373- configuredToolsWithUI := tools .FilterToolsByConfigUsage (apiTools )
363+ configuredToolsWithUI := tools .FilterToolsByConfigUsage (toolsWithLatestVersion )
374364
375365 // Create main config files with all enabled API tools
376- err = createConfigurationFiles (apiTools , false )
366+ err = createConfigurationFiles (toolsWithLatestVersion , false )
377367 if err != nil {
378368 log .Fatal (err )
379369 }
@@ -398,43 +388,48 @@ func buildRepositoryConfigurationFiles(token string) error {
398388func createToolFileConfigurations (tool domain.Tool , patternConfiguration []domain.PatternConfiguration ) error {
399389 toolsConfigDir := config .Config .ToolsConfigDirectory ()
400390 switch tool .Uuid {
401- case ESLint :
391+ case domain . ESLint , domain . ESLint9 :
402392 err := tools .CreateEslintConfig (toolsConfigDir , patternConfiguration )
403393 if err != nil {
404394 return fmt .Errorf ("failed to write eslint config: %v" , err )
405395 }
406396 fmt .Println ("ESLint configuration created based on Codacy settings. Ignoring plugin rules. ESLint plugins are not supported yet." )
407- case Trivy :
397+ case domain . Trivy :
408398 err := createTrivyConfigFile (patternConfiguration , toolsConfigDir )
409399 if err != nil {
410400 return fmt .Errorf ("failed to create Trivy config: %v" , err )
411401 }
412402 fmt .Println ("Trivy configuration created based on Codacy settings" )
413- case PMD :
403+ case domain . PMD :
414404 err := createPMDConfigFile (patternConfiguration , toolsConfigDir )
415405 if err != nil {
416406 return fmt .Errorf ("failed to create PMD config: %v" , err )
417407 }
418- fmt .Println ("PMD configuration created based on Codacy settings" )
419- case PyLint :
408+ case domain .PMD7 :
409+ err := createPMD7ConfigFile (patternConfiguration , toolsConfigDir )
410+ if err != nil {
411+ return fmt .Errorf ("failed to create PMD7 config: %v" , err )
412+ }
413+ fmt .Println ("PMD7 configuration created based on Codacy settings" )
414+ case domain .PyLint :
420415 err := createPylintConfigFile (patternConfiguration , toolsConfigDir )
421416 if err != nil {
422417 return fmt .Errorf ("failed to create Pylint config: %v" , err )
423418 }
424419 fmt .Println ("Pylint configuration created based on Codacy settings" )
425- case DartAnalyzer :
420+ case domain . DartAnalyzer :
426421 err := createDartAnalyzerConfigFile (patternConfiguration , toolsConfigDir )
427422 if err != nil {
428423 return fmt .Errorf ("failed to create Dart Analyzer config: %v" , err )
429424 }
430425 fmt .Println ("Dart configuration created based on Codacy settings" )
431- case Semgrep :
426+ case domain . Semgrep :
432427 err := createSemgrepConfigFile (patternConfiguration , toolsConfigDir )
433428 if err != nil {
434429 return fmt .Errorf ("failed to create Semgrep config: %v" , err )
435430 }
436431 fmt .Println ("Semgrep configuration created based on Codacy settings" )
437- case Lizard :
432+ case domain . Lizard :
438433 err := createLizardConfigFile (toolsConfigDir , patternConfiguration )
439434 if err != nil {
440435 return fmt .Errorf ("failed to create Lizard config: %v" , err )
@@ -445,7 +440,12 @@ func createToolFileConfigurations(tool domain.Tool, patternConfiguration []domai
445440}
446441
447442func createPMDConfigFile (config []domain.PatternConfiguration , toolsConfigDir string ) error {
448- pmdConfigurationString := tools .CreatePmdConfig (config )
443+ pmdConfigurationString := tools .CreatePmd6Config (config )
444+ return os .WriteFile (filepath .Join (toolsConfigDir , "ruleset.xml" ), []byte (pmdConfigurationString ), utils .DefaultFilePerms )
445+ }
446+
447+ func createPMD7ConfigFile (config []domain.PatternConfiguration , toolsConfigDir string ) error {
448+ pmdConfigurationString := tools .CreatePmd7Config (config )
449449 return os .WriteFile (filepath .Join (toolsConfigDir , "ruleset.xml" ), []byte (pmdConfigurationString ), utils .DefaultFilePerms )
450450}
451451
@@ -529,62 +529,91 @@ func createLizardConfigFile(toolsConfigDir string, patternConfiguration []domain
529529
530530// buildDefaultConfigurationFiles creates default configuration files for all tools
531531func buildDefaultConfigurationFiles (toolsConfigDir string ) error {
532- for _ , tool := range AvailableTools {
533- patternsConfig , err := codacyclient .GetDefaultToolPatternsConfig (initFlags , tool )
532+ for uuid := range domain . SupportedToolsMetadata {
533+ patternsConfig , err := codacyclient .GetDefaultToolPatternsConfig (initFlags , uuid )
534534 if err != nil {
535535 return fmt .Errorf ("failed to get default tool patterns config: %w" , err )
536536 }
537- switch tool {
538- case ESLint :
537+ switch uuid {
538+ case domain . ESLint :
539539 if err := tools .CreateEslintConfig (toolsConfigDir , patternsConfig ); err != nil {
540540 return fmt .Errorf ("failed to create eslint config file: %v" , err )
541541 }
542- case Trivy :
542+ case domain . Trivy :
543543 if err := createTrivyConfigFile (patternsConfig , toolsConfigDir ); err != nil {
544544 return fmt .Errorf ("failed to create default Trivy configuration: %w" , err )
545545 }
546- case PMD :
546+ case domain . PMD :
547547 if err := createPMDConfigFile (patternsConfig , toolsConfigDir ); err != nil {
548548 return fmt .Errorf ("failed to create default PMD configuration: %w" , err )
549549 }
550- case PyLint :
550+ case domain . PyLint :
551551 if err := createPylintConfigFile (patternsConfig , toolsConfigDir ); err != nil {
552552 return fmt .Errorf ("failed to create default Pylint configuration: %w" , err )
553553 }
554- case DartAnalyzer :
554+ case domain . DartAnalyzer :
555555 if err := createDartAnalyzerConfigFile (patternsConfig , toolsConfigDir ); err != nil {
556556 return fmt .Errorf ("failed to create default Dart Analyzer configuration: %w" , err )
557557 }
558- case Semgrep :
558+ case domain . Semgrep :
559559 if err := createSemgrepConfigFile (patternsConfig , toolsConfigDir ); err != nil {
560560 return fmt .Errorf ("failed to create default Semgrep configuration: %w" , err )
561561 }
562- case Lizard :
562+ case domain . Lizard :
563563 if err := createLizardConfigFile (toolsConfigDir , patternsConfig ); err != nil {
564564 return fmt .Errorf ("failed to create default Lizard configuration: %w" , err )
565565 }
566+ case domain .PMD7 , domain .ESLint9 :
567+ continue
566568 }
567569 }
568570 return nil
569571}
570572
571- const (
572- ESLint string = "f8b29663-2cb2-498d-b923-a10c6a8c05cd"
573- Trivy string = "2fd7fbe0-33f9-4ab3-ab73-e9b62404e2cb"
574- PMD string = "9ed24812-b6ee-4a58-9004-0ed183c45b8f"
575- PyLint string = "31677b6d-4ae0-4f56-8041-606a8d7a8e61"
576- DartAnalyzer string = "d203d615-6cf1-41f9-be5f-e2f660f7850f"
577- Semgrep string = "6792c561-236d-41b7-ba5e-9d6bee0d548b"
578- Lizard string = "76348462-84b3-409a-90d3-955e90abfb87"
579- )
573+ // KeepToolsWithLatestVersion filters the tools to keep only the latest version of each tool family.
574+ func KeepToolsWithLatestVersion (tools []domain.Tool ) (
575+ toolsWithLatestVersion []domain.Tool ,
576+ uuidToName map [string ]string ,
577+ familyToVersions map [string ][]string ,
578+ ) {
579+ latestTools := map [string ]domain.Tool {}
580+ uuidToName = map [string ]string {}
581+ seen := map [string ][]domain.Tool {}
582+ familyToVersions = map [string ][]string {}
583+
584+ for _ , tool := range tools {
585+ meta , ok := domain .SupportedToolsMetadata [tool .Uuid ]
586+ if ! ok {
587+ continue
588+ }
589+
590+ // Track all tools seen per family
591+ seen [meta .Name ] = append (seen [meta .Name ], tool )
592+
593+ // Pick the best version
594+ current , exists := latestTools [meta .Name ]
595+ if ! exists || domain .SupportedToolsMetadata [current .Uuid ].Priority > meta .Priority {
596+ latestTools [meta .Name ] = tool
597+ uuidToName [tool .Uuid ] = meta .Name
598+ }
599+ }
600+
601+ // Populate final list and version map for logging
602+ for family , tools := range seen {
603+ var versions []string
604+ for _ , t := range tools {
605+ v := t .Version
606+ if v == "" {
607+ v = "(unknown)"
608+ }
609+ versions = append (versions , v )
610+ }
611+ familyToVersions [family ] = versions
612+ }
613+
614+ for _ , tool := range latestTools {
615+ toolsWithLatestVersion = append (toolsWithLatestVersion , tool )
616+ }
580617
581- // AvailableTools lists all tool UUIDs supported by Codacy CLI.
582- var AvailableTools = []string {
583- ESLint ,
584- Trivy ,
585- PMD ,
586- PyLint ,
587- DartAnalyzer ,
588- Semgrep ,
589- Lizard ,
618+ return
590619}
0 commit comments