@@ -9,15 +9,15 @@ import (
99 reviveTool "codacy/cli-v2/tools/revive"
1010 "encoding/json"
1111 "fmt"
12- "io"
1312 "log"
14- "net/http"
1513 "os"
1614 "path/filepath"
1715 "strings"
1816
1917 "codacy/cli-v2/utils"
2018
19+ codacyclient "codacy/cli-v2/codacy-client"
20+
2121 "github.com/spf13/cobra"
2222 "gopkg.in/yaml.v3"
2323)
@@ -212,21 +212,6 @@ type CodacyIssue struct {
212212 Category string `json:"category"`
213213}
214214
215- type Tool struct {
216- UUID string `json:"uuid"`
217- ShortName string `json:"shortName"`
218- Prefix string `json:"prefix"`
219- }
220-
221- type Pattern struct {
222- UUID string `json:"uuid"`
223- ID string `json:"id"`
224- Name string `json:"name"`
225- Category string `json:"category"`
226- Description string `json:"description"`
227- Level string `json:"level"`
228- }
229-
230215func init () {
231216 analyzeCmd .Flags ().StringVarP (& outputFile , "output" , "o" , "" , "Output file for analysis results" )
232217 analyzeCmd .Flags ().StringVarP (& toolsToAnalyzeParam , "tool" , "t" , "" , "Which tool to run analysis with. If not specified, all configured tools will be run" )
@@ -235,66 +220,24 @@ func init() {
235220 rootCmd .AddCommand (analyzeCmd )
236221}
237222
238- func loadsToolAndPatterns (toolName string ) (Tool , []Pattern ) {
239- var toolsURL = "https://app.codacy.com/api/v3/tools"
240-
241- req , err := http .NewRequest ("GET" , toolsURL , nil )
223+ func loadsToolAndPatterns (toolName string ) (domain.Tool , []domain.PatternConfiguration ) {
224+ var toolsResponse , err = codacyclient .GetToolsVersions ()
242225 if err != nil {
243- fmt .Printf ("Error creating request: %v \n " , err )
244- panic ( "panic" )
226+ fmt .Println ("Error: " , err )
227+ return domain. Tool {}, []domain. PatternConfiguration {}
245228 }
246- req .Header .Set ("Content-Type" , "application/json" )
247-
248- resp , err := http .DefaultClient .Do (req )
249- if err != nil {
250- fmt .Printf ("Error fetching patterns: %v\n " , err )
251- panic ("panic" )
252- }
253- defer resp .Body .Close ()
254-
255- body , _ := io .ReadAll (resp .Body )
256-
257- var toolsResponse struct {
258- Data []Tool `json:"data"`
259- }
260- json .Unmarshal (body , & toolsResponse )
261- var tool Tool
262- for _ , t := range toolsResponse .Data {
263- if t .ShortName == toolName {
229+ var tool domain.Tool
230+ for _ , t := range toolsResponse {
231+ if t .Name == toolName {
264232 tool = t
265233 break
266234 }
267235 }
268- var patterns []Pattern
269- var hasNext bool = true
270- cursor := ""
271- client := & http.Client {}
272-
273- for hasNext {
274- baseURL := fmt .Sprintf ("https://app.codacy.com/api/v3/tools/%s/patterns?limit=1000%s" , tool .UUID , cursor )
275- req , _ := http .NewRequest ("GET" , baseURL , nil )
276- req .Header .Set ("Content-Type" , "application/json" )
277-
278- resp , err := client .Do (req )
279- if err != nil {
280- fmt .Println ("Error:" , err )
281- break
282- }
283- defer resp .Body .Close ()
284- body , _ := io .ReadAll (resp .Body )
285-
286- var patternsResponse struct {
287- Data []Pattern `json:"data"`
288- Pagination struct {
289- Cursor string `json:"cursor"`
290- } `json:"pagination"`
291- }
292- json .Unmarshal (body , & patternsResponse )
293- patterns = append (patterns , patternsResponse .Data ... )
294- hasNext = patternsResponse .Pagination .Cursor != ""
295- if hasNext {
296- cursor = "&cursor=" + patternsResponse .Pagination .Cursor
297- }
236+ var patterns []domain.PatternConfiguration
237+ patterns , err = codacyclient .GetDefaultToolPatternsConfig (domain.InitFlags {}, tool .Uuid )
238+ if err != nil {
239+ fmt .Println ("Error:" , err )
240+ return domain.Tool {}, []domain.PatternConfiguration {}
298241 }
299242 return tool , patterns
300243}
@@ -317,110 +260,113 @@ func getToolName(toolName string, version string) string {
317260 return toolName
318261}
319262
320- func runEslintAnalysis (workDirectory string , pathsToCheck []string , autoFix bool , outputFile string , outputFormat string ) error {
321- eslint := config .Config .Tools ()["eslint" ]
322- eslintInstallationDirectory := eslint .InstallDir
323- nodeRuntime := config .Config .Runtimes ()["node" ]
324- nodeBinary := nodeRuntime .Binaries ["node" ]
325-
326- return tools .RunEslint (workDirectory , eslintInstallationDirectory , nodeBinary , pathsToCheck , autoFix , outputFile , outputFormat )
327- }
328-
329- func runTrivyAnalysis (workDirectory string , pathsToCheck []string , outputFile string , outputFormat string ) error {
330- trivy := config .Config .Tools ()["trivy" ]
331- if trivy == nil {
332- log .Fatal ("Trivy tool configuration not found" )
263+ func validateToolName (toolName string ) error {
264+ if toolName == "" {
265+ return fmt .Errorf ("tool name cannot be empty" )
333266 }
334- trivyBinary := trivy .Binaries ["trivy" ]
335267
336- return tools . RunTrivy ( workDirectory , trivyBinary , pathsToCheck , outputFile , outputFormat )
337- }
268+ // Get plugin manager to access the tools filesystem
269+ pluginManager := plugins . GetPluginManager ()
338270
339- func runPmdAnalysis ( workDirectory string , pathsToCheck [] string , outputFile string , outputFormat string ) error {
340- pmd := config . Config . Tools ()[ "pmd" ]
341- if pmd = = nil {
342- log . Fatal ( "Pmd tool configuration not found" )
271+ // Try to get the tool configuration - this will fail if the tool doesn't exist
272+ _ , err := pluginManager . GetToolConfig ( toolName )
273+ if err ! = nil {
274+ return fmt . Errorf ( " tool '%s' is not supported" , toolName )
343275 }
344- pmdBinary := pmd .Binaries ["pmd" ]
345276
346- return tools . RunPmd ( workDirectory , pmdBinary , pathsToCheck , outputFile , outputFormat , config . Config )
277+ return nil
347278}
348279
349- func runPylintAnalysis (workDirectory string , pathsToCheck []string , outputFile string , outputFormat string ) error {
350- pylint := config .Config .Tools ()["pylint" ]
351- if pylint == nil {
352- log .Fatal ("Pylint tool configuration not found" )
280+ func runToolByName (toolName string , workDirectory string , pathsToCheck []string , autoFix bool , outputFile string , outputFormat string , tool * plugins.ToolInfo , runtime * plugins.RuntimeInfo ) error {
281+ switch toolName {
282+ case "eslint" :
283+ binaryPath := runtime .Binaries [tool .Runtime ]
284+ return tools .RunEslint (workDirectory , tool .InstallDir , binaryPath , pathsToCheck , autoFix , outputFile , outputFormat )
285+ case "trivy" :
286+ binaryPath := tool .Binaries [toolName ]
287+ return tools .RunTrivy (workDirectory , binaryPath , pathsToCheck , outputFile , outputFormat )
288+ case "pmd" :
289+ binaryPath := tool .Binaries [toolName ]
290+ return tools .RunPmd (workDirectory , binaryPath , pathsToCheck , outputFile , outputFormat , config .Config )
291+ case "pylint" :
292+ binaryPath := tool .Binaries [tool .Runtime ]
293+ return tools .RunPylint (workDirectory , binaryPath , pathsToCheck , outputFile , outputFormat )
294+ case "dartanalyzer" :
295+ binaryPath := tool .Binaries [tool .Runtime ]
296+ return tools .RunDartAnalyzer (workDirectory , tool .InstallDir , binaryPath , pathsToCheck , outputFile , outputFormat )
297+ case "semgrep" :
298+ binaryPath := tool .Binaries [toolName ]
299+ return tools .RunSemgrep (workDirectory , binaryPath , pathsToCheck , outputFile , outputFormat )
300+ case "lizard" :
301+ binaryPath := tool .Binaries [tool .Runtime ]
302+ return lizard .RunLizard (workDirectory , binaryPath , pathsToCheck , outputFile , outputFormat )
303+ case "codacy-enigma-cli" :
304+ return tools .RunEnigma (workDirectory , tool .InstallDir , tool .Binaries ["codacy-enigma-cli" ], pathsToCheck , outputFile , outputFormat )
305+ case "revive" :
306+ return reviveTool .RunRevive (workDirectory , tool .Binaries ["revive" ], pathsToCheck , outputFile , outputFormat )
353307 }
354- pylintBinary := pylint .Binaries ["python" ]
355-
356- return tools .RunPylint (workDirectory , pylintBinary , pathsToCheck , outputFile , outputFormat )
308+ return fmt .Errorf ("unsupported tool: %s" , toolName )
357309}
358310
359- func runDartAnalyzer (workDirectory string , pathsToCheck []string , outputFile string , outputFormat string ) error {
360- dartanalyzer := config . Config . Tools ()[ "dartanalyzer" ]
361- if dartanalyzer = = nil {
362- log . Fatal ( "Dart analyzer tool configuration not found" )
311+ func runTool (workDirectory string , toolName string , pathsToCheck []string , outputFile string , autoFix bool , outputFormat string ) error {
312+ err := validateToolName ( toolName )
313+ if err ! = nil {
314+ return err
363315 }
364- return tools . RunDartAnalyzer ( workDirectory , dartanalyzer . InstallDir , dartanalyzer . Binaries [ "dart" ], pathsToCheck , outputFile , outputFormat )
365- }
316+ log . Println ( "Running tools for the specified file(s)..." )
317+ log . Printf ( "Running %s... \n " , toolName )
366318
367- func runSemgrepAnalysis (workDirectory string , pathsToCheck []string , outputFile string , outputFormat string ) error {
368- semgrep := config .Config .Tools ()["semgrep" ]
369- if semgrep == nil {
370- log .Fatal ("Semgrep tool configuration not found" )
319+ tool := config .Config .Tools ()[toolName ]
320+ var isToolInstalled bool
321+ if tool == nil {
322+ isToolInstalled = false
323+ } else {
324+ isToolInstalled = config .Config .IsToolInstalled (toolName , tool )
371325 }
372- semgrepBinary := semgrep .Binaries ["semgrep" ]
373-
374- return tools .RunSemgrep (workDirectory , semgrepBinary , pathsToCheck , outputFile , outputFormat )
375- }
326+ var isRuntimeInstalled bool
376327
377- func runLizardAnalysis (workDirectory string , pathsToCheck []string , outputFile string , outputFormat string ) error {
378- lizardTool := config .Config .Tools ()["lizard" ]
328+ var runtime * plugins.RuntimeInfo
379329
380- if lizardTool == nil {
381- log . Fatal ( "Lizard plugin configuration not found" )
330+ if toolName == "codacy-enigma-cli" {
331+ isToolInstalled = true
382332 }
383333
384- lizardBinary := lizardTool .Binaries ["python" ]
385-
386- configFile , exists := tools .ConfigFileExists (config .Config , "lizard.yaml" )
387- var patterns []domain.PatternDefinition
388- var err error
389-
390- if exists {
391- // Configuration exists, read from file
392- patterns , err = lizard .ReadConfig (configFile )
393- if err != nil {
394- return fmt .Errorf ("error reading config file: %v" , err )
334+ if tool == nil || ! isToolInstalled {
335+ if tool == nil {
336+ fmt .Println ("Tool configuration not found, adding and installing..." )
395337 }
396- } else {
397- fmt .Println ("No configuration file found for Lizard, using default patterns, run init with repository token to get a custom configuration" )
398- patterns , err = tools .FetchDefaultEnabledPatterns (domain .Lizard )
338+ if ! isToolInstalled {
339+ fmt .Println ("Tool is not installed, installing..." )
340+ }
341+ err := config .InstallTool (toolName , tool , "" )
399342 if err != nil {
400- return fmt .Errorf ("failed to fetch default patterns: %v" , err )
343+ return fmt .Errorf ("failed to install %s: %w" , toolName , err )
344+ }
345+ tool = config .Config .Tools ()[toolName ]
346+ runtime = config .Config .Runtimes ()[tool .Runtime ]
347+ isRuntimeInstalled = runtime == nil || config .Config .IsRuntimeInstalled (tool .Runtime , runtime )
348+ if ! isRuntimeInstalled {
349+ fmt .Printf ("%s runtime is not installed, installing...\n " , tool .Runtime )
350+ err := config .InstallRuntime (tool .Runtime , runtime )
351+ if err != nil {
352+ return fmt .Errorf ("failed to install %s runtime: %w" , tool .Runtime , err )
353+ }
354+ runtime = config .Config .Runtimes ()[tool .Runtime ]
401355 }
402- }
403-
404- return lizard .RunLizard (workDirectory , lizardBinary , pathsToCheck , outputFile , outputFormat , patterns )
405- }
406-
407- func runEnigmaAnalysis (workDirectory string , pathsToCheck []string , outputFile string , outputFormat string ) error {
408- enigma := config .Config .Tools ()["codacy-enigma-cli" ]
409- if enigma == nil {
410- log .Fatal ("Enigma tool configuration not found" )
411- }
412-
413- return tools .RunEnigma (workDirectory , enigma .InstallDir , enigma .Binaries ["codacy-enigma-cli" ], pathsToCheck , outputFile , outputFormat )
414- }
415356
416- func runReviveAnalysis (workDirectory string , pathsToCheck []string , outputFile string , outputFormat string ) error {
417- revive := config .Config .Tools ()["revive" ]
418- if revive == nil {
419- log .Fatal ("Revive tool configuration not found" )
357+ } else {
358+ runtime = config .Config .Runtimes ()[tool .Runtime ]
359+ isRuntimeInstalled = runtime == nil || config .Config .IsRuntimeInstalled (tool .Runtime , runtime )
360+ if ! isRuntimeInstalled {
361+ fmt .Printf ("%s runtime is not installed, installing...\n " , tool .Runtime )
362+ err := config .InstallRuntime (tool .Runtime , runtime )
363+ if err != nil {
364+ return fmt .Errorf ("failed to install %s runtime: %w" , tool .Runtime , err )
365+ }
366+ runtime = config .Config .Runtimes ()[tool .Runtime ]
367+ }
420368 }
421- reviveBinary := revive .Binaries ["revive" ]
422-
423- return reviveTool .RunRevive (workDirectory , reviveBinary , pathsToCheck , outputFile , outputFormat )
369+ return runToolByName (toolName , workDirectory , pathsToCheck , autoFix , outputFile , outputFormat , tool , runtime )
424370}
425371
426372var analyzeCmd = & cobra.Command {
@@ -457,8 +403,6 @@ var analyzeCmd = &cobra.Command{
457403 return
458404 }
459405
460- log .Println ("Running tools for the specified file(s)..." )
461-
462406 if outputFormat == "sarif" {
463407 // Create temporary directory for individual tool outputs
464408 tmpDir , err := os .MkdirTemp ("" , "codacy-analysis-*" )
@@ -469,10 +413,9 @@ var analyzeCmd = &cobra.Command{
469413
470414 var sarifOutputs []string
471415 for toolName := range toolsToRun {
472- log .Printf ("Running %s...\n " , toolName )
473416 tmpFile := filepath .Join (tmpDir , fmt .Sprintf ("%s.sarif" , toolName ))
474- if err := runTool (workDirectory , toolName , args , tmpFile ); err != nil {
475- log .Printf ("Tool failed to run: %s: % v\n " , toolName , err )
417+ if err := runTool (workDirectory , toolName , args , tmpFile , autoFix , outputFormat ); err != nil {
418+ log .Printf ("Tool failed to run: %v\n " , err )
476419 }
477420 sarifOutputs = append (sarifOutputs , tmpFile )
478421 }
@@ -506,36 +449,10 @@ var analyzeCmd = &cobra.Command{
506449 } else {
507450 // Run tools without merging outputs
508451 for toolName := range toolsToRun {
509- log .Printf ("Running %s...\n " , toolName )
510- if err := runTool (workDirectory , toolName , args , outputFile ); err != nil {
511- log .Printf ("Tool failed to run: %s: %v\n " , toolName , err )
452+ if err := runTool (workDirectory , toolName , args , outputFile , autoFix , outputFormat ); err != nil {
453+ log .Printf ("Tool failed to run: %v\n " , err )
512454 }
513455 }
514456 }
515457 },
516458}
517-
518- func runTool (workDirectory string , toolName string , args []string , outputFile string ) error {
519- switch toolName {
520- case "eslint" :
521- return runEslintAnalysis (workDirectory , args , autoFix , outputFile , outputFormat )
522- case "trivy" :
523- return runTrivyAnalysis (workDirectory , args , outputFile , outputFormat )
524- case "pmd" :
525- return runPmdAnalysis (workDirectory , args , outputFile , outputFormat )
526- case "pylint" :
527- return runPylintAnalysis (workDirectory , args , outputFile , outputFormat )
528- case "semgrep" :
529- return runSemgrepAnalysis (workDirectory , args , outputFile , outputFormat )
530- case "dartanalyzer" :
531- return runDartAnalyzer (workDirectory , args , outputFile , outputFormat )
532- case "lizard" :
533- return runLizardAnalysis (workDirectory , args , outputFile , outputFormat )
534- case "codacy-enigma-cli" :
535- return runEnigmaAnalysis (workDirectory , args , outputFile , outputFormat )
536- case "revive" :
537- return runReviveAnalysis (workDirectory , args , outputFile , outputFormat )
538- default :
539- return fmt .Errorf ("unsupported tool: %s" , toolName )
540- }
541- }
0 commit comments