Skip to content

Commit da30241

Browse files
added tests and fixed bug with runtime installation
1 parent 9eba6ae commit da30241

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
@@ -320,11 +320,22 @@ func getToolName(toolName string, version string) string {
320320
func runEslintAnalysis(workDirectory string, pathsToCheck []string, autoFix bool, outputFile string, outputFormat string) error {
321321
// Ensure ESLint tool is configured and installed
322322
eslint := config.Config.Tools()["eslint"]
323-
if eslint == nil || !config.Config.IsToolInstalled("eslint", eslint) {
323+
isToolInstalled := config.Config.IsToolInstalled("eslint", eslint)
324+
325+
// Also check if the runtime is installed
326+
var isRuntimeInstalled bool
327+
if eslint != nil {
328+
nodeRuntime := config.Config.Runtimes()["node"]
329+
isRuntimeInstalled = nodeRuntime != nil && config.Config.IsRuntimeInstalled("node", nodeRuntime)
330+
}
331+
332+
if eslint == nil || !isToolInstalled || !isRuntimeInstalled {
324333
if eslint == nil {
325334
fmt.Println("Eslint tool configuration not found, adding and installing...")
326-
} else {
335+
} else if !isToolInstalled {
327336
fmt.Println("Eslint tool is not installed, installing...")
337+
} else if !isRuntimeInstalled {
338+
fmt.Println("Node.js runtime is not installed, installing eslint (which will install the runtime)...")
328339
}
329340

330341
err := config.InstallTool("eslint", eslint, "")
@@ -359,10 +370,15 @@ func runEslintAnalysis(workDirectory string, pathsToCheck []string, autoFix bool
359370
func runTrivyAnalysis(workDirectory string, pathsToCheck []string, outputFile string, outputFormat string) error {
360371
// Ensure Trivy tool is configured and installed
361372
trivy := config.Config.Tools()["trivy"]
362-
if trivy == nil || !config.Config.IsToolInstalled("trivy", trivy) {
373+
isToolInstalled := config.Config.IsToolInstalled("trivy", trivy)
374+
375+
// Trivy is a download-based tool (no runtime dependency), so runtime is always "installed"
376+
isRuntimeInstalled := true
377+
378+
if trivy == nil || !isToolInstalled || !isRuntimeInstalled {
363379
if trivy == nil {
364380
fmt.Println("Trivy tool configuration not found, adding and installing...")
365-
} else {
381+
} else if !isToolInstalled {
366382
fmt.Println("Trivy tool is not installed, installing...")
367383
}
368384

@@ -392,11 +408,22 @@ func runTrivyAnalysis(workDirectory string, pathsToCheck []string, outputFile st
392408
func runPmdAnalysis(workDirectory string, pathsToCheck []string, outputFile string, outputFormat string) error {
393409
// Ensure PMD tool is configured and installed
394410
pmd := config.Config.Tools()["pmd"]
395-
if pmd == nil || !config.Config.IsToolInstalled("pmd", pmd) {
411+
isToolInstalled := config.Config.IsToolInstalled("pmd", pmd)
412+
413+
// Also check if the runtime is installed
414+
var isRuntimeInstalled bool
415+
if pmd != nil {
416+
javaRuntime := config.Config.Runtimes()["java"]
417+
isRuntimeInstalled = javaRuntime != nil && config.Config.IsRuntimeInstalled("java", javaRuntime)
418+
}
419+
420+
if pmd == nil || !isToolInstalled || !isRuntimeInstalled {
396421
if pmd == nil {
397422
fmt.Println("PMD tool configuration not found, adding and installing...")
398-
} else {
423+
} else if !isToolInstalled {
399424
fmt.Println("PMD tool is not installed, installing...")
425+
} else if !isRuntimeInstalled {
426+
fmt.Println("Java runtime is not installed, installing PMD (which will install the runtime)...")
400427
}
401428

402429
err := config.InstallTool("pmd", pmd, "")
@@ -431,11 +458,22 @@ func runPmdAnalysis(workDirectory string, pathsToCheck []string, outputFile stri
431458
func runPylintAnalysis(workDirectory string, pathsToCheck []string, outputFile string, outputFormat string) error {
432459
// Ensure Pylint tool is configured and installed
433460
pylint := config.Config.Tools()["pylint"]
434-
if pylint == nil || !config.Config.IsToolInstalled("pylint", pylint) {
461+
isToolInstalled := config.Config.IsToolInstalled("pylint", pylint)
462+
463+
// Also check if the runtime is installed
464+
var isRuntimeInstalled bool
465+
if pylint != nil {
466+
pythonRuntime := config.Config.Runtimes()["python"]
467+
isRuntimeInstalled = pythonRuntime != nil && config.Config.IsRuntimeInstalled("python", pythonRuntime)
468+
}
469+
470+
if pylint == nil || !isToolInstalled || !isRuntimeInstalled {
435471
if pylint == nil {
436472
fmt.Println("Pylint tool configuration not found, adding and installing...")
437-
} else {
473+
} else if !isToolInstalled {
438474
fmt.Println("Pylint tool is not installed, installing...")
475+
} else if !isRuntimeInstalled {
476+
fmt.Println("Python runtime is not installed, installing Pylint (which will install the runtime)...")
439477
}
440478

441479
err := config.InstallTool("pylint", pylint, "")
@@ -470,11 +508,22 @@ func runPylintAnalysis(workDirectory string, pathsToCheck []string, outputFile s
470508
func runDartAnalyzer(workDirectory string, pathsToCheck []string, outputFile string, outputFormat string) error {
471509
// Ensure Dart Analyzer tool is configured and installed
472510
dartanalyzer := config.Config.Tools()["dartanalyzer"]
473-
if dartanalyzer == nil || !config.Config.IsToolInstalled("dartanalyzer", dartanalyzer) {
511+
isToolInstalled := config.Config.IsToolInstalled("dartanalyzer", dartanalyzer)
512+
513+
// Also check if the runtime is installed
514+
var isRuntimeInstalled bool
515+
if dartanalyzer != nil {
516+
dartRuntime := config.Config.Runtimes()["dart"]
517+
isRuntimeInstalled = dartRuntime != nil && config.Config.IsRuntimeInstalled("dart", dartRuntime)
518+
}
519+
520+
if dartanalyzer == nil || !isToolInstalled || !isRuntimeInstalled {
474521
if dartanalyzer == nil {
475522
fmt.Println("Dart analyzer tool configuration not found, adding and installing...")
476-
} else {
523+
} else if !isToolInstalled {
477524
fmt.Println("Dart analyzer tool is not installed, installing...")
525+
} else if !isRuntimeInstalled {
526+
fmt.Println("Dart runtime is not installed, installing Dart Analyzer (which will install the runtime)...")
478527
}
479528

480529
err := config.InstallTool("dartanalyzer", dartanalyzer, "")
@@ -508,11 +557,22 @@ func runDartAnalyzer(workDirectory string, pathsToCheck []string, outputFile str
508557
func runSemgrepAnalysis(workDirectory string, pathsToCheck []string, outputFile string, outputFormat string) error {
509558
// Ensure Semgrep tool is configured and installed
510559
semgrep := config.Config.Tools()["semgrep"]
511-
if semgrep == nil || !config.Config.IsToolInstalled("semgrep", semgrep) {
560+
isToolInstalled := config.Config.IsToolInstalled("semgrep", semgrep)
561+
562+
// Also check if the runtime is installed
563+
var isRuntimeInstalled bool
564+
if semgrep != nil {
565+
pythonRuntime := config.Config.Runtimes()["python"]
566+
isRuntimeInstalled = pythonRuntime != nil && config.Config.IsRuntimeInstalled("python", pythonRuntime)
567+
}
568+
569+
if semgrep == nil || !isToolInstalled || !isRuntimeInstalled {
512570
if semgrep == nil {
513571
fmt.Println("Semgrep tool configuration not found, adding and installing...")
514-
} else {
572+
} else if !isToolInstalled {
515573
fmt.Println("Semgrep tool is not installed, installing...")
574+
} else if !isRuntimeInstalled {
575+
fmt.Println("Python runtime is not installed, installing Semgrep (which will install the runtime)...")
516576
}
517577

518578
err := config.InstallTool("semgrep", semgrep, "")
@@ -547,11 +607,22 @@ func runSemgrepAnalysis(workDirectory string, pathsToCheck []string, outputFile
547607
func runLizardAnalysis(workDirectory string, pathsToCheck []string, outputFile string, outputFormat string) error {
548608
// Ensure Lizard tool is configured and installed
549609
lizardTool := config.Config.Tools()["lizard"]
550-
if lizardTool == nil || !config.Config.IsToolInstalled("lizard", lizardTool) {
610+
isToolInstalled := config.Config.IsToolInstalled("lizard", lizardTool)
611+
612+
// Also check if the runtime is installed
613+
var isRuntimeInstalled bool
614+
if lizardTool != nil {
615+
pythonRuntime := config.Config.Runtimes()["python"]
616+
isRuntimeInstalled = pythonRuntime != nil && config.Config.IsRuntimeInstalled("python", pythonRuntime)
617+
}
618+
619+
if lizardTool == nil || !isToolInstalled || !isRuntimeInstalled {
551620
if lizardTool == nil {
552621
fmt.Println("Lizard tool configuration not found, adding and installing...")
553-
} else {
622+
} else if !isToolInstalled {
554623
fmt.Println("Lizard tool is not installed, installing...")
624+
} else if !isRuntimeInstalled {
625+
fmt.Println("Python runtime is not installed, installing Lizard (which will install the runtime)...")
555626
}
556627

557628
err := config.InstallTool("lizard", lizardTool, "")
@@ -605,10 +676,15 @@ func runLizardAnalysis(workDirectory string, pathsToCheck []string, outputFile s
605676
func runEnigmaAnalysis(workDirectory string, pathsToCheck []string, outputFile string, outputFormat string) error {
606677
// Ensure Enigma tool is configured and installed
607678
enigma := config.Config.Tools()["codacy-enigma-cli"]
608-
if enigma == nil || !config.Config.IsToolInstalled("codacy-enigma-cli", enigma) {
679+
isToolInstalled := config.Config.IsToolInstalled("codacy-enigma-cli", enigma)
680+
681+
// Enigma is a download-based tool (no runtime dependency), so runtime is always "installed"
682+
isRuntimeInstalled := true
683+
684+
if enigma == nil || !isToolInstalled || !isRuntimeInstalled {
609685
if enigma == nil {
610686
fmt.Println("Enigma tool configuration not found, adding and installing...")
611-
} else {
687+
} else if !isToolInstalled {
612688
fmt.Println("Enigma tool is not installed, installing...")
613689
}
614690

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)