Skip to content

Commit b14e5a3

Browse files
feature: install missing runtimes when installing tool - PLUTO-1426 (#140)
* WIP: working for node, not for java, python also worked before but not working right now * working for JAVA * working with all runtimes, still needs more clear code * cleaning code, remove old approach of installing missing runtime during installation * fixed tests * handle complexity issue * code review changes
1 parent c9c7dd1 commit b14e5a3

File tree

2 files changed

+144
-5
lines changed

2 files changed

+144
-5
lines changed

config/config.go

Lines changed: 136 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@ import (
55
"log"
66
"os"
77
"path/filepath"
8+
"strings"
89

910
"codacy/cli-v2/plugins"
1011
"codacy/cli-v2/utils"
@@ -68,6 +69,84 @@ func (c *ConfigType) Runtimes() map[string]*plugins.RuntimeInfo {
6869
return c.runtimes
6970
}
7071

72+
// loadConfigOrInitializeEmpty reads the existing YAML config or returns an empty configuration if the file doesn't exist
73+
func (c *ConfigType) loadConfigOrInitializeEmpty(codacyPath string) (map[string]interface{}, error) {
74+
content, err := os.ReadFile(codacyPath)
75+
if err != nil && !os.IsNotExist(err) {
76+
return nil, fmt.Errorf("error reading codacy.yaml: %w", err)
77+
}
78+
79+
var config map[string]interface{}
80+
if len(content) > 0 {
81+
if err := yaml.Unmarshal(content, &config); err != nil {
82+
return nil, fmt.Errorf("error parsing codacy.yaml: %w", err)
83+
}
84+
} else {
85+
config = make(map[string]interface{})
86+
}
87+
88+
return config, nil
89+
}
90+
91+
// updateRuntimesList updates or adds a runtime entry in the runtimes list
92+
func updateRuntimesList(runtimes []interface{}, name, version string) []interface{} {
93+
runtimeEntry := fmt.Sprintf("%s@%s", name, version)
94+
95+
// Check if runtime already exists and update it
96+
for i, r := range runtimes {
97+
if runtime, ok := r.(string); ok {
98+
if strings.Split(runtime, "@")[0] == name {
99+
runtimes[i] = runtimeEntry
100+
return runtimes
101+
}
102+
}
103+
}
104+
105+
// Add new runtime if not found
106+
return append(runtimes, runtimeEntry)
107+
}
108+
109+
// writeConfig writes the config back to the YAML file
110+
func (c *ConfigType) writeConfig(codacyPath string, config map[string]interface{}) error {
111+
yamlData, err := yaml.Marshal(config)
112+
if err != nil {
113+
return fmt.Errorf("error marshaling codacy.yaml: %w", err)
114+
}
115+
116+
// Ensure directory exists
117+
if err := os.MkdirAll(filepath.Dir(codacyPath), utils.DefaultDirPerms); err != nil {
118+
return fmt.Errorf("error creating .codacy directory: %w", err)
119+
}
120+
121+
if err := os.WriteFile(codacyPath, yamlData, utils.DefaultFilePerms); err != nil {
122+
return fmt.Errorf("error writing codacy.yaml: %w", err)
123+
}
124+
125+
return nil
126+
}
127+
128+
// addRuntimeToCodacyYaml adds or updates a runtime entry in codacy.yaml as a YAML list
129+
func (c *ConfigType) addRuntimeToCodacyYaml(name string, version string) error {
130+
codacyPath := filepath.Join(c.localCodacyDirectory, "codacy.yaml")
131+
132+
config, err := c.loadConfigOrInitializeEmpty(codacyPath)
133+
if err != nil {
134+
return err
135+
}
136+
137+
// Get or create runtimes list
138+
runtimes, ok := config["runtimes"].([]interface{})
139+
if !ok {
140+
runtimes = make([]interface{}, 0)
141+
}
142+
143+
// Update runtimes list
144+
runtimes = updateRuntimesList(runtimes, name, version)
145+
config["runtimes"] = runtimes
146+
147+
return c.writeConfig(codacyPath, config)
148+
}
149+
71150
func (c *ConfigType) AddRuntimes(configs []plugins.RuntimeConfig) error {
72151
// Process the runtime configurations using the plugins.ProcessRuntimes function
73152
runtimeInfoMap, err := plugins.ProcessRuntimes(configs, c.runtimesDirectory)
@@ -78,6 +157,11 @@ func (c *ConfigType) AddRuntimes(configs []plugins.RuntimeConfig) error {
78157
// Store the runtime information in the config
79158
for name, info := range runtimeInfoMap {
80159
c.runtimes[name] = info
160+
161+
// Update codacy.yaml with the new runtime
162+
if err := c.addRuntimeToCodacyYaml(name, info.Version); err != nil {
163+
return fmt.Errorf("failed to update codacy.yaml with runtime %s: %w", name, err)
164+
}
81165
}
82166

83167
return nil
@@ -88,7 +172,58 @@ func (c *ConfigType) Tools() map[string]*plugins.ToolInfo {
88172
}
89173

90174
func (c *ConfigType) AddTools(configs []plugins.ToolConfig) error {
91-
// Process the tool configurations using the plugins.ProcessTools function
175+
// Get the plugin manager to access tool configurations
176+
pluginManager := plugins.GetPluginManager()
177+
178+
// Ensure all required runtimes are present before processing tools
179+
for _, toolConfig := range configs {
180+
// Get the tool's plugin configuration to access runtime info
181+
pluginConfig, err := pluginManager.GetToolConfig(toolConfig.Name)
182+
if err != nil {
183+
return fmt.Errorf("failed to get plugin config for tool %s: %w", toolConfig.Name, err)
184+
}
185+
186+
if pluginConfig.Runtime != "" {
187+
// Special handling for dartanalyzer - check for existing dart or flutter runtimes
188+
if toolConfig.Name == "dartanalyzer" {
189+
// Check if either dart or flutter runtime is already available
190+
if runtimeInfo := c.runtimes["flutter"]; runtimeInfo != nil {
191+
// Flutter runtime exists, use it
192+
continue
193+
}
194+
if runtimeInfo := c.runtimes["dart"]; runtimeInfo != nil {
195+
// Dart runtime exists, use it
196+
continue
197+
}
198+
// Neither runtime exists, proceed with installing dart runtime
199+
pluginConfig.Runtime = "dart"
200+
}
201+
202+
runtimeInfo := c.runtimes[pluginConfig.Runtime]
203+
if runtimeInfo == nil {
204+
// Get the default version for the runtime
205+
defaultVersions := plugins.GetRuntimeVersions()
206+
version, ok := defaultVersions[pluginConfig.Runtime]
207+
if !ok {
208+
return fmt.Errorf("no default version found for runtime %s", pluginConfig.Runtime)
209+
}
210+
211+
// Add the runtime to the config
212+
fmt.Println("Adding missing runtime to codacy.yaml", pluginConfig.Runtime, version)
213+
if err := c.AddRuntimes([]plugins.RuntimeConfig{{Name: pluginConfig.Runtime, Version: version}}); err != nil {
214+
return fmt.Errorf("failed to add runtime %s: %w", pluginConfig.Runtime, err)
215+
}
216+
217+
// Fetch the runtimeInfo again
218+
runtimeInfo = c.runtimes[pluginConfig.Runtime]
219+
if runtimeInfo == nil {
220+
return fmt.Errorf("runtime %s still missing after adding to config", pluginConfig.Runtime)
221+
}
222+
}
223+
}
224+
}
225+
226+
// Now safe to process tools
92227
toolInfoMap, err := plugins.ProcessTools(configs, c.toolsDirectory, c.runtimes)
93228
if err != nil {
94229
return err

config/tools-installer_test.go

Lines changed: 8 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -20,8 +20,10 @@ func TestAddTools(t *testing.T) {
2020

2121
// Initialize config with test directories
2222
Config = ConfigType{
23-
toolsDirectory: tempDir,
24-
tools: make(map[string]*plugins.ToolInfo),
23+
toolsDirectory: tempDir,
24+
tools: make(map[string]*plugins.ToolInfo),
25+
runtimes: make(map[string]*plugins.RuntimeInfo),
26+
localCodacyDirectory: tempDir, // Add this to ensure .codacy/codacy.yaml is created in the temp dir
2527
}
2628

2729
// Create a list of tool configs for testing
@@ -94,8 +96,10 @@ func TestAddDownloadBasedTool(t *testing.T) {
9496

9597
// Initialize config with test directories
9698
Config = ConfigType{
97-
toolsDirectory: tempDir,
98-
tools: make(map[string]*plugins.ToolInfo),
99+
toolsDirectory: tempDir,
100+
tools: make(map[string]*plugins.ToolInfo),
101+
runtimes: make(map[string]*plugins.RuntimeInfo),
102+
localCodacyDirectory: tempDir, // Add this to ensure .codacy/codacy.yaml is created in the temp dir
99103
}
100104

101105
// Create a list of tool configs for testing

0 commit comments

Comments
 (0)