Skip to content

Commit 10796cb

Browse files
refactor: Take default versions from yaml declarations (#121)
Refactor: improve plugin management and configuration handling Major changes: - Introduce new plugin management system with PluginManager - Refactor runtime and tool configuration handling - Centralize version management for tools and runtimes - Remove hardcoded tool configurations in favor of plugin-based approach - Improve error handling and logging - Add support for dynamic runtime dependencies - Sort tools and runtimes in configuration output The changes make the codebase more maintainable and extensible by: - Reducing code duplication - Improving separation of concerns - Making tool and runtime management more dynamic - Centralizing configuration management
1 parent f894e3d commit 10796cb

File tree

27 files changed

+679
-503
lines changed

27 files changed

+679
-503
lines changed

.codacy/codacy.yaml

Lines changed: 7 additions & 7 deletions

.github/workflows/it-test.yml

Lines changed: 0 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -40,12 +40,6 @@ jobs:
4040
with:
4141
fetch-depth: 0 # Needed for git history
4242

43-
- name: Set up Go
44-
uses: actions/setup-go@v5
45-
with:
46-
go-version: '1.21'
47-
cache: true
48-
4943
- name: Download CLI binaries
5044
uses: actions/download-artifact@v4
5145
with:

cmd/init.go

Lines changed: 127 additions & 60 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@ import (
44
codacyclient "codacy/cli-v2/codacy-client"
55
"codacy/cli-v2/config"
66
"codacy/cli-v2/domain"
7+
"codacy/cli-v2/plugins"
78
"codacy/cli-v2/tools"
89
"codacy/cli-v2/tools/lizard"
910
"codacy/cli-v2/tools/pylint"
@@ -12,6 +13,7 @@ import (
1213
"log"
1314
"os"
1415
"path/filepath"
16+
"sort"
1517
"strings"
1618

1719
"github.com/spf13/cobra"
@@ -151,46 +153,65 @@ func createConfigurationFiles(tools []domain.Tool, cliLocalMode bool) error {
151153
return nil
152154
}
153155

156+
// Map tool UUIDs to their names
157+
var toolNameMap = map[string]string{
158+
ESLint: "eslint",
159+
Trivy: "trivy",
160+
PyLint: "pylint",
161+
PMD: "pmd",
162+
DartAnalyzer: "dartanalyzer",
163+
Semgrep: "semgrep",
164+
Lizard: "lizard",
165+
}
166+
167+
// RuntimePluginConfig holds the structure of the runtime plugin.yaml file
168+
type RuntimePluginConfig struct {
169+
Name string `yaml:"name"`
170+
Description string `yaml:"description"`
171+
DefaultVersion string `yaml:"default_version"`
172+
}
173+
154174
func configFileTemplate(tools []domain.Tool) string {
155175
// Maps to track which tools are enabled
156176
toolsMap := make(map[string]bool)
157177
toolVersions := make(map[string]string)
158178

159179
// Track needed runtimes
160-
needsNode := false
161-
needsPython := false
162-
needsDart := false
163-
needsJava := false
164-
165-
// Default versions
166-
defaultVersions := map[string]string{
167-
ESLint: "8.57.0",
168-
Trivy: "0.59.1",
169-
PyLint: "3.3.6",
170-
PMD: "6.55.0",
171-
DartAnalyzer: "3.7.2",
172-
Semgrep: "1.78.0",
173-
Lizard: "1.17.19",
174-
}
180+
neededRuntimes := make(map[string]bool)
181+
182+
// Get tool versions from plugin configurations
183+
defaultVersions := plugins.GetToolVersions()
184+
185+
// Get runtime versions all at once
186+
runtimeVersions := plugins.GetRuntimeVersions()
187+
188+
// Get tool runtime dependencies
189+
runtimeDependencies := plugins.GetToolRuntimeDependencies()
175190

176191
// Build map of enabled tools with their versions
177192
for _, tool := range tools {
178193
toolsMap[tool.Uuid] = true
179194
if tool.Version != "" {
180195
toolVersions[tool.Uuid] = tool.Version
181196
} else {
182-
toolVersions[tool.Uuid] = defaultVersions[tool.Uuid]
197+
toolName := toolNameMap[tool.Uuid]
198+
if defaultVersion, ok := defaultVersions[toolName]; ok {
199+
toolVersions[tool.Uuid] = defaultVersion
200+
}
183201
}
184202

185-
// Check if tool needs a runtime
186-
if tool.Uuid == ESLint {
187-
needsNode = true
188-
} else if tool.Uuid == PyLint || tool.Uuid == Lizard {
189-
needsPython = true
190-
} else if tool.Uuid == DartAnalyzer {
191-
needsDart = true
192-
} else if tool.Uuid == PMD {
193-
needsJava = true
203+
// Get the tool's runtime dependency
204+
toolName := toolNameMap[tool.Uuid]
205+
if toolName != "" {
206+
if runtime, ok := runtimeDependencies[toolName]; ok {
207+
// Handle special case for dartanalyzer which can use either dart or flutter
208+
if toolName == "dartanalyzer" {
209+
// For now, default to dart runtime
210+
neededRuntimes["dart"] = true
211+
} else {
212+
neededRuntimes[runtime] = true
213+
}
214+
}
194215
}
195216
}
196217

@@ -200,55 +221,101 @@ func configFileTemplate(tools []domain.Tool) string {
200221

201222
// Only include runtimes needed by the enabled tools
202223
if len(tools) > 0 {
203-
if needsNode {
204-
sb.WriteString(" - [email protected]\n")
224+
// Create a sorted slice of runtimes
225+
var sortedRuntimes []string
226+
for runtime := range neededRuntimes {
227+
sortedRuntimes = append(sortedRuntimes, runtime)
205228
}
206-
if needsPython {
207-
sb.WriteString(" - [email protected]\n")
229+
sort.Strings(sortedRuntimes)
230+
231+
// Write sorted runtimes
232+
for _, runtime := range sortedRuntimes {
233+
sb.WriteString(fmt.Sprintf(" - %s@%s\n", runtime, runtimeVersions[runtime]))
208234
}
209-
if needsDart {
210-
sb.WriteString(" - [email protected]\n")
235+
} else {
236+
// In local mode with no tools specified, include only the necessary runtimes
237+
supportedTools, err := plugins.GetSupportedTools()
238+
if err != nil {
239+
log.Printf("Warning: failed to get supported tools: %v", err)
240+
return sb.String()
211241
}
212-
if needsJava {
213-
sb.WriteString(" - [email protected]\n")
242+
243+
// Get runtimes needed by supported tools
244+
for toolName := range supportedTools {
245+
if runtime, ok := runtimeDependencies[toolName]; ok {
246+
if toolName == "dartanalyzer" {
247+
neededRuntimes["dart"] = true
248+
} else {
249+
neededRuntimes[runtime] = true
250+
}
251+
}
252+
}
253+
254+
// Create a sorted slice of runtimes
255+
var sortedRuntimes []string
256+
for runtime := range neededRuntimes {
257+
sortedRuntimes = append(sortedRuntimes, runtime)
258+
}
259+
sort.Strings(sortedRuntimes)
260+
261+
// Write sorted runtimes
262+
for _, runtime := range sortedRuntimes {
263+
sb.WriteString(fmt.Sprintf(" - %s@%s\n", runtime, runtimeVersions[runtime]))
214264
}
215-
} else {
216-
// In local mode with no tools specified, include all runtimes
217-
sb.WriteString(" - [email protected]\n")
218-
sb.WriteString(" - [email protected]\n")
219-
sb.WriteString(" - [email protected]\n")
220-
sb.WriteString(" - [email protected]\n")
221265
}
222266

223267
sb.WriteString("tools:\n")
224268

225269
// If we have tools from the API (enabled tools), use only those
226270
if len(tools) > 0 {
227-
// Add only the tools that are in the API response (enabled tools)
228-
uuidToName := map[string]string{
229-
ESLint: "eslint",
230-
Trivy: "trivy",
231-
PyLint: "pylint",
232-
PMD: "pmd",
233-
DartAnalyzer: "dartanalyzer",
234-
Semgrep: "semgrep",
235-
Lizard: "lizard",
236-
}
237-
238-
for uuid, name := range uuidToName {
271+
// Create a sorted slice of tool names
272+
var sortedTools []string
273+
for uuid, name := range toolNameMap {
239274
if toolsMap[uuid] {
240-
sb.WriteString(fmt.Sprintf(" - %s@%s\n", name, toolVersions[uuid]))
275+
sortedTools = append(sortedTools, name)
276+
}
277+
}
278+
sort.Strings(sortedTools)
279+
280+
// Write sorted tools
281+
for _, name := range sortedTools {
282+
// Find the UUID for this tool name to get its version
283+
for uuid, toolName := range toolNameMap {
284+
if toolName == name && toolsMap[uuid] {
285+
version := toolVersions[uuid]
286+
sb.WriteString(fmt.Sprintf(" - %s@%s\n", name, version))
287+
break
288+
}
241289
}
242290
}
243291
} else {
244-
// If no tools were specified (local mode), include all defaults
245-
sb.WriteString(fmt.Sprintf(" - eslint@%s\n", defaultVersions[ESLint]))
246-
sb.WriteString(fmt.Sprintf(" - trivy@%s\n", defaultVersions[Trivy]))
247-
sb.WriteString(fmt.Sprintf(" - pylint@%s\n", defaultVersions[PyLint]))
248-
sb.WriteString(fmt.Sprintf(" - pmd@%s\n", defaultVersions[PMD]))
249-
sb.WriteString(fmt.Sprintf(" - dartanalyzer@%s\n", defaultVersions[DartAnalyzer]))
250-
sb.WriteString(fmt.Sprintf(" - semgrep@%s\n", defaultVersions[Semgrep]))
251-
sb.WriteString(fmt.Sprintf(" - lizard@%s\n", defaultVersions[Lizard]))
292+
// If no tools were specified (local mode), include all tools in sorted order
293+
var sortedTools []string
294+
295+
// Get supported tools from plugin system
296+
supportedTools, err := plugins.GetSupportedTools()
297+
if err != nil {
298+
log.Printf("Warning: failed to get supported tools: %v", err)
299+
return sb.String()
300+
}
301+
302+
// Convert map keys to slice and sort them
303+
for toolName := range supportedTools {
304+
if version, ok := defaultVersions[toolName]; ok {
305+
// Skip tools without a version
306+
if version != "" {
307+
sortedTools = append(sortedTools, toolName)
308+
}
309+
}
310+
}
311+
sort.Strings(sortedTools)
312+
313+
// Write sorted tools
314+
for _, toolName := range sortedTools {
315+
if version, ok := defaultVersions[toolName]; ok {
316+
sb.WriteString(fmt.Sprintf(" - %s@%s\n", toolName, version))
317+
}
318+
}
252319
}
253320

254321
return sb.String()

cmd/init_test.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -24,7 +24,7 @@ func TestConfigFileTemplate(t *testing.T) {
2424
expected: []string{
2525
2626
27-
"eslint@8.57.0",
27+
"eslint@9.3.0",
2828
2929
3030

config/runtimes-installer.go

Lines changed: 3 additions & 35 deletions
Original file line numberDiff line numberDiff line change
@@ -47,9 +47,8 @@ func InstallRuntimes(config *ConfigType) error {
4747

4848
// InstallRuntime installs a specific runtime
4949
func InstallRuntime(name string, runtimeInfo *plugins.RuntimeInfo) error {
50-
51-
// Check if the runtime is already installed
52-
if isRuntimeInstalled(runtimeInfo) {
50+
// Skip if already installed
51+
if Config.IsRuntimeInstalled(name, runtimeInfo) {
5352
logger.Info("Runtime already installed", logrus.Fields{
5453
"runtime": name,
5554
"version": runtimeInfo.Version,
@@ -65,7 +64,7 @@ func InstallRuntime(name string, runtimeInfo *plugins.RuntimeInfo) error {
6564
}
6665

6766
// Verify that the runtime binaries are available
68-
if !isRuntimeInstalled(runtimeInfo) {
67+
if !Config.IsRuntimeInstalled(name, runtimeInfo) {
6968
logger.Error("Runtime binaries not found after extraction", logrus.Fields{
7069
"runtime": name,
7170
"version": runtimeInfo.Version,
@@ -76,37 +75,6 @@ func InstallRuntime(name string, runtimeInfo *plugins.RuntimeInfo) error {
7675
return nil
7776
}
7877

79-
// isRuntimeInstalled checks if a runtime is already installed by checking for the binary
80-
func isRuntimeInstalled(runtimeInfo *plugins.RuntimeInfo) bool {
81-
// If there are no binaries, check the install directory
82-
if len(runtimeInfo.Binaries) == 0 {
83-
_, err := os.Stat(runtimeInfo.InstallDir)
84-
if err != nil {
85-
logger.Debug("Runtime install directory not found", logrus.Fields{
86-
"directory": runtimeInfo.InstallDir,
87-
"error": err,
88-
})
89-
return false
90-
}
91-
return true
92-
}
93-
94-
// Check if at least one binary exists
95-
for binaryName, binaryPath := range runtimeInfo.Binaries {
96-
_, err := os.Stat(binaryPath)
97-
if err != nil {
98-
logger.Debug("Runtime binary not found", logrus.Fields{
99-
"binary": binaryName,
100-
"path": binaryPath,
101-
"error": err,
102-
})
103-
return false
104-
}
105-
}
106-
107-
return true
108-
}
109-
11078
// downloadAndExtractRuntime downloads and extracts a runtime
11179
func downloadAndExtractRuntime(runtimeInfo *plugins.RuntimeInfo) error {
11280
// Create a file name for the downloaded archive

config/runtimes-installer_test.go

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -24,14 +24,14 @@ func TestIsRuntimeInstalled(t *testing.T) {
2424
}
2525

2626
// Test when the install directory doesn't exist
27-
assert.False(t, isRuntimeInstalled(runtimeInfoNoBinaries))
27+
assert.False(t, Config.IsRuntimeInstalled("test-runtime", runtimeInfoNoBinaries))
2828

2929
// Create the install directory
3030
err = os.MkdirAll(runtimeInfoNoBinaries.InstallDir, utils.DefaultDirPerms)
3131
assert.NoError(t, err)
3232

3333
// Test when the install directory exists
34-
assert.True(t, isRuntimeInstalled(runtimeInfoNoBinaries))
34+
assert.True(t, Config.IsRuntimeInstalled("test-runtime", runtimeInfoNoBinaries))
3535

3636
// Create a mock RuntimeInfo with binaries
3737
binPath := filepath.Join(tempDir, "test-runtime-bin")
@@ -45,12 +45,12 @@ func TestIsRuntimeInstalled(t *testing.T) {
4545
}
4646

4747
// Test when the binary doesn't exist
48-
assert.False(t, isRuntimeInstalled(runtimeInfoWithBinaries))
48+
assert.False(t, Config.IsRuntimeInstalled("test-runtime", runtimeInfoWithBinaries))
4949

5050
// Create a mock binary file
5151
_, err = os.Create(binPath)
5252
assert.NoError(t, err)
5353

5454
// Test when the binary exists
55-
assert.True(t, isRuntimeInstalled(runtimeInfoWithBinaries))
55+
assert.True(t, Config.IsRuntimeInstalled("test-runtime", runtimeInfoWithBinaries))
5656
}

config/tools-installer.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -58,7 +58,7 @@ func InstallTools(config *ConfigType, registry string) error {
5858
// InstallTool installs a specific tool
5959
func InstallTool(name string, toolInfo *plugins.ToolInfo, registry string) error {
6060
// Check if the tool is already installed
61-
if isToolInstalled(toolInfo) {
61+
if Config.IsToolInstalled(name, toolInfo) {
6262
logger.Info("Tool already installed", logrus.Fields{
6363
"tool": name,
6464
"version": toolInfo.Version,

0 commit comments

Comments
 (0)