From 331b6ef9dd2ff865476d69f9765565a674c5da92 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jo=C3=A3o=20Costa?= Date: Tue, 21 May 2024 15:19:12 +0100 Subject: [PATCH 1/3] WIP: config parsing and usage (replace hard-coded vals) --- config/configTool.go | 1 + 1 file changed, 1 insertion(+) create mode 100644 config/configTool.go diff --git a/config/configTool.go b/config/configTool.go new file mode 100644 index 0000000..d912156 --- /dev/null +++ b/config/configTool.go @@ -0,0 +1 @@ +package config From 7f083ca1b06480978a16607cb6ff884f5d5bdd39 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jo=C3=A3o=20Costa?= Date: Tue, 21 May 2024 15:24:16 +0100 Subject: [PATCH 2/3] WIP: config parsing and usage (replace hard-coded vals) --- cli-v2.go | 26 +++++++++------- config.go | 7 ----- config/configFile.go | 72 ++++++++++++++++++++++++++++++++++++++++++++ config/configTool.go | 27 +++++++++++++++++ 4 files changed, 114 insertions(+), 18 deletions(-) delete mode 100644 config.go create mode 100644 config/configFile.go diff --git a/cli-v2.go b/cli-v2.go index da2a373..c9dcfb1 100644 --- a/cli-v2.go +++ b/cli-v2.go @@ -2,6 +2,7 @@ package main import ( "codacy/cli-v2/cmd" + "codacy/cli-v2/config" "context" "fmt" "io" @@ -13,7 +14,6 @@ import ( "runtime" "github.com/mholt/archiver/v4" - "gopkg.in/yaml.v3" ) // https://nodejs.org/dist/v22.2.0/node-v22.2.0-linux-arm64.tar.xz @@ -181,15 +181,22 @@ func installESLint(npmExecutablePath string, ESLintversion string, codacyPath st } } -func main() { - content, err := os.ReadFile(".codacy/codacy.yaml") - if err != nil { - log.Fatal(err) +func fetchRuntimes(runtimes map[string]*config.Runtime) { + for _, runtime := range runtimes { + switch runtime.Name() { + case "node": + fmt.Println("Fetching node...") + default: + fmt.Println("Unknown runtime:", runtime.Name()) + } } +} - config := Config{} - if err := yaml.Unmarshal(content, &config); err != nil { - log.Fatalf("error: %v", err) +func main() { + _, configErr := config.ReadConfigFile(".codacy/codacy.yaml") + if configErr != nil { + log.Fatal(configErr) + return } homePath, err := os.UserHomeDir() @@ -219,9 +226,6 @@ func main() { log.Fatal(err) } - fmt.Println(codacyDirectory) - - fmt.Println(config) downloadNodeURL := getNodeDownloadURL("v22.2.0") nodeTar, err := downloadFile(downloadNodeURL, codacyDirectory) diff --git a/config.go b/config.go deleted file mode 100644 index 9e71ffe..0000000 --- a/config.go +++ /dev/null @@ -1,7 +0,0 @@ -package main - - -type Config struct { - RUNTIMES []string - TOOLS []string -} diff --git a/config/configFile.go b/config/configFile.go new file mode 100644 index 0000000..404db77 --- /dev/null +++ b/config/configFile.go @@ -0,0 +1,72 @@ +package config + +import ( + "gopkg.in/yaml.v3" + "os" +) + +type configFile struct { + RUNTIMES []string + TOOLS []string +} + +type Runtime struct { + name string + version string + tools []ConfigTool +} + +func (r *Runtime) Name() string { + return r.name +} + +func (r *Runtime) Version() string { + return r.version +} + +func (r *Runtime) Tools() []ConfigTool { + return r.tools +} + +func (r *Runtime) AddTool(tool *ConfigTool) { + r.tools = append(r.tools, *tool) +} + +func parseConfigFile(configContents []byte) (map[string]*Runtime, error) { + configFile := configFile{} + if err := yaml.Unmarshal(configContents, &configFile); err != nil { + return nil, err + } + + runtimes := make(map[string]*Runtime) + for _, rt := range configFile.RUNTIMES { + ct, err := parseConfigTool(rt) + if err != nil { + } + runtimes[ct.name] = &Runtime{ + name: ct.name, + version: ct.version, + } + } + + for _, tl := range configFile.TOOLS { + ct, err := parseConfigTool(tl) + if err != nil { + } + switch ct.name { + case "eslint": + runtimes["node"].AddTool(ct) + } + } + + return runtimes, nil +} + +func ReadConfigFile(configPath string) (map[string]*Runtime, error) { + content, err := os.ReadFile(configPath) + if err != nil { + return nil, err + } + + return parseConfigFile(content) +} \ No newline at end of file diff --git a/config/configTool.go b/config/configTool.go index d912156..67e75b7 100644 --- a/config/configTool.go +++ b/config/configTool.go @@ -1 +1,28 @@ package config + +import ( + "errors" + "strings" +) + +type ConfigTool struct { + name string + version string +} + +func (ct *ConfigTool) Name() string { + return ct.name +} + +func (ct *ConfigTool) Version() string { + return ct.version +} + +func parseConfigTool(tool string) (*ConfigTool, error) { + toolSplited := strings.Split(tool, "@") + if len(toolSplited) != 2 { + return &ConfigTool{}, errors.New("invalid tool format") + } + + return &ConfigTool{name: toolSplited[0], version: toolSplited[1]}, nil +} \ No newline at end of file From ce84edebc224edf9d01febee2697e266a68e2ba5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jo=C3=A3o=20Costa?= Date: Tue, 21 May 2024 16:45:48 +0100 Subject: [PATCH 3/3] Use config file instead of hard-coded values --- .codacy/codacy.yaml | 2 +- cli-v2.go | 130 ++++++++++++++++++++----------------------- config/configFile.go | 7 +++ 3 files changed, 68 insertions(+), 71 deletions(-) diff --git a/.codacy/codacy.yaml b/.codacy/codacy.yaml index 9e85104..bbcd08d 100644 --- a/.codacy/codacy.yaml +++ b/.codacy/codacy.yaml @@ -1,4 +1,4 @@ runtimes: - - node@22.1.0 + - node@22.2.0 tools: - eslint@9.3.0 \ No newline at end of file diff --git a/cli-v2.go b/cli-v2.go index c9dcfb1..7eac626 100644 --- a/cli-v2.go +++ b/cli-v2.go @@ -16,11 +16,7 @@ import ( "github.com/mholt/archiver/v4" ) -// https://nodejs.org/dist/v22.2.0/node-v22.2.0-linux-arm64.tar.xz -// https://nodejs.org/dist/v22.2.0/node-v22.2.0-darwin-x64.tar.gz -// https://nodejs.org/dist/v13.14.0/node-v13.14.0-win-x64.zip -// https://nodejs.org/dist/v22.2.0/node-v22.2.0-linux-armv7l.tar.xz -func getNodeDownloadURL(version string) string { +func getNodeFileName(nodeRuntime *config.Runtime) string { // Detect the OS and architecture goos := runtime.GOOS goarch := runtime.GOARCH @@ -40,13 +36,20 @@ func getNodeDownloadURL(version string) string { nodeArch = goarch } + return fmt.Sprintf("node-v%s-%s-%s", nodeRuntime.Version(), goos, nodeArch) +} + +func getNodeDownloadURL(nodeRuntime *config.Runtime) string { + // Detect the OS and architecture + goos := runtime.GOOS + // Construct the Node.js download URL extension := "tar.gz" if goos == "windows" { extension = "zip" } - downloadURL := fmt.Sprintf("https://nodejs.org/dist/%s/node-%s-%s-%s.%s", version, version, goos, nodeArch, extension) + downloadURL := fmt.Sprintf("https://nodejs.org/dist/v%s/%s.%s", nodeRuntime.Version(), getNodeFileName(nodeRuntime), extension) return downloadURL } @@ -89,29 +92,14 @@ func downloadFile(url string, destDir string) (string, error) { return destPath, nil } -func extract(t *os.File, codacyDirectory string) { - +func extract(t *os.File, targetDir string) { format := archiver.CompressedArchive{ Compression: archiver.Gz{}, Archival: archiver.Tar{}, } - // format, _, err := archiver.Identify(t.Name(), nil) - // if err != nil { - // log.Fatal(err) - // } - - // the list of files we want out of the archive; any - // directories will include all their contents unless - // we return fs.SkipDir from our handler - // (leave this nil to walk ALL files from the archive) - // fileList := []string{"file1.txt", "subfolder"} - handler := func(ctx context.Context, f archiver.File) error { - - fmt.Printf("Contents of %s:\n", f.NameInArchive) - - path := filepath.Join(codacyDirectory, "runtimes", f.NameInArchive) + path := filepath.Join(targetDir, f.NameInArchive) switch f.IsDir() { case true: @@ -123,20 +111,19 @@ func extract(t *os.File, codacyDirectory string) { } case false: + log.Print("extracting: " + f.NameInArchive) + // if is a symlink if f.LinkTarget != "" { os.Remove(path) err := os.Symlink(f.LinkTarget, path) if err != nil { log.Fatal(err) } - return nil } // write a file - fmt.Println("extracting: " + f.NameInArchive) - fmt.Println("targe link: " + f.LinkTarget) w, err := os.OpenFile(path, os.O_RDWR|os.O_CREATE|os.O_TRUNC, f.Mode()) if err != nil { log.Fatal(err) @@ -159,14 +146,12 @@ func extract(t *os.File, codacyDirectory string) { if err != nil { log.Fatal(err) } - } -func installESLint(npmExecutablePath string, ESLintversion string, codacyPath string) { - - fmt.Println("Installing ESLint") +func installESLint(npmExecutablePath string, ESLintversion string, toolsDirectory string) { + log.Println("Installing ESLint") - eslintInstallationFolder := filepath.Join(codacyPath, "tools", ESLintversion) + eslintInstallationFolder := filepath.Join(toolsDirectory, ESLintversion) cmd := exec.Command(npmExecutablePath, "install", "--prefix", eslintInstallationFolder, ESLintversion, "@microsoft/eslint-formatter-sarif") // to use the chdir command we needed to create the folder before, we can change this after @@ -181,24 +166,46 @@ func installESLint(npmExecutablePath string, ESLintversion string, codacyPath st } } -func fetchRuntimes(runtimes map[string]*config.Runtime) { +func fetchRuntimes(runtimes map[string]*config.Runtime, runtimesDirectory string) { for _, runtime := range runtimes { switch runtime.Name() { case "node": - fmt.Println("Fetching node...") + // TODO should delete downloaded archive + // TODO check for deflated archive + log.Println("Fetching node...") + downloadNodeURL := getNodeDownloadURL(runtime) + nodeTar, err := downloadFile(downloadNodeURL, runtimesDirectory) + if err != nil { + log.Fatal(err) + } + + // deflate node archive + t, err := os.Open(nodeTar) + defer t.Close() + if err != nil { + log.Fatal(err) + } + extract(t, runtimesDirectory) default: - fmt.Println("Unknown runtime:", runtime.Name()) + log.Fatal("Unknown runtime:", runtime.Name()) } } } -func main() { - _, configErr := config.ReadConfigFile(".codacy/codacy.yaml") - if configErr != nil { - log.Fatal(configErr) - return +func fetchTools(runtime *config.Runtime, runtimesDirectory string, toolsDirectory string) { + for _, tool := range runtime.Tools() { + switch tool.Name() { + case "eslint": + npmPath := filepath.Join(runtimesDirectory, getNodeFileName(runtime), + "bin", "npm") + installESLint(npmPath, "eslint@" + tool.Version(), toolsDirectory) + default: + log.Fatal("Unknown tool:", tool.Name()) + } } +} +func main() { homePath, err := os.UserHomeDir() if err != nil { log.Fatal(err) @@ -207,47 +214,30 @@ func main() { codacyDirectory := filepath.Join(homePath, ".cache", "codacy") runtimesDirectory := filepath.Join(codacyDirectory, "runtimes") toolsDirectory := filepath.Join(codacyDirectory, "tools") - - fmt.Println("creating: " + codacyDirectory) - err = os.MkdirAll(codacyDirectory, 0777) - if err != nil { + fmt.Println("creating: " + codacyDirectory) + if os.MkdirAll(codacyDirectory, 0777) != nil { log.Fatal(err) } - - fmt.Println("creating: " + runtimesDirectory) - err = os.MkdirAll(runtimesDirectory, 0777) - if err != nil { + fmt.Println("creating: " + runtimesDirectory) + if os.MkdirAll(runtimesDirectory, 0777) != nil { log.Fatal(err) } - - fmt.Println("creating: " + toolsDirectory) - err = os.MkdirAll(toolsDirectory, 0777) - if err != nil { + fmt.Println("creating: " + toolsDirectory) + if os.MkdirAll(toolsDirectory, 0777) != nil { log.Fatal(err) } - downloadNodeURL := getNodeDownloadURL("v22.2.0") - - nodeTar, err := downloadFile(downloadNodeURL, codacyDirectory) - if err != nil { - log.Fatal(err) + // TODO can use a variable to stored the "local" codacy dir + runtimes, configErr := config.ReadConfigFile(filepath.Join(".codacy", "codacy.yaml")) + if configErr != nil { + log.Fatal(configErr) } - fmt.Println("Downloaded node: " + nodeTar) - - t, err := os.Open(nodeTar) - defer t.Close() - if err != nil { - log.Fatal(err) + // install runtimes + fetchRuntimes(runtimes, runtimesDirectory) + for _, r := range runtimes { + fetchTools(r, runtimesDirectory, toolsDirectory) } - fmt.Println("About to extract node: " + t.Name()) - extract(t, codacyDirectory) - - npmPath := filepath.Join(codacyDirectory, "runtimes", "node-v22.2.0-darwin-x64", "bin", "npm") - - fmt.Println("About to install eslint") - installESLint(npmPath, "eslint@9.3.0", codacyDirectory) - cmd.Execute() } diff --git a/config/configFile.go b/config/configFile.go index 404db77..8fb7c0f 100644 --- a/config/configFile.go +++ b/config/configFile.go @@ -1,6 +1,7 @@ package config import ( + "fmt" "gopkg.in/yaml.v3" "os" ) @@ -32,6 +33,10 @@ func (r *Runtime) AddTool(tool *ConfigTool) { r.tools = append(r.tools, *tool) } +func (r *Runtime) FullName() string { + return fmt.Sprintf("%s-%s", r.name, r.version) +} + func parseConfigFile(configContents []byte) (map[string]*Runtime, error) { configFile := configFile{} if err := yaml.Unmarshal(configContents, &configFile); err != nil { @@ -42,6 +47,7 @@ func parseConfigFile(configContents []byte) (map[string]*Runtime, error) { for _, rt := range configFile.RUNTIMES { ct, err := parseConfigTool(rt) if err != nil { + return nil, err } runtimes[ct.name] = &Runtime{ name: ct.name, @@ -52,6 +58,7 @@ func parseConfigFile(configContents []byte) (map[string]*Runtime, error) { for _, tl := range configFile.TOOLS { ct, err := parseConfigTool(tl) if err != nil { + return nil, err } switch ct.name { case "eslint":