Skip to content
This repository has been archived by the owner on Nov 28, 2022. It is now read-only.

Call commands defined by extensions #119

Merged
merged 11 commits into from
Sep 23, 2019
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
53 changes: 45 additions & 8 deletions actions/project.go
Original file line number Diff line number Diff line change
Expand Up @@ -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"
Expand Down Expand Up @@ -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._-]");
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Good spot!

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It was actually the auto-linter that changed this :)

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"
}

Expand All @@ -70,6 +71,37 @@ func DownloadTemplate(c *cli.Context) {
}
}

// 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 {
log.Println("There was a problem retrieving extensions data")
return "unknown", 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)) {

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
}
}

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) {
Expand All @@ -78,14 +110,19 @@ 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,
Language: language,
BuildType: buildType,
}
if isAppsody {
validated, err := utils.SuccessfullyCallAppsodyInit(projectPath)
if !validated {
extensionType, err := checkIsExtension(projectPath)
if extensionType != "" {
if err == nil {
validationResult = ProjectType{
Language: language,
BuildType: extensionType,
}
} else {
validationStatus = "failed"
validationResult = err.Error()
}
Expand Down
50 changes: 50 additions & 0 deletions apiroutes/extensions.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
/*******************************************************************************
* 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"
"github.com/eclipse/codewind-installer/utils"
)

type (
// Extension represents a project extension defined by codewind.yaml
Extension struct {
ProjectType string `json:"projectType"`
Detection string `json:"detection"`
Commands []utils.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
}
47 changes: 25 additions & 22 deletions utils/appsody.go → utils/extensions.go
Original file line number Diff line number Diff line change
Expand Up @@ -9,43 +9,46 @@
* IBM Corporation - initial API and implementation
*******************************************************************************/

package utils
package utils

import (
import (
"bytes"
"log"
"os"
"os/exec"
"log"
"runtime"
"path/filepath"
)

// 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")
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 initializing the Appsody project: ", err, ". Project was not initialized.")
return false, err
log.Println("There was a problem running the command:", commandName)
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())
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This should be fmt.Printf() as this is currently going to stderr by default and not stdout

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

So I needed to revert this back to log. Because stdout needed to be json only because it is parsed by IDE

cmd.Wait()
log.Println(output.String()) // Wait to finish execution, so we can read all output
return true, nil
}

return nil
}
16 changes: 7 additions & 9 deletions utils/project.go
Original file line number Diff line number Diff line change
Expand Up @@ -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)
Expand All @@ -49,13 +49,11 @@ 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, "Pipfile")) {
language = "python"
buildType = "docker"
}
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
Expand Down
8 changes: 1 addition & 7 deletions utils/project_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -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)
})
}
}
Expand Down