@@ -11,6 +11,7 @@ import (
1111 "net/http"
1212 "os"
1313 "path/filepath"
14+ "strings"
1415
1516 "codacy/cli-v2/utils"
1617
@@ -188,42 +189,33 @@ func getToolName(toolName string, version string) string {
188189 return toolName
189190}
190191
191- func runEslintAnalysis (workDirectory string , pathsToCheck []string , autoFix bool , outputFile string , outputFormat string ) {
192+ func runEslintAnalysis (workDirectory string , pathsToCheck []string , autoFix bool , outputFile string , outputFormat string ) error {
192193 eslint := config .Config .Tools ()["eslint" ]
193194 eslintInstallationDirectory := eslint .InstallDir
194195 nodeRuntime := config .Config .Runtimes ()["node" ]
195196 nodeBinary := nodeRuntime .Binaries ["node" ]
196197
197- tools .RunEslint (workDirectory , eslintInstallationDirectory , nodeBinary , pathsToCheck , autoFix , outputFile , outputFormat )
198+ return tools .RunEslint (workDirectory , eslintInstallationDirectory , nodeBinary , pathsToCheck , autoFix , outputFile , outputFormat )
198199}
199200
200- func runTrivyAnalysis (workDirectory string , pathsToCheck []string , outputFile string , outputFormat string ) {
201+ func runTrivyAnalysis (workDirectory string , pathsToCheck []string , outputFile string , outputFormat string ) error {
201202 trivy := config .Config .Tools ()["trivy" ]
202203 trivyBinary := trivy .Binaries ["trivy" ]
203204
204- err := tools .RunTrivy (workDirectory , trivyBinary , pathsToCheck , outputFile , outputFormat )
205- if err != nil {
206- log .Fatalf ("Error running Trivy: %v" , err )
207- }
205+ return tools .RunTrivy (workDirectory , trivyBinary , pathsToCheck , outputFile , outputFormat )
208206}
209207
210- func runPmdAnalysis (workDirectory string , pathsToCheck []string , outputFile string , outputFormat string ) {
208+ func runPmdAnalysis (workDirectory string , pathsToCheck []string , outputFile string , outputFormat string ) error {
211209 pmd := config .Config .Tools ()["pmd" ]
212210 pmdBinary := pmd .Binaries ["pmd" ]
213211
214- err := tools .RunPmd (workDirectory , pmdBinary , pathsToCheck , outputFile , outputFormat , config .Config )
215- if err != nil {
216- log .Fatalf ("Error running PMD: %v" , err )
217- }
212+ return tools .RunPmd (workDirectory , pmdBinary , pathsToCheck , outputFile , outputFormat , config .Config )
218213}
219214
220- func runPylintAnalysis (workDirectory string , pathsToCheck []string , outputFile string , outputFormat string ) {
215+ func runPylintAnalysis (workDirectory string , pathsToCheck []string , outputFile string , outputFormat string ) error {
221216 pylint := config .Config .Tools ()["pylint" ]
222217
223- err := tools .RunPylint (workDirectory , pylint , pathsToCheck , outputFile , outputFormat )
224- if err != nil {
225- log .Fatalf ("Error running Pylint: %v" , err )
226- }
218+ return tools .RunPylint (workDirectory , pylint , pathsToCheck , outputFile , outputFormat )
227219}
228220
229221var analyzeCmd = & cobra.Command {
@@ -262,13 +254,22 @@ var analyzeCmd = &cobra.Command{
262254 defer os .RemoveAll (tmpDir )
263255
264256 var sarifOutputs []string
257+ failedTools := make (map [string ]error )
265258 for toolName := range toolsToRun {
266259 log .Printf ("Running %s...\n " , toolName )
267260 tmpFile := filepath .Join (tmpDir , fmt .Sprintf ("%s.sarif" , toolName ))
268- runTool (workDirectory , toolName , args , tmpFile )
261+ if err := runTool (workDirectory , toolName , args , tmpFile ); err != nil {
262+ log .Printf ("Warning: Tool %s failed: %v\n " , toolName , err )
263+ failedTools [toolName ] = err
264+ continue
265+ }
269266 sarifOutputs = append (sarifOutputs , tmpFile )
270267 }
271268
269+ if len (sarifOutputs ) == 0 && len (failedTools ) > 0 {
270+ log .Fatal ("All tools failed to run. No analysis results available." )
271+ }
272+
272273 // create output file tmp file
273274 tmpOutputFile := filepath .Join (tmpDir , "merged.sarif" )
274275
@@ -277,6 +278,22 @@ var analyzeCmd = &cobra.Command{
277278 log .Fatalf ("Failed to merge SARIF outputs: %v" , err )
278279 }
279280
281+ // Add error runs to the merged SARIF
282+ if len (failedTools ) > 0 {
283+ mergedSarif , err := utils .ReadSarifFile (tmpOutputFile )
284+ if err != nil {
285+ log .Fatalf ("Failed to read merged SARIF: %v" , err )
286+ }
287+
288+ for toolName , err := range failedTools {
289+ utils .AddErrorRun (& mergedSarif , toolName , err .Error ())
290+ }
291+
292+ if err := utils .WriteSarifFile (mergedSarif , tmpOutputFile ); err != nil {
293+ log .Fatalf ("Failed to write updated SARIF: %v" , err )
294+ }
295+ }
296+
280297 if outputFile != "" {
281298 // copy tmpOutputFile to outputFile
282299 content , err := os .ReadFile (tmpOutputFile )
@@ -293,26 +310,83 @@ var analyzeCmd = &cobra.Command{
293310 fmt .Println (string (content ))
294311 }
295312 } else {
296- // Run tools without merging outputs
313+ // Create temporary directory for individual tool outputs
314+ tmpDir , err := os .MkdirTemp ("" , "codacy-analysis-*" )
315+ if err != nil {
316+ log .Fatalf ("Failed to create temporary directory: %v" , err )
317+ }
318+ defer os .RemoveAll (tmpDir )
319+
320+ var toolOutputs []string
321+ failedTools := make (map [string ]error )
297322 for toolName := range toolsToRun {
298323 log .Printf ("Running %s...\n " , toolName )
299- runTool (workDirectory , toolName , args , outputFile )
324+ tmpFile := filepath .Join (tmpDir , fmt .Sprintf ("%s.json" , toolName ))
325+ if err := runTool (workDirectory , toolName , args , tmpFile ); err != nil {
326+ log .Printf ("Warning: Tool %s failed: %v\n " , toolName , err )
327+ failedTools [toolName ] = err
328+ continue
329+ }
330+ toolOutputs = append (toolOutputs , tmpFile )
331+ }
332+
333+ // Create merged output
334+ mergedOutput := make (map [string ]interface {})
335+ mergedOutput ["tools" ] = make (map [string ]interface {})
336+ mergedOutput ["errors" ] = make (map [string ]string )
337+
338+ // Add error information
339+ for toolName , err := range failedTools {
340+ mergedOutput ["errors" ].(map [string ]string )[toolName ] = err .Error ()
341+ }
342+
343+ // Add tool outputs
344+ for _ , toolOutput := range toolOutputs {
345+ output , err := os .ReadFile (toolOutput )
346+ if err != nil {
347+ log .Printf ("Warning: Failed to read output from %s: %v\n " , filepath .Base (toolOutput ), err )
348+ continue
349+ }
350+
351+ toolName := strings .TrimSuffix (filepath .Base (toolOutput ), ".json" )
352+ var toolOutput interface {}
353+ if err := json .Unmarshal (output , & toolOutput ); err != nil {
354+ log .Printf ("Warning: Failed to parse output from %s as JSON, storing raw output: %v" , toolName , err )
355+ toolOutput = map [string ]interface {}{
356+ "raw" : string (output ),
357+ }
358+ }
359+ mergedOutput ["tools" ].(map [string ]interface {})[toolName ] = toolOutput
360+ }
361+
362+ // Write merged output
363+ mergedJSON , err := json .MarshalIndent (mergedOutput , "" , " " )
364+ if err != nil {
365+ log .Fatalf ("Failed to marshal merged output: %v" , err )
366+ }
367+
368+ if outputFile != "" {
369+ if err := os .WriteFile (outputFile , mergedJSON , utils .DefaultFilePerms ); err != nil {
370+ log .Fatalf ("Failed to write merged output: %v" , err )
371+ }
372+ } else {
373+ fmt .Println (string (mergedJSON ))
300374 }
301375 }
302376 },
303377}
304378
305- func runTool (workDirectory string , toolName string , args []string , outputFile string ) {
379+ func runTool (workDirectory string , toolName string , args []string , outputFile string ) error {
306380 switch toolName {
307381 case "eslint" :
308- runEslintAnalysis (workDirectory , args , autoFix , outputFile , outputFormat )
382+ return runEslintAnalysis (workDirectory , args , autoFix , outputFile , outputFormat )
309383 case "trivy" :
310- runTrivyAnalysis (workDirectory , args , outputFile , outputFormat )
384+ return runTrivyAnalysis (workDirectory , args , outputFile , outputFormat )
311385 case "pmd" :
312- runPmdAnalysis (workDirectory , args , outputFile , outputFormat )
386+ return runPmdAnalysis (workDirectory , args , outputFile , outputFormat )
313387 case "pylint" :
314- runPylintAnalysis (workDirectory , args , outputFile , outputFormat )
388+ return runPylintAnalysis (workDirectory , args , outputFile , outputFormat )
315389 default :
316- log . Printf ( "Warning: Unsupported tool: %s\n " , toolName )
390+ return fmt . Errorf ( "unsupported tool: %s" , toolName )
317391 }
318392}
0 commit comments