From 00931ce75da44c52f72242f15cc1582424f748a1 Mon Sep 17 00:00:00 2001 From: Andrew Mak Date: Fri, 13 Sep 2019 14:13:19 -0400 Subject: [PATCH 01/11] remove hardcode appsody Signed-off-by: Andrew Mak --- actions/project.go | 16 ++++++++-------- utils/project.go | 14 ++++---------- utils/project_test.go | 8 +------- 3 files changed, 13 insertions(+), 25 deletions(-) diff --git a/actions/project.go b/actions/project.go index 6a7788be..4601a0e9 100644 --- a/actions/project.go +++ b/actions/project.go @@ -78,18 +78,18 @@ func ValidateProject(c *cli.Context) { validationStatus := "success" // result could be ProjectType or string, so define as an interface var validationResult interface{} - language, buildType, isAppsody := utils.DetermineProjectInfo(projectPath) + language, buildType := utils.DetermineProjectInfo(projectPath) validationResult = ProjectType{ Language: language, BuildType: buildType, } - if isAppsody { - validated, err := utils.SuccessfullyCallAppsodyInit(projectPath) - if !validated { - validationStatus = "failed" - validationResult = err.Error() - } - } + // if isAppsody { + // validated, err := utils.SuccessfullyCallAppsodyInit(projectPath) + // if !validated { + // validationStatus = "failed" + // validationResult = err.Error() + // } + // } response := ValidationResponse{ Status: validationStatus, diff --git a/utils/project.go b/utils/project.go index 35a0308e..98590a16 100644 --- a/utils/project.go +++ b/utils/project.go @@ -34,9 +34,9 @@ type CWSettings struct { MavenProperties []string `json:"mavenProperties,omitempty"` } -// DetermineProjectInfo returns the language and build-type of a project, as well as if it's an Appsody project -func DetermineProjectInfo(projectPath string) (string, string, bool) { - language, buildType, isAppsody := "unknown", "docker", false +// DetermineProjectInfo returns the language and build-type of a project +func DetermineProjectInfo(projectPath string) (string, string) { + language, buildType := "unknown", "docker" if PathExists(path.Join(projectPath, "pom.xml")) { language = "java" buildType = determineJavaBuildType(projectPath) @@ -49,13 +49,7 @@ func DetermineProjectInfo(projectPath string) (string, string, bool) { language = "swift" buildType = "swift" } - if PathExists(path.Join(projectPath, "stack.yaml")) { - isAppsody = true - } - if PathExists(path.Join(projectPath, ".appsody-config.yaml")) { - isAppsody = true - } - return language, buildType, isAppsody + return language, buildType } // CheckProjectPath will stop the process and return an error if path does not diff --git a/utils/project_test.go b/utils/project_test.go index 48d2f330..648caccc 100644 --- a/utils/project_test.go +++ b/utils/project_test.go @@ -27,41 +27,35 @@ func TestDetermineProjectInfo(t *testing.T) { in string wantLanguage string wantBuildType string - wantIsAppsody bool wantedErr error }{ "success case: liberty project": { in: path.Join("..", "resources", "test", "liberty-project"), wantLanguage: "java", wantBuildType: "liberty", - wantIsAppsody: false, }, "success case: spring project": { in: path.Join("..", "resources", "test", "spring-project"), wantLanguage: "java", wantBuildType: "spring", - wantIsAppsody: false, }, "success case: node.js project": { in: path.Join("..", "resources", "test", "node-project"), wantLanguage: "nodejs", wantBuildType: "nodejs", - wantIsAppsody: false, }, "success case: swift project": { in: path.Join("..", "resources", "test", "swift-project"), wantLanguage: "swift", wantBuildType: "swift", - wantIsAppsody: false, }, } for name, test := range tests { t.Run(name, func(t *testing.T) { - gotLanguage, gotBuildType, gotIsAppsody := DetermineProjectInfo(test.in) + gotLanguage, gotBuildType := DetermineProjectInfo(test.in) assert.Equal(t, test.wantLanguage, gotLanguage) assert.Equal(t, test.wantBuildType, gotBuildType) - assert.Equal(t, test.wantIsAppsody, gotIsAppsody) }) } } From 6e97062264d3ca77c8eeb564d1da792ffea4af40 Mon Sep 17 00:00:00 2001 From: Andrew Mak Date: Fri, 13 Sep 2019 16:01:32 -0400 Subject: [PATCH 02/11] extensions api route Signed-off-by: Andrew Mak --- apiroutes/extensions.go | 55 +++++++++++++++++++++++++++++++++++++++++ 1 file changed, 55 insertions(+) create mode 100644 apiroutes/extensions.go diff --git a/apiroutes/extensions.go b/apiroutes/extensions.go new file mode 100644 index 00000000..40819264 --- /dev/null +++ b/apiroutes/extensions.go @@ -0,0 +1,55 @@ +/******************************************************************************* + * Copyright (c) 2019 IBM Corporation and others. + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v2.0 + * which accompanies this distribution, and is available at + * http://www.eclipse.org/legal/epl-v20.html + * + * Contributors: + * IBM Corporation - initial API and implementation + *******************************************************************************/ + +package apiroutes + +import ( + "encoding/json" + "io/ioutil" + "net/http" + + "github.com/eclipse/codewind-installer/config" +) + +type ( + // ExtensionCommand represents a command defined by a project extension + ExtensionCommand struct { + Name string `json:"name"` + Command string `json:"command"` + } + + // Extension represents a project extension defined by codewind.yaml + Extension struct { + ProjectType string `json:"projectType"` + Detection string `json:"detection"` + Commands []ExtensionCommand `json:"commands"` + } +) + +// GetExtensions gets project extensions from PFE's REST API. +func GetExtensions() ([]Extension, error) { + resp, err := http.Get(config.PFEApiRoute() + "extensions") + if err != nil { + return nil, err + } + + defer resp.Body.Close() + + byteArray, err := ioutil.ReadAll(resp.Body) + if err != nil { + return nil, err + } + + var extensions []Extension + json.Unmarshal(byteArray, &extensions) + + return extensions, nil +} From d79dd9133985d5310070f3c8ce5c067a742f5e77 Mon Sep 17 00:00:00 2001 From: Andrew Mak Date: Fri, 13 Sep 2019 16:22:29 -0400 Subject: [PATCH 03/11] call extensions api Signed-off-by: Andrew Mak --- actions/project.go | 46 ++++++++++++++++++++++------- utils/{appsody.go => extensions.go} | 17 +++++------ 2 files changed, 43 insertions(+), 20 deletions(-) rename utils/{appsody.go => extensions.go} (87%) diff --git a/actions/project.go b/actions/project.go index 4601a0e9..dc75c8e9 100644 --- a/actions/project.go +++ b/actions/project.go @@ -19,6 +19,7 @@ import ( "path" "regexp" + "github.com/eclipse/codewind-installer/apiroutes" "github.com/eclipse/codewind-installer/errors" "github.com/eclipse/codewind-installer/utils" "github.com/urfave/cli" @@ -52,9 +53,9 @@ func DownloadTemplate(c *cli.Context) { // Remove invalid characters from the string we will use // as the project name in the template. - r := regexp.MustCompile("[^a-zA-Z0-9._-]"); - projectName := r.ReplaceAllString(projectDir, ""); - if (len(projectName) == 0){ + r := regexp.MustCompile("[^a-zA-Z0-9._-]") + projectName := r.ReplaceAllString(projectDir, "") + if len(projectName) == 0 { projectName = "PROJ_NAME_PLACEHOLDER" } @@ -70,6 +71,24 @@ func DownloadTemplate(c *cli.Context) { } } +// testIsExtension tests if a project an extension project and run associated commands if necessary +func testIsExtension(projectPath string) (string, error) { + + extensions, err := apiroutes.GetExtensions() + if err != nil { + return "", err + } + + for _, extension := range extensions { + // check if project contains the detection file an extension defines + if extension.Detection != "" && utils.PathExists(path.Join(projectPath, extension.Detection)) { + return extension.ProjectType, nil + } + } + + return "", nil +} + // ValidateProject returns the language and buildType for a project at given filesystem path, // and writes a default .cw-settings file to that project func ValidateProject(c *cli.Context) { @@ -80,16 +99,21 @@ func ValidateProject(c *cli.Context) { var validationResult interface{} language, buildType := utils.DetermineProjectInfo(projectPath) validationResult = ProjectType{ - Language: language, + Language: language, BuildType: buildType, } - // if isAppsody { - // validated, err := utils.SuccessfullyCallAppsodyInit(projectPath) - // if !validated { - // validationStatus = "failed" - // validationResult = err.Error() - // } - // } + extensionType, err := testIsExtension(projectPath) + if extensionType != "" { + if err == nil { + validationResult = ProjectType{ + Language: language, + BuildType: extensionType, + } + } else { + validationStatus = "failed" + validationResult = err.Error() + } + } response := ValidationResponse{ Status: validationStatus, diff --git a/utils/appsody.go b/utils/extensions.go similarity index 87% rename from utils/appsody.go rename to utils/extensions.go index 6327406e..d2a3a95b 100644 --- a/utils/appsody.go +++ b/utils/extensions.go @@ -9,19 +9,19 @@ * IBM Corporation - initial API and implementation *******************************************************************************/ - package utils +package utils - import ( +import ( "bytes" + "log" "os" "os/exec" - "log" - "runtime" "path/filepath" + "runtime" ) - - // SuccessfullyCallAppsodyInit calls Appsody Init to initialise Appsody projects and returns a boolean to indicate success - func SuccessfullyCallAppsodyInit(projectPath string) (bool, error) { + +// SuccessfullyCallAppsodyInit calls Appsody Init to initialise Appsody projects and returns a boolean to indicate success +func SuccessfullyCallAppsodyInit(projectPath string) (bool, error) { cwd, err := os.Executable() if err != nil { log.Println("There was a problem with locating appsody binary") @@ -47,5 +47,4 @@ cmd.Wait() log.Println(output.String()) // Wait to finish execution, so we can read all output return true, nil - } - \ No newline at end of file +} From 3fea17d9f08f44e747408b151fd949e46e4066da Mon Sep 17 00:00:00 2001 From: Andrew Mak Date: Fri, 13 Sep 2019 17:26:09 -0400 Subject: [PATCH 04/11] call command Signed-off-by: Andrew Mak --- actions/project.go | 14 +++++++++++++- apiroutes/extensions.go | 13 ++++--------- utils/extensions.go | 35 +++++++++++++++++++---------------- 3 files changed, 36 insertions(+), 26 deletions(-) diff --git a/actions/project.go b/actions/project.go index dc75c8e9..532ebc42 100644 --- a/actions/project.go +++ b/actions/project.go @@ -80,9 +80,21 @@ func testIsExtension(projectPath string) (string, error) { } for _, extension := range extensions { + // check if project contains the detection file an extension defines if extension.Detection != "" && utils.PathExists(path.Join(projectPath, extension.Detection)) { - return extension.ProjectType, nil + + var cmdErr error + + // check if there are any commands to run + for _, command := range extension.Commands { + if command.Name == "postProjectValidate" { + cmdErr = utils.RunCommand(projectPath, command) + break + } + } + + return extension.ProjectType, cmdErr } } diff --git a/apiroutes/extensions.go b/apiroutes/extensions.go index 40819264..eae7a820 100644 --- a/apiroutes/extensions.go +++ b/apiroutes/extensions.go @@ -17,20 +17,15 @@ import ( "net/http" "github.com/eclipse/codewind-installer/config" + "github.com/eclipse/codewind-installer/utils" ) type ( - // ExtensionCommand represents a command defined by a project extension - ExtensionCommand struct { - Name string `json:"name"` - Command string `json:"command"` - } - // Extension represents a project extension defined by codewind.yaml Extension struct { - ProjectType string `json:"projectType"` - Detection string `json:"detection"` - Commands []ExtensionCommand `json:"commands"` + ProjectType string `json:"projectType"` + Detection string `json:"detection"` + Commands []utils.ExtensionCommand `json:"commands"` } ) diff --git a/utils/extensions.go b/utils/extensions.go index d2a3a95b..f35d3860 100644 --- a/utils/extensions.go +++ b/utils/extensions.go @@ -17,34 +17,37 @@ import ( "os" "os/exec" "path/filepath" - "runtime" ) -// SuccessfullyCallAppsodyInit calls Appsody Init to initialise Appsody projects and returns a boolean to indicate success -func SuccessfullyCallAppsodyInit(projectPath string) (bool, error) { +type ( + // ExtensionCommand represents a command defined by a project extension + ExtensionCommand struct { + Name string `json:"name"` + Command string `json:"command"` + Args []string `json:"args"` + } +) + +// RunCommand runs a command defined by an extension +func RunCommand(projectPath string, command ExtensionCommand) error { cwd, err := os.Executable() if err != nil { - log.Println("There was a problem with locating appsody binary") - return false, err + log.Println("There was a problem with locating the command directory") + return err } - const GOOS string = runtime.GOOS installerPath := filepath.Dir(cwd) - appsodyBinPath := "/appsody" - if GOOS == "windows" { - appsodyBinPath = "/appsody.exe" - } - appsodyBin := installerPath + appsodyBinPath - cmd := exec.Command(appsodyBin, "init") + commandBin := filepath.Join(installerPath, command.Command) + cmd := exec.Command(commandBin, command.Args...) cmd.Dir = projectPath output := new(bytes.Buffer) cmd.Stdout = output cmd.Stderr = output if err := cmd.Start(); err != nil { // after 'Start' the program is continued and script is executing in background - log.Println("There was a problem initializing the Appsody project: ", err, ". Project was not initialized.") - return false, err + log.Println("There was a problem running the command:", command.Command) + return err } - log.Printf("Please wait while the Appsody project is initialized... %s \n", output.String()) + log.Printf("Please wait while the project is initialized... %s", output.String()) cmd.Wait() log.Println(output.String()) // Wait to finish execution, so we can read all output - return true, nil + return nil } From 121c133d4a01f4e430c124e1c231c08feaec350b Mon Sep 17 00:00:00 2001 From: Andrew Mak Date: Fri, 13 Sep 2019 17:28:14 -0400 Subject: [PATCH 05/11] typo Signed-off-by: Andrew Mak --- actions/project.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/actions/project.go b/actions/project.go index 532ebc42..148239ab 100644 --- a/actions/project.go +++ b/actions/project.go @@ -71,7 +71,7 @@ func DownloadTemplate(c *cli.Context) { } } -// testIsExtension tests if a project an extension project and run associated commands if necessary +// testIsExtension tests if a project is an extension project and run associated commands as necessary func testIsExtension(projectPath string) (string, error) { extensions, err := apiroutes.GetExtensions() From 1890960b6631c2e771f3be334b52456433f57889 Mon Sep 17 00:00:00 2001 From: Andrew Mak Date: Wed, 18 Sep 2019 09:53:29 -0400 Subject: [PATCH 06/11] address a couple of comments Signed-off-by: Andrew Mak --- actions/project.go | 1 + utils/extensions.go | 3 ++- 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/actions/project.go b/actions/project.go index 148239ab..31578060 100644 --- a/actions/project.go +++ b/actions/project.go @@ -76,6 +76,7 @@ func testIsExtension(projectPath string) (string, error) { extensions, err := apiroutes.GetExtensions() if err != nil { + log.Println("There was a problem retrieving extensions data") return "", err } diff --git a/utils/extensions.go b/utils/extensions.go index f35d3860..3f0f4373 100644 --- a/utils/extensions.go +++ b/utils/extensions.go @@ -13,6 +13,7 @@ package utils import ( "bytes" + "fmt" "log" "os" "os/exec" @@ -46,7 +47,7 @@ func RunCommand(projectPath string, command ExtensionCommand) error { log.Println("There was a problem running the command:", command.Command) return err } - log.Printf("Please wait while the project is initialized... %s", output.String()) + fmt.Printf("Please wait while the project is initialized... %s", output.String()) cmd.Wait() log.Println(output.String()) // Wait to finish execution, so we can read all output return nil From 4b295108a1f5c85cfdd45bcd554184ba8fff062c Mon Sep 17 00:00:00 2001 From: Andrew Mak Date: Wed, 18 Sep 2019 09:55:44 -0400 Subject: [PATCH 07/11] rename function Signed-off-by: Andrew Mak --- actions/project.go | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/actions/project.go b/actions/project.go index 31578060..512f6d07 100644 --- a/actions/project.go +++ b/actions/project.go @@ -71,8 +71,8 @@ func DownloadTemplate(c *cli.Context) { } } -// testIsExtension tests if a project is an extension project and run associated commands as necessary -func testIsExtension(projectPath string) (string, error) { +// checkIsExtension checks if a project is an extension project and run associated commands as necessary +func checkIsExtension(projectPath string) (string, error) { extensions, err := apiroutes.GetExtensions() if err != nil { @@ -115,7 +115,7 @@ func ValidateProject(c *cli.Context) { Language: language, BuildType: buildType, } - extensionType, err := testIsExtension(projectPath) + extensionType, err := checkIsExtension(projectPath) if extensionType != "" { if err == nil { validationResult = ProjectType{ From 0ad6861a7af92088a4e3bfe5d537e18b8bd30155 Mon Sep 17 00:00:00 2001 From: Andrew Mak Date: Wed, 18 Sep 2019 10:15:08 -0400 Subject: [PATCH 08/11] return unknown Signed-off-by: Andrew Mak --- actions/project.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/actions/project.go b/actions/project.go index 512f6d07..c8a62e1e 100644 --- a/actions/project.go +++ b/actions/project.go @@ -77,7 +77,7 @@ func checkIsExtension(projectPath string) (string, error) { extensions, err := apiroutes.GetExtensions() if err != nil { log.Println("There was a problem retrieving extensions data") - return "", err + return "unknown", err } for _, extension := range extensions { From c59beb77639414a4be73091e2673c602d30cc4ce Mon Sep 17 00:00:00 2001 From: Andrew Mak Date: Wed, 18 Sep 2019 20:30:08 -0400 Subject: [PATCH 09/11] revert a change Signed-off-by: Andrew Mak --- utils/extensions.go | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/utils/extensions.go b/utils/extensions.go index 3f0f4373..f35d3860 100644 --- a/utils/extensions.go +++ b/utils/extensions.go @@ -13,7 +13,6 @@ package utils import ( "bytes" - "fmt" "log" "os" "os/exec" @@ -47,7 +46,7 @@ func RunCommand(projectPath string, command ExtensionCommand) error { log.Println("There was a problem running the command:", command.Command) return err } - fmt.Printf("Please wait while the project is initialized... %s", output.String()) + log.Printf("Please wait while the project is initialized... %s", output.String()) cmd.Wait() log.Println(output.String()) // Wait to finish execution, so we can read all output return nil From 8c6072a40815017236e24a3c09bb14118f668501 Mon Sep 17 00:00:00 2001 From: Andrew Mak Date: Wed, 18 Sep 2019 21:18:41 -0400 Subject: [PATCH 10/11] fix for python detection Signed-off-by: Andrew Mak --- utils/project.go | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/utils/project.go b/utils/project.go index 98590a16..ee95aab2 100644 --- a/utils/project.go +++ b/utils/project.go @@ -49,6 +49,10 @@ func DetermineProjectInfo(projectPath string) (string, string) { language = "swift" buildType = "swift" } + if PathExists(path.Join(projectPath, "Pipfile")) { + language = "python" + buildType = "docker" + } return language, buildType } From 3eaaf17b7d851c5d8f8c49a2a0796337cf4e9f91 Mon Sep 17 00:00:00 2001 From: Andrew Mak Date: Sat, 21 Sep 2019 22:19:35 -0400 Subject: [PATCH 11/11] prevent path traversal Signed-off-by: Andrew Mak --- utils/extensions.go | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/utils/extensions.go b/utils/extensions.go index f35d3860..8b9380b3 100644 --- a/utils/extensions.go +++ b/utils/extensions.go @@ -36,14 +36,15 @@ func RunCommand(projectPath string, command ExtensionCommand) error { return err } installerPath := filepath.Dir(cwd) - commandBin := filepath.Join(installerPath, command.Command) + commandName := filepath.Base(command.Command) // prevent path traversal + commandBin := filepath.Join(installerPath, commandName) cmd := exec.Command(commandBin, command.Args...) cmd.Dir = projectPath output := new(bytes.Buffer) cmd.Stdout = output cmd.Stderr = output if err := cmd.Start(); err != nil { // after 'Start' the program is continued and script is executing in background - log.Println("There was a problem running the command:", command.Command) + log.Println("There was a problem running the command:", commandName) return err } log.Printf("Please wait while the project is initialized... %s", output.String())