Skip to content

Commit 4770bfe

Browse files
authored
[PLUTO-1392] Add Lizard tool (#101)
* [PLUTO-1392] Add Lizard tool (#101)
1 parent d320201 commit 4770bfe

18 files changed

+1090
-20
lines changed

.codacy/codacy.yaml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,3 +9,4 @@ tools:
99
1010
1111
12+

cmd/analyze.go

Lines changed: 55 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2,8 +2,10 @@ package cmd
22

33
import (
44
"codacy/cli-v2/config"
5+
"codacy/cli-v2/domain"
56
"codacy/cli-v2/plugins"
67
"codacy/cli-v2/tools"
8+
"codacy/cli-v2/tools/lizard"
79
"encoding/json"
810
"fmt"
911
"io"
@@ -329,26 +331,39 @@ func runEslintAnalysis(workDirectory string, pathsToCheck []string, autoFix bool
329331

330332
func runTrivyAnalysis(workDirectory string, pathsToCheck []string, outputFile string, outputFormat string) error {
331333
trivy := config.Config.Tools()["trivy"]
334+
if trivy == nil {
335+
log.Fatal("Trivy tool configuration not found")
336+
}
332337
trivyBinary := trivy.Binaries["trivy"]
333338

334339
return tools.RunTrivy(workDirectory, trivyBinary, pathsToCheck, outputFile, outputFormat)
335340
}
336341

337342
func runPmdAnalysis(workDirectory string, pathsToCheck []string, outputFile string, outputFormat string) error {
338343
pmd := config.Config.Tools()["pmd"]
344+
if pmd == nil {
345+
log.Fatal("Pmd tool configuration not found")
346+
}
339347
pmdBinary := pmd.Binaries["pmd"]
340348

341349
return tools.RunPmd(workDirectory, pmdBinary, pathsToCheck, outputFile, outputFormat, config.Config)
342350
}
343351

344352
func runPylintAnalysis(workDirectory string, pathsToCheck []string, outputFile string, outputFormat string) error {
345353
pylint := config.Config.Tools()["pylint"]
354+
if pylint == nil {
355+
log.Fatal("Pylint tool configuration not found")
356+
}
357+
pylintBinary := pylint.Binaries["python"]
346358

347-
return tools.RunPylint(workDirectory, pylint, pathsToCheck, outputFile, outputFormat)
359+
return tools.RunPylint(workDirectory, pylintBinary, pathsToCheck, outputFile, outputFormat)
348360
}
349361

350362
func runDartAnalyzer(workDirectory string, pathsToCheck []string, outputFile string, outputFormat string) error {
351363
dartanalyzer := config.Config.Tools()["dartanalyzer"]
364+
if dartanalyzer == nil {
365+
log.Fatal("Dart analyzer tool configuration not found")
366+
}
352367
return tools.RunDartAnalyzer(workDirectory, dartanalyzer.InstallDir, dartanalyzer.Binaries["dart"], pathsToCheck, outputFile, outputFormat)
353368
}
354369

@@ -357,8 +372,43 @@ func runSemgrepAnalysis(workDirectory string, pathsToCheck []string, outputFile
357372
if semgrep == nil {
358373
log.Fatal("Semgrep tool configuration not found")
359374
}
375+
semgrepBinary := semgrep.Binaries["python"]
376+
377+
return tools.RunSemgrep(workDirectory, semgrepBinary, pathsToCheck, outputFile, outputFormat)
378+
}
379+
380+
func runLizardAnalysis(workDirectory string, pathsToCheck []string, outputFile string, outputFormat string) error {
381+
lizardTool := config.Config.Tools()["lizard"]
382+
383+
if lizardTool == nil {
384+
log.Fatal("Lizard plugin configuration not found")
385+
}
386+
387+
lizardBinary := lizardTool.Binaries["python"]
388+
389+
configFile, exists := tools.ConfigFileExists(config.Config, "lizard.yaml")
390+
var patterns []domain.PatternDefinition
391+
var err error
392+
393+
//this logic is here because I want to pass the config to the runner which is good for:
394+
//Separation of concerns, runner will simply run the tool now
395+
//Easier testing, since config is now passed
396+
//Avoiding fetching the default patterns in tests (unless we want to maintain codacy directory with the configs)
397+
if exists {
398+
// Configuration exists, read from file
399+
patterns, err = lizard.ReadConfig(configFile)
400+
if err != nil {
401+
return fmt.Errorf("error reading config file: %v", err)
402+
}
403+
} else {
404+
fmt.Println("No configuration file found for Lizard, using default patterns, run init with repository token to get a custom configuration")
405+
patterns, err = tools.FetchDefaultEnabledPatterns(Lizard)
406+
if err != nil {
407+
return fmt.Errorf("failed to fetch default patterns: %v", err)
408+
}
409+
}
360410

361-
return tools.RunSemgrep(workDirectory, semgrep, pathsToCheck, outputFile, outputFormat)
411+
return lizard.RunLizard(workDirectory, lizardBinary, pathsToCheck, outputFile, outputFormat, patterns)
362412
}
363413

364414
var analyzeCmd = &cobra.Command{
@@ -380,6 +430,7 @@ var analyzeCmd = &cobra.Command{
380430
} else {
381431
// Run all configured tools
382432
toolsToRun = config.Config.Tools()
433+
log.Println("Running all configured tools...")
383434
}
384435

385436
if len(toolsToRun) == 0 {
@@ -463,6 +514,8 @@ func runTool(workDirectory string, toolName string, args []string, outputFile st
463514
return runSemgrepAnalysis(workDirectory, args, outputFile, outputFormat)
464515
case "dartanalyzer":
465516
return runDartAnalyzer(workDirectory, args, outputFile, outputFormat)
517+
case "lizard":
518+
return runLizardAnalysis(workDirectory, args, outputFile, outputFormat)
466519
default:
467520
return fmt.Errorf("unsupported tool: %s", toolName)
468521
}

cmd/init.go

Lines changed: 56 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@ import (
44
"codacy/cli-v2/config"
55
"codacy/cli-v2/domain"
66
"codacy/cli-v2/tools"
7+
"codacy/cli-v2/tools/lizard"
78
"codacy/cli-v2/tools/pylint"
89
"codacy/cli-v2/utils"
910
"encoding/json"
@@ -65,6 +66,10 @@ var initCmd = &cobra.Command{
6566
if err != nil {
6667
log.Fatal(err)
6768
}
69+
// Create default configuration files
70+
if err := buildDefaultConfigurationFiles(toolsConfigDir); err != nil {
71+
log.Fatal(err)
72+
}
6873
} else {
6974
err := buildRepositoryConfigurationFiles(initFlags.apiToken)
7075
if err != nil {
@@ -140,6 +145,7 @@ func configFileTemplate(tools []tools.Tool) string {
140145
needsNode := false
141146
needsPython := false
142147
needsDart := false
148+
143149
// Default versions
144150
defaultVersions := map[string]string{
145151
ESLint: "9.3.0",
@@ -148,6 +154,7 @@ func configFileTemplate(tools []tools.Tool) string {
148154
PMD: "6.55.0",
149155
DartAnalyzer: "3.7.2",
150156
Semgrep: "1.78.0",
157+
Lizard: "1.17.19",
151158
}
152159

153160
// Build map of enabled tools with their versions
@@ -162,7 +169,7 @@ func configFileTemplate(tools []tools.Tool) string {
162169
// Check if tool needs a runtime
163170
if tool.Uuid == ESLint {
164171
needsNode = true
165-
} else if tool.Uuid == PyLint {
172+
} else if tool.Uuid == PyLint || tool.Uuid == Lizard {
166173
needsPython = true
167174
} else if tool.Uuid == DartAnalyzer {
168175
needsDart = true
@@ -203,6 +210,7 @@ func configFileTemplate(tools []tools.Tool) string {
203210
PMD: "pmd",
204211
DartAnalyzer: "dartanalyzer",
205212
Semgrep: "semgrep",
213+
Lizard: "lizard",
206214
}
207215

208216
for uuid, name := range uuidToName {
@@ -218,6 +226,7 @@ func configFileTemplate(tools []tools.Tool) string {
218226
sb.WriteString(fmt.Sprintf(" - pmd@%s\n", defaultVersions[PMD]))
219227
sb.WriteString(fmt.Sprintf(" - dartanalyzer@%s\n", defaultVersions[DartAnalyzer]))
220228
sb.WriteString(fmt.Sprintf(" - semgrep@%s\n", defaultVersions[Semgrep]))
229+
sb.WriteString(fmt.Sprintf(" - lizard@%s\n", defaultVersions[Lizard]))
221230
}
222231

223232
return sb.String()
@@ -422,6 +431,8 @@ func createToolFileConfigurations(tool tools.Tool, patternConfiguration []domain
422431
return fmt.Errorf("failed to create Semgrep config: %v", err)
423432
}
424433
}
434+
case Lizard:
435+
createLizardConfigFile(toolsConfigDir, patternConfiguration)
425436
}
426437
return nil
427438
}
@@ -434,7 +445,6 @@ func createPMDConfigFile(config []domain.PatternConfiguration, toolsConfigDir st
434445
func createDefaultPMDConfigFile(toolsConfigDir string) error {
435446
content := tools.CreatePmdConfig([]domain.PatternConfiguration{})
436447
return os.WriteFile(filepath.Join(toolsConfigDir, "ruleset.xml"), []byte(content), utils.DefaultFilePerms)
437-
438448
}
439449

440450
func createPylintConfigFile(config []domain.PatternConfiguration, toolsConfigDir string) error {
@@ -527,11 +537,55 @@ func cleanConfigDirectory(toolsConfigDir string) error {
527537
return nil
528538
}
529539

540+
func createLizardConfigFile(toolsConfigDir string, patternConfiguration []domain.PatternConfiguration) error {
541+
var patterns []domain.PatternDefinition
542+
543+
if len(patternConfiguration) == 0 {
544+
fmt.Println("Using default Lizard configuration")
545+
var err error
546+
patterns, err = tools.FetchDefaultEnabledPatterns(Lizard)
547+
if err != nil {
548+
return err
549+
}
550+
} else {
551+
patterns = make([]domain.PatternDefinition, len(patternConfiguration))
552+
for i, pattern := range patternConfiguration {
553+
patterns[i] = pattern.PatternDefinition
554+
}
555+
556+
fmt.Println("Lizard configuration created based on Codacy settings")
557+
}
558+
559+
content, err := lizard.CreateLizardConfig(patterns)
560+
if err != nil {
561+
return fmt.Errorf("failed to create Lizard configuration: %w", err)
562+
}
563+
564+
return os.WriteFile(filepath.Join(toolsConfigDir, "lizard.yaml"), []byte(content), utils.DefaultFilePerms)
565+
}
566+
567+
// buildDefaultConfigurationFiles creates default configuration files for all tools
568+
func buildDefaultConfigurationFiles(toolsConfigDir string) error {
569+
// Create default Lizard configuration
570+
if err := createLizardConfigFile(toolsConfigDir, []domain.PatternConfiguration{}); err != nil {
571+
return fmt.Errorf("failed to create default Lizard configuration: %w", err)
572+
}
573+
574+
// Add other default tool configurations here as needed
575+
// For example:
576+
// if err := createDefaultEslintConfigFile(toolsConfigDir); err != nil {
577+
// return fmt.Errorf("failed to create default ESLint configuration: %w", err)
578+
// }
579+
580+
return nil
581+
}
582+
530583
const (
531584
ESLint string = "f8b29663-2cb2-498d-b923-a10c6a8c05cd"
532585
Trivy string = "2fd7fbe0-33f9-4ab3-ab73-e9b62404e2cb"
533586
PMD string = "9ed24812-b6ee-4a58-9004-0ed183c45b8f"
534587
PyLint string = "31677b6d-4ae0-4f56-8041-606a8d7a8e61"
535588
DartAnalyzer string = "d203d615-6cf1-41f9-be5f-e2f660f7850f"
536589
Semgrep string = "6792c561-236d-41b7-ba5e-9d6bee0d548b"
590+
Lizard string = "76348462-84b3-409a-90d3-955e90abfb87"
537591
)

domain/patternConfiguration.go

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,9 @@
11
package domain
22

33
type ParameterConfiguration struct {
4-
Name string `json:"name"`
5-
Value string `json:"value"`
4+
Name string `json:"name"`
5+
Value string `json:"value,omitempty"`
6+
Default string `json:"default,omitempty"`
67
}
78

89
type PatternDefinition struct {

plugins/tool-utils_test.go

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -168,6 +168,7 @@ func TestGetSupportedTools(t *testing.T) {
168168
"trivy",
169169
"dartanalyzer",
170170
"semgrep",
171+
"lizard",
171172
},
172173
expectedError: false,
173174
},

plugins/tools/lizard/plugin.yaml

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
name: lizard
2+
description: Lizard code complexity analyzer
3+
runtime: python
4+
runtime_binaries:
5+
package_manager: python3
6+
execution: python3
7+
binaries:
8+
- name: python
9+
path: "venv/bin/python3"
10+
output_options:
11+
file_flag: "-o"
12+
analysis_options:
13+
default_path: "."

0 commit comments

Comments
 (0)