Skip to content

Commit d3fdc43

Browse files
added tests and fixed bug with runtime installation
1 parent fd66b6b commit d3fdc43

File tree

4 files changed

+202
-22
lines changed

4 files changed

+202
-22
lines changed

cmd/analyze.go

Lines changed: 92 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -319,11 +319,22 @@ func getToolName(toolName string, version string) string {
319319
func runEslintAnalysis(workDirectory string, pathsToCheck []string, autoFix bool, outputFile string, outputFormat string) error {
320320
// Ensure ESLint tool is configured and installed
321321
eslint := config.Config.Tools()["eslint"]
322-
if eslint == nil || !config.Config.IsToolInstalled("eslint", eslint) {
322+
isToolInstalled := config.Config.IsToolInstalled("eslint", eslint)
323+
324+
// Also check if the runtime is installed
325+
var isRuntimeInstalled bool
326+
if eslint != nil {
327+
nodeRuntime := config.Config.Runtimes()["node"]
328+
isRuntimeInstalled = nodeRuntime != nil && config.Config.IsRuntimeInstalled("node", nodeRuntime)
329+
}
330+
331+
if eslint == nil || !isToolInstalled || !isRuntimeInstalled {
323332
if eslint == nil {
324333
fmt.Println("Eslint tool configuration not found, adding and installing...")
325-
} else {
334+
} else if !isToolInstalled {
326335
fmt.Println("Eslint tool is not installed, installing...")
336+
} else if !isRuntimeInstalled {
337+
fmt.Println("Node.js runtime is not installed, installing eslint (which will install the runtime)...")
327338
}
328339

329340
err := config.InstallTool("eslint", eslint, "")
@@ -358,10 +369,15 @@ func runEslintAnalysis(workDirectory string, pathsToCheck []string, autoFix bool
358369
func runTrivyAnalysis(workDirectory string, pathsToCheck []string, outputFile string, outputFormat string) error {
359370
// Ensure Trivy tool is configured and installed
360371
trivy := config.Config.Tools()["trivy"]
361-
if trivy == nil || !config.Config.IsToolInstalled("trivy", trivy) {
372+
isToolInstalled := config.Config.IsToolInstalled("trivy", trivy)
373+
374+
// Trivy is a download-based tool (no runtime dependency), so runtime is always "installed"
375+
isRuntimeInstalled := true
376+
377+
if trivy == nil || !isToolInstalled || !isRuntimeInstalled {
362378
if trivy == nil {
363379
fmt.Println("Trivy tool configuration not found, adding and installing...")
364-
} else {
380+
} else if !isToolInstalled {
365381
fmt.Println("Trivy tool is not installed, installing...")
366382
}
367383

@@ -391,11 +407,22 @@ func runTrivyAnalysis(workDirectory string, pathsToCheck []string, outputFile st
391407
func runPmdAnalysis(workDirectory string, pathsToCheck []string, outputFile string, outputFormat string) error {
392408
// Ensure PMD tool is configured and installed
393409
pmd := config.Config.Tools()["pmd"]
394-
if pmd == nil || !config.Config.IsToolInstalled("pmd", pmd) {
410+
isToolInstalled := config.Config.IsToolInstalled("pmd", pmd)
411+
412+
// Also check if the runtime is installed
413+
var isRuntimeInstalled bool
414+
if pmd != nil {
415+
javaRuntime := config.Config.Runtimes()["java"]
416+
isRuntimeInstalled = javaRuntime != nil && config.Config.IsRuntimeInstalled("java", javaRuntime)
417+
}
418+
419+
if pmd == nil || !isToolInstalled || !isRuntimeInstalled {
395420
if pmd == nil {
396421
fmt.Println("PMD tool configuration not found, adding and installing...")
397-
} else {
422+
} else if !isToolInstalled {
398423
fmt.Println("PMD tool is not installed, installing...")
424+
} else if !isRuntimeInstalled {
425+
fmt.Println("Java runtime is not installed, installing PMD (which will install the runtime)...")
399426
}
400427

401428
err := config.InstallTool("pmd", pmd, "")
@@ -430,11 +457,22 @@ func runPmdAnalysis(workDirectory string, pathsToCheck []string, outputFile stri
430457
func runPylintAnalysis(workDirectory string, pathsToCheck []string, outputFile string, outputFormat string) error {
431458
// Ensure Pylint tool is configured and installed
432459
pylint := config.Config.Tools()["pylint"]
433-
if pylint == nil || !config.Config.IsToolInstalled("pylint", pylint) {
460+
isToolInstalled := config.Config.IsToolInstalled("pylint", pylint)
461+
462+
// Also check if the runtime is installed
463+
var isRuntimeInstalled bool
464+
if pylint != nil {
465+
pythonRuntime := config.Config.Runtimes()["python"]
466+
isRuntimeInstalled = pythonRuntime != nil && config.Config.IsRuntimeInstalled("python", pythonRuntime)
467+
}
468+
469+
if pylint == nil || !isToolInstalled || !isRuntimeInstalled {
434470
if pylint == nil {
435471
fmt.Println("Pylint tool configuration not found, adding and installing...")
436-
} else {
472+
} else if !isToolInstalled {
437473
fmt.Println("Pylint tool is not installed, installing...")
474+
} else if !isRuntimeInstalled {
475+
fmt.Println("Python runtime is not installed, installing Pylint (which will install the runtime)...")
438476
}
439477

440478
err := config.InstallTool("pylint", pylint, "")
@@ -469,11 +507,22 @@ func runPylintAnalysis(workDirectory string, pathsToCheck []string, outputFile s
469507
func runDartAnalyzer(workDirectory string, pathsToCheck []string, outputFile string, outputFormat string) error {
470508
// Ensure Dart Analyzer tool is configured and installed
471509
dartanalyzer := config.Config.Tools()["dartanalyzer"]
472-
if dartanalyzer == nil || !config.Config.IsToolInstalled("dartanalyzer", dartanalyzer) {
510+
isToolInstalled := config.Config.IsToolInstalled("dartanalyzer", dartanalyzer)
511+
512+
// Also check if the runtime is installed
513+
var isRuntimeInstalled bool
514+
if dartanalyzer != nil {
515+
dartRuntime := config.Config.Runtimes()["dart"]
516+
isRuntimeInstalled = dartRuntime != nil && config.Config.IsRuntimeInstalled("dart", dartRuntime)
517+
}
518+
519+
if dartanalyzer == nil || !isToolInstalled || !isRuntimeInstalled {
473520
if dartanalyzer == nil {
474521
fmt.Println("Dart analyzer tool configuration not found, adding and installing...")
475-
} else {
522+
} else if !isToolInstalled {
476523
fmt.Println("Dart analyzer tool is not installed, installing...")
524+
} else if !isRuntimeInstalled {
525+
fmt.Println("Dart runtime is not installed, installing Dart Analyzer (which will install the runtime)...")
477526
}
478527

479528
err := config.InstallTool("dartanalyzer", dartanalyzer, "")
@@ -507,11 +556,22 @@ func runDartAnalyzer(workDirectory string, pathsToCheck []string, outputFile str
507556
func runSemgrepAnalysis(workDirectory string, pathsToCheck []string, outputFile string, outputFormat string) error {
508557
// Ensure Semgrep tool is configured and installed
509558
semgrep := config.Config.Tools()["semgrep"]
510-
if semgrep == nil || !config.Config.IsToolInstalled("semgrep", semgrep) {
559+
isToolInstalled := config.Config.IsToolInstalled("semgrep", semgrep)
560+
561+
// Also check if the runtime is installed
562+
var isRuntimeInstalled bool
563+
if semgrep != nil {
564+
pythonRuntime := config.Config.Runtimes()["python"]
565+
isRuntimeInstalled = pythonRuntime != nil && config.Config.IsRuntimeInstalled("python", pythonRuntime)
566+
}
567+
568+
if semgrep == nil || !isToolInstalled || !isRuntimeInstalled {
511569
if semgrep == nil {
512570
fmt.Println("Semgrep tool configuration not found, adding and installing...")
513-
} else {
571+
} else if !isToolInstalled {
514572
fmt.Println("Semgrep tool is not installed, installing...")
573+
} else if !isRuntimeInstalled {
574+
fmt.Println("Python runtime is not installed, installing Semgrep (which will install the runtime)...")
515575
}
516576

517577
err := config.InstallTool("semgrep", semgrep, "")
@@ -546,11 +606,22 @@ func runSemgrepAnalysis(workDirectory string, pathsToCheck []string, outputFile
546606
func runLizardAnalysis(workDirectory string, pathsToCheck []string, outputFile string, outputFormat string) error {
547607
// Ensure Lizard tool is configured and installed
548608
lizardTool := config.Config.Tools()["lizard"]
549-
if lizardTool == nil || !config.Config.IsToolInstalled("lizard", lizardTool) {
609+
isToolInstalled := config.Config.IsToolInstalled("lizard", lizardTool)
610+
611+
// Also check if the runtime is installed
612+
var isRuntimeInstalled bool
613+
if lizardTool != nil {
614+
pythonRuntime := config.Config.Runtimes()["python"]
615+
isRuntimeInstalled = pythonRuntime != nil && config.Config.IsRuntimeInstalled("python", pythonRuntime)
616+
}
617+
618+
if lizardTool == nil || !isToolInstalled || !isRuntimeInstalled {
550619
if lizardTool == nil {
551620
fmt.Println("Lizard tool configuration not found, adding and installing...")
552-
} else {
621+
} else if !isToolInstalled {
553622
fmt.Println("Lizard tool is not installed, installing...")
623+
} else if !isRuntimeInstalled {
624+
fmt.Println("Python runtime is not installed, installing Lizard (which will install the runtime)...")
554625
}
555626

556627
err := config.InstallTool("lizard", lizardTool, "")
@@ -604,10 +675,15 @@ func runLizardAnalysis(workDirectory string, pathsToCheck []string, outputFile s
604675
func runEnigmaAnalysis(workDirectory string, pathsToCheck []string, outputFile string, outputFormat string) error {
605676
// Ensure Enigma tool is configured and installed
606677
enigma := config.Config.Tools()["codacy-enigma-cli"]
607-
if enigma == nil || !config.Config.IsToolInstalled("codacy-enigma-cli", enigma) {
678+
isToolInstalled := config.Config.IsToolInstalled("codacy-enigma-cli", enigma)
679+
680+
// Enigma is a download-based tool (no runtime dependency), so runtime is always "installed"
681+
isRuntimeInstalled := true
682+
683+
if enigma == nil || !isToolInstalled || !isRuntimeInstalled {
608684
if enigma == nil {
609685
fmt.Println("Enigma tool configuration not found, adding and installing...")
610-
} else {
686+
} else if !isToolInstalled {
611687
fmt.Println("Enigma tool is not installed, installing...")
612688
}
613689

cmd/analyze_test.go

Lines changed: 91 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,10 @@
11
package cmd
22

33
import (
4+
"codacy/cli-v2/plugins"
45
"testing"
6+
7+
"github.com/stretchr/testify/assert"
58
)
69

710
func TestGetFileExtension(t *testing.T) {
@@ -130,3 +133,91 @@ func TestIsToolSupportedForFile(t *testing.T) {
130133
})
131134
}
132135
}
136+
137+
func TestEslintInstallationValidationLogic(t *testing.T) {
138+
// Test the logic that determines when ESLint installation should be triggered
139+
// This tests the core logic from runEslintAnalysis without complex config setup
140+
141+
tests := []struct {
142+
name string
143+
eslint *plugins.ToolInfo
144+
nodeRuntime *plugins.RuntimeInfo
145+
isToolInstalled bool
146+
expectInstallTriggered bool
147+
description string
148+
}{
149+
{
150+
name: "tool_nil_should_trigger_install",
151+
eslint: nil,
152+
nodeRuntime: nil,
153+
isToolInstalled: false,
154+
expectInstallTriggered: true,
155+
description: "When ESLint tool is nil, installation should be triggered",
156+
},
157+
{
158+
name: "tool_exists_runtime_nil_should_trigger_install",
159+
eslint: &plugins.ToolInfo{
160+
Name: "eslint",
161+
Version: "8.38.0",
162+
Runtime: "node",
163+
},
164+
nodeRuntime: nil,
165+
isToolInstalled: true,
166+
expectInstallTriggered: true,
167+
description: "When ESLint tool exists but runtime is nil, installation should be triggered",
168+
},
169+
{
170+
name: "tool_not_installed_runtime_exists_should_trigger_install",
171+
eslint: &plugins.ToolInfo{
172+
Name: "eslint",
173+
Version: "8.38.0",
174+
Runtime: "node",
175+
},
176+
nodeRuntime: &plugins.RuntimeInfo{
177+
Name: "node",
178+
Version: "22.2.0",
179+
},
180+
isToolInstalled: false,
181+
expectInstallTriggered: true,
182+
description: "When ESLint tool is not installed, installation should be triggered",
183+
},
184+
{
185+
name: "both_tool_and_runtime_available_should_not_trigger_install",
186+
eslint: &plugins.ToolInfo{
187+
Name: "eslint",
188+
Version: "8.38.0",
189+
Runtime: "node",
190+
},
191+
nodeRuntime: &plugins.RuntimeInfo{
192+
Name: "node",
193+
Version: "22.2.0",
194+
},
195+
isToolInstalled: true,
196+
expectInstallTriggered: false,
197+
description: "When both ESLint tool and runtime are available and installed, installation should not be triggered",
198+
},
199+
}
200+
201+
for _, tt := range tests {
202+
t.Run(tt.name, func(t *testing.T) {
203+
// This mimics the logic from runEslintAnalysis
204+
205+
// Check if the runtime is installed
206+
var isRuntimeInstalled bool
207+
if tt.eslint != nil {
208+
isRuntimeInstalled = tt.nodeRuntime != nil
209+
// In the real code, this would also call Config.IsRuntimeInstalled,
210+
// but for this test we simplify it to just checking if runtime exists
211+
}
212+
213+
// Apply the installation trigger logic
214+
shouldInstall := tt.eslint == nil || !tt.isToolInstalled || !isRuntimeInstalled
215+
216+
if tt.expectInstallTriggered {
217+
assert.True(t, shouldInstall, tt.description)
218+
} else {
219+
assert.False(t, shouldInstall, tt.description)
220+
}
221+
})
222+
}
223+
}

config-file/configFile.go

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -9,8 +9,8 @@ import (
99
)
1010

1111
type configFile struct {
12-
RUNTIMES []string
13-
TOOLS []string
12+
RUNTIMES []string `yaml:"runtimes"`
13+
TOOLS []string `yaml:"tools"`
1414
}
1515

1616
func parseConfigFile(configContents []byte) error {

config/tools-installer.go

Lines changed: 17 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -57,6 +57,7 @@ func InstallTools(config *ConfigType, registry string) error {
5757

5858
// InstallTool installs a specific tool
5959
func InstallTool(name string, toolInfo *plugins.ToolInfo, registry string) error {
60+
6061
// If toolInfo is nil, it means the tool is not in the configuration
6162
// Add it with the default version
6263
if toolInfo == nil {
@@ -77,14 +78,26 @@ func InstallTool(name string, toolInfo *plugins.ToolInfo, registry string) error
7778
}
7879
}
7980

80-
// Check if the tool is already installed
81-
if Config.IsToolInstalled(name, toolInfo) {
82-
logger.Info("Tool already installed", logrus.Fields{
81+
// Check if the tool is already installed AND its runtime is available
82+
isToolInstalled := Config.IsToolInstalled(name, toolInfo)
83+
84+
// Also check if the required runtime is installed
85+
var isRuntimeInstalled bool
86+
if toolInfo.Runtime != "" {
87+
runtimeInfo, exists := Config.Runtimes()[toolInfo.Runtime]
88+
isRuntimeInstalled = exists && Config.IsRuntimeInstalled(toolInfo.Runtime, runtimeInfo)
89+
} else {
90+
// No runtime dependency
91+
isRuntimeInstalled = true
92+
}
93+
94+
if isToolInstalled && isRuntimeInstalled {
95+
logger.Info("Tool and runtime already installed", logrus.Fields{
8396
"tool": name,
8497
"version": toolInfo.Version,
8598
"runtime": toolInfo.Runtime,
8699
})
87-
fmt.Printf("Tool %s v%s is already installed\n", name, toolInfo.Version)
100+
fmt.Printf("Tool %s v%s and its runtime are already installed\n", name, toolInfo.Version)
88101
return nil
89102
}
90103

0 commit comments

Comments
 (0)