Skip to content

Commit 12d3a79

Browse files
authored
Merge pull request #42 from codacy/refactor-node-plugin-try-2
refactor: implement runtime plugins with declarative configuration PLUTO-1369
2 parents 36db748 + 75a6851 commit 12d3a79

File tree

13 files changed

+621
-118
lines changed

13 files changed

+621
-118
lines changed

cmd/analyze.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -207,7 +207,7 @@ var analyzeCmd = &cobra.Command{
207207
eslint := config.Config.Tools()["eslint"]
208208
eslintInstallationDirectory := eslint.Info()["installDir"]
209209
nodeRuntime := config.Config.Runtimes()["node"]
210-
nodeBinary := nodeRuntime.Info()["node"]
210+
nodeBinary := nodeRuntime.Binaries["node"]
211211

212212
log.Printf("Running %s...\n", toolToAnalyze)
213213
if outputFormat == "sarif" {

cmd/install.go

Lines changed: 7 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -20,28 +20,19 @@ var installCmd = &cobra.Command{
2020
Short: "Installs the tools specified in the project's config-file.",
2121
Long: "Installs all runtimes and tools specified in the project's config-file file.",
2222
Run: func(cmd *cobra.Command, args []string) {
23-
// install runtimes
24-
fetchRuntimes(&cfg.Config)
25-
// install tools
26-
fetchTools(&cfg.Config)
23+
installRuntimes(&cfg.Config)
24+
installTools(&cfg.Config)
2725
},
2826
}
2927

30-
func fetchRuntimes(config *cfg.ConfigType) {
31-
for _, r := range config.Runtimes() {
32-
switch r.Name() {
33-
case "node":
34-
err := cfg.InstallNode(r)
35-
if err != nil {
36-
log.Fatal(err)
37-
}
38-
default:
39-
log.Fatal("Unknown runtime:", r.Name())
40-
}
28+
func installRuntimes(config *cfg.ConfigType) {
29+
err := cfg.InstallRuntimes()
30+
if err != nil {
31+
log.Fatal(err)
4132
}
4233
}
4334

44-
func fetchTools(config *cfg.ConfigType) {
35+
func installTools(config *cfg.ConfigType) {
4536
for _, tool := range config.Tools() {
4637
switch tool.Name() {
4738
case "eslint":

config-file/configFile.go

Lines changed: 12 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@ package config_file
22

33
import (
44
"codacy/cli-v2/config"
5+
"codacy/cli-v2/plugins"
56
"gopkg.in/yaml.v3"
67
"os"
78
)
@@ -17,12 +18,22 @@ func parseConfigFile(configContents []byte) error {
1718
return err
1819
}
1920

21+
// Convert the runtime strings to RuntimeConfig objects
22+
runtimeConfigs := make([]plugins.RuntimeConfig, 0, len(configFile.RUNTIMES))
2023
for _, rt := range configFile.RUNTIMES {
2124
ct, err := parseConfigTool(rt)
2225
if err != nil {
2326
return err
2427
}
25-
config.Config.AddRuntime(config.NewRuntime(ct.name, ct.version))
28+
runtimeConfigs = append(runtimeConfigs, plugins.RuntimeConfig{
29+
Name: ct.name,
30+
Version: ct.version,
31+
})
32+
}
33+
34+
// Add all runtimes at once
35+
if err := config.Config.AddRuntimes(runtimeConfigs); err != nil {
36+
return err
2637
}
2738

2839
for _, tl := range configFile.TOOLS {

config/config.go

Lines changed: 18 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,8 @@ import (
44
"log"
55
"os"
66
"path/filepath"
7+
8+
"codacy/cli-v2/plugins"
79
)
810

911
type ConfigType struct {
@@ -14,7 +16,7 @@ type ConfigType struct {
1416
localCodacyDirectory string
1517
projectConfigFile string
1618

17-
runtimes map[string]*Runtime
19+
runtimes map[string]*plugins.RuntimeInfo
1820
tools map[string]*Runtime
1921
}
2022

@@ -42,12 +44,23 @@ func (c *ConfigType) ProjectConfigFile() string {
4244
return c.projectConfigFile
4345
}
4446

45-
func (c *ConfigType) Runtimes() map[string]*Runtime {
47+
func (c *ConfigType) Runtimes() map[string]*plugins.RuntimeInfo {
4648
return c.runtimes
4749
}
4850

49-
func (c *ConfigType) AddRuntime(r *Runtime) {
50-
c.runtimes[r.Name()] = r
51+
func (c *ConfigType) AddRuntimes(configs []plugins.RuntimeConfig) error {
52+
// Process the runtime configurations using the plugins.ProcessRuntimes function
53+
runtimeInfoMap, err := plugins.ProcessRuntimes(configs, c.runtimesDirectory)
54+
if err != nil {
55+
return err
56+
}
57+
58+
// Store the runtime information in the config
59+
for name, info := range runtimeInfoMap {
60+
c.runtimes[name] = info
61+
}
62+
63+
return nil
5164
}
5265

5366
// TODO do inheritance with tool
@@ -103,7 +116,7 @@ func Init() {
103116

104117
Config.initCodacyDirs()
105118

106-
Config.runtimes = make(map[string]*Runtime)
119+
Config.runtimes = make(map[string]*plugins.RuntimeInfo)
107120
Config.tools = make(map[string]*Runtime)
108121
}
109122

config/eslint-utils.go

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@ import (
55
"log"
66
"os/exec"
77
"path"
8+
"codacy/cli-v2/plugins"
89
)
910

1011
func genInfoEslint(r *Runtime) map[string]string {
@@ -20,20 +21,20 @@ func genInfoEslint(r *Runtime) map[string]string {
2021
/*
2122
* This installs eslint using node's npm alongside its sarif extension
2223
*/
23-
func InstallEslint(nodeRuntime *Runtime, eslint *Runtime, registry string) error {
24+
func InstallEslint(nodeRuntime *plugins.RuntimeInfo, eslint *Runtime, registry string) error {
2425
log.Println("Installing ESLint")
2526

2627
eslintInstallArg := fmt.Sprintf("%s@%s", eslint.Name(), eslint.Version())
2728
if registry != "" {
2829
fmt.Println("Using registry:", registry)
29-
configCmd := exec.Command(nodeRuntime.Info()["npm"], "config", "set", "registry", registry)
30+
configCmd := exec.Command(nodeRuntime.Binaries["npm"], "config", "set", "registry", registry)
3031
if configOut, err := configCmd.Output(); err != nil {
3132
fmt.Println("Error setting npm registry:", err)
3233
fmt.Println(string(configOut))
3334
return err
3435
}
3536
}
36-
cmd := exec.Command(nodeRuntime.Info()["npm"], "install", "--prefix", eslint.Info()["installDir"],
37+
cmd := exec.Command(nodeRuntime.Binaries["npm"], "install", "--prefix", eslint.Info()["installDir"],
3738
eslintInstallArg, "@microsoft/eslint-formatter-sarif")
3839
fmt.Println(cmd.String())
3940
// to use the chdir command we needed to create the folder before, we can change this after

config/node-utils.go

Lines changed: 0 additions & 90 deletions
This file was deleted.

config/runtime.go

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@ import (
44
"fmt"
55
)
66

7+
// Note that this is only used by tools
78
type Runtime struct {
89
name string
910
version string
@@ -26,10 +27,9 @@ func (r *Runtime) Info() map[string]string {
2627
return r.info
2728
}
2829

30+
// populateInfo populates the runtime info
2931
func (r *Runtime) populateInfo() {
3032
switch r.Name() {
31-
case "node":
32-
r.info = genInfoNode(r)
3333
case "eslint":
3434
r.info = genInfoEslint(r)
3535
}

config/runtimes-installer.go

Lines changed: 102 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,102 @@
1+
package config
2+
3+
import (
4+
"codacy/cli-v2/plugins"
5+
"codacy/cli-v2/utils"
6+
"fmt"
7+
"log"
8+
"os"
9+
"path/filepath"
10+
"strings"
11+
)
12+
13+
// InstallRuntimes installs all runtimes defined in the configuration
14+
func InstallRuntimes() error {
15+
for name, runtimeInfo := range Config.Runtimes() {
16+
err := InstallRuntime(name, runtimeInfo)
17+
if err != nil {
18+
return fmt.Errorf("failed to install runtime %s: %w", name, err)
19+
}
20+
}
21+
return nil
22+
}
23+
24+
// InstallRuntime installs a specific runtime
25+
func InstallRuntime(name string, runtimeInfo *plugins.RuntimeInfo) error {
26+
// Check if the runtime is already installed
27+
if isRuntimeInstalled(runtimeInfo) {
28+
fmt.Printf("Runtime %s v%s is already installed\n", name, runtimeInfo.Version)
29+
return nil
30+
}
31+
32+
// Download and extract the runtime
33+
err := downloadAndExtractRuntime(runtimeInfo)
34+
if err != nil {
35+
return fmt.Errorf("failed to download and extract runtime %s: %w", name, err)
36+
}
37+
38+
return nil
39+
}
40+
41+
// isRuntimeInstalled checks if a runtime is already installed by checking for the binary
42+
func isRuntimeInstalled(runtimeInfo *plugins.RuntimeInfo) bool {
43+
// If there are no binaries, check the install directory
44+
if len(runtimeInfo.Binaries) == 0 {
45+
_, err := os.Stat(runtimeInfo.InstallDir)
46+
return err == nil
47+
}
48+
49+
// Check if at least one binary exists
50+
for _, binaryPath := range runtimeInfo.Binaries {
51+
_, err := os.Stat(binaryPath)
52+
if err == nil {
53+
return true
54+
}
55+
}
56+
57+
return false
58+
}
59+
60+
// downloadAndExtractRuntime downloads and extracts a runtime
61+
func downloadAndExtractRuntime(runtimeInfo *plugins.RuntimeInfo) error {
62+
// Create a file name for the downloaded archive
63+
fileName := filepath.Base(runtimeInfo.DownloadURL)
64+
downloadPath := filepath.Join(Config.RuntimesDirectory(), fileName)
65+
66+
// Check if the file already exists
67+
_, err := os.Stat(downloadPath)
68+
if os.IsNotExist(err) {
69+
// Download the file
70+
log.Printf("Downloading %s v%s...\n", runtimeInfo.Name, runtimeInfo.Version)
71+
downloadPath, err = utils.DownloadFile(runtimeInfo.DownloadURL, Config.RuntimesDirectory())
72+
if err != nil {
73+
return fmt.Errorf("failed to download runtime: %w", err)
74+
}
75+
} else if err != nil {
76+
return fmt.Errorf("error checking for existing download: %w", err)
77+
} else {
78+
log.Printf("Using existing download for %s v%s\n", runtimeInfo.Name, runtimeInfo.Version)
79+
}
80+
81+
// Open the downloaded file
82+
file, err := os.Open(downloadPath)
83+
if err != nil {
84+
return fmt.Errorf("failed to open downloaded file: %w", err)
85+
}
86+
defer file.Close()
87+
88+
// Extract based on file extension
89+
log.Printf("Extracting %s v%s...\n", runtimeInfo.Name, runtimeInfo.Version)
90+
if strings.HasSuffix(fileName, ".zip") {
91+
err = utils.ExtractZip(file.Name(), Config.RuntimesDirectory())
92+
} else {
93+
err = utils.ExtractTarGz(file, Config.RuntimesDirectory())
94+
}
95+
96+
if err != nil {
97+
return fmt.Errorf("failed to extract runtime: %w", err)
98+
}
99+
100+
log.Printf("Successfully installed %s v%s\n", runtimeInfo.Name, runtimeInfo.Version)
101+
return nil
102+
}

0 commit comments

Comments
 (0)