Skip to content

Commit

Permalink
Implemented python support (#78)
Browse files Browse the repository at this point in the history
* Implemented python support

* Added tests. Fixed small issues

* Added more tests
  • Loading branch information
michelvocks committed Aug 18, 2018
1 parent 0351ed7 commit 312ae68
Show file tree
Hide file tree
Showing 15 changed files with 555 additions and 34 deletions.
11 changes: 10 additions & 1 deletion frontend/client/views/overview/index.vue
Expand Up @@ -9,7 +9,7 @@
<div class="outer-box">
<router-link :to="{ path: '/pipeline/detail', query: { pipelineid: pipeline.p.id }}" class="hoveraction">
<div class="outer-box-icon-image">
<img :src="getImagePath(pipeline.p.type)" class="outer-box-image">
<img :src="getImagePath(pipeline.p.type)" class="outer-box-image" v-bind:class="{ 'outer-box-image-python': pipeline.p.type === 'python', 'outer-box-image': pipeline.p.type !== 'python' }">
</div>
<div>
<span class="subtitle">{{ pipeline.p.name }}</span>
Expand Down Expand Up @@ -243,6 +243,15 @@ export default {
transform: translate(-50%, -50%);
}
.outer-box-image-python {
position: absolute;
width: 50px;
height: 40px;
top: 53%;
left: 50%;
transform: translate(-50%, -50%);
}
.hoveraction:hover .outer-box-icon-image {
border-color: #4da2fc !important;
}
Expand Down
4 changes: 2 additions & 2 deletions frontend/client/views/pipeline/create.vue
Expand Up @@ -66,8 +66,8 @@
<div class="pipelinetype" title="Java" v-tippy="{ arrow : true, animation : 'shift-away'}" v-on:click="createPipeline.pipeline.type = 'java'" v-bind:class="{ pipelinetypeactive: createPipeline.pipeline.type === 'java' }" data-tippy-hideOnClick="false">
<img src="~assets/java.png" class="typeimage">
</div>
<div class="pipelinetype" title="Python (not yet supported)" v-tippy="{ arrow : true, animation : 'shift-away'}" v-bind:class="{ pipelinetypeactive: createPipeline.pipeline.type === 'python' }" data-tippy-hideOnClick="false">
<img src="~assets/python.png" class="typeimage typeimagenotyetsupported">
<div class="pipelinetype" title="Python" v-tippy="{ arrow : true, animation : 'shift-away'}" v-on:click="createPipeline.pipeline.type = 'python'" v-bind:class="{ pipelinetypeactive: createPipeline.pipeline.type === 'python' }" data-tippy-hideOnClick="false">
<img src="~assets/python.png" class="typeimage">
</div>
</div>
<div class="content" style="display: flex;">
Expand Down
12 changes: 12 additions & 0 deletions gaia.go
Expand Up @@ -31,6 +31,9 @@ const (
// PTypeJava java plugin type
PTypeJava PipelineType = "java"

// PTypePython python plugin type
PTypePython PipelineType = "python"

// CreatePipelineFailed status
CreatePipelineFailed CreatePipelineType = "failed"

Expand Down Expand Up @@ -72,6 +75,15 @@ const (

// LogsFileName represents the file name of the logs output
LogsFileName = "output.log"

// TmpFolder is the temp folder for temporary files
TmpFolder = "tmp"

// TmpPythonFolder is the name of the python temporary folder
TmpPythonFolder = "python"

// TmpGoFolder is the name of the golang temporary folder
TmpGoFolder = "golang"
)

// User is the user object
Expand Down
5 changes: 2 additions & 3 deletions pipeline/build_golang.go
Expand Up @@ -14,7 +14,6 @@ import (

const (
golangBinaryName = "go"
golangFolder = "golang"
)

// BuildPipelineGolang is the real implementation of BuildPipeline for golang
Expand All @@ -28,7 +27,7 @@ func (b *BuildPipelineGolang) PrepareEnvironment(p *gaia.CreatePipeline) error {
uuid := uuid.Must(uuid.NewV4(), nil)

// Create local temp folder for clone
goPath := filepath.Join(gaia.Cfg.HomePath, tmpFolder, golangFolder)
goPath := filepath.Join(gaia.Cfg.HomePath, gaia.TmpFolder, gaia.TmpGoFolder)
cloneFolder := filepath.Join(goPath, srcFolder, uuid.String())
err := os.MkdirAll(cloneFolder, 0700)
if err != nil {
Expand All @@ -49,7 +48,7 @@ func (b *BuildPipelineGolang) ExecuteBuild(p *gaia.CreatePipeline) error {
gaia.Cfg.Logger.Debug("cannot find go executeable", "error", err.Error())
return err
}
goPath := filepath.Join(gaia.Cfg.HomePath, tmpFolder, golangFolder)
goPath := filepath.Join(gaia.Cfg.HomePath, gaia.TmpFolder, gaia.TmpGoFolder)

// Set command args for get dependencies
args := []string{
Expand Down
2 changes: 1 addition & 1 deletion pipeline/build_java.go
Expand Up @@ -33,7 +33,7 @@ func (b *BuildPipelineJava) PrepareEnvironment(p *gaia.CreatePipeline) error {
uuid := uuid.Must(uuid.NewV4(), nil)

// Create local temp folder for clone
rootPath := filepath.Join(gaia.Cfg.HomePath, tmpFolder, javaFolder)
rootPath := filepath.Join(gaia.Cfg.HomePath, gaia.TmpFolder, javaFolder)
cloneFolder := filepath.Join(rootPath, srcFolder, uuid.String())
err := os.MkdirAll(cloneFolder, 0700)
if err != nil {
Expand Down
118 changes: 118 additions & 0 deletions pipeline/build_python.go
@@ -0,0 +1,118 @@
package pipeline

import (
"errors"
"io/ioutil"
"os"
"os/exec"
"path/filepath"
"strings"
"time"

"github.com/gaia-pipeline/gaia"
"github.com/gaia-pipeline/gaia/services"
"github.com/satori/go.uuid"
)

var (
pythonBinaryName = "python"
)

// BuildPipelinePython is the real implementation of BuildPipeline for python
type BuildPipelinePython struct {
Type gaia.PipelineType
}

// PrepareEnvironment prepares the environment before we start the build process.
func (b *BuildPipelinePython) PrepareEnvironment(p *gaia.CreatePipeline) error {
// create uuid for destination folder
uuid := uuid.Must(uuid.NewV4(), nil)

// Create local temp folder for clone
rootPath := filepath.Join(gaia.Cfg.HomePath, gaia.TmpFolder, gaia.TmpPythonFolder)
cloneFolder := filepath.Join(rootPath, srcFolder, uuid.String())
err := os.MkdirAll(cloneFolder, 0700)
if err != nil {
return err
}

// Set new generated path in pipeline obj for later usage
p.Pipeline.Repo.LocalDest = cloneFolder
p.Pipeline.UUID = uuid.String()
return err
}

// ExecuteBuild executes the python build process
func (b *BuildPipelinePython) ExecuteBuild(p *gaia.CreatePipeline) error {
// Look for python executeable
path, err := exec.LookPath(pythonBinaryName)
if err != nil {
gaia.Cfg.Logger.Debug("cannot find python executeable", "error", err.Error())
return err
}

// Set command args for build distribution package
args := []string{
"setup.py",
"sdist",
}

// Execute and wait until finish or timeout
output, err := executeCmd(path, args, os.Environ(), p.Pipeline.Repo.LocalDest)
if err != nil {
gaia.Cfg.Logger.Debug("cannot generate python distribution package", "error", err.Error(), "output", string(output))
p.Output = string(output)
return err
}

return nil
}

// CopyBinary copies the final compiled archive to the
// destination folder.
func (b *BuildPipelinePython) CopyBinary(p *gaia.CreatePipeline) error {
// find all files in dist folder
distFolder := filepath.Join(p.Pipeline.Repo.LocalDest, "dist")
files, err := ioutil.ReadDir(distFolder)
if err != nil {
return err
}

// filter for archives
archive := []os.FileInfo{}
for _, file := range files {
if strings.HasSuffix(file.Name(), ".tar.gz") {
archive = append(archive, file)
}
}

// if we found more or less than one archive we have a problem
if len(archive) != 1 {
gaia.Cfg.Logger.Debug("cannot copy python package", "foundPackages", len(archive), "archives", files)
return errors.New("cannot copy python package: not found")
}

// Define src and destination
src := filepath.Join(distFolder, archive[0].Name())
dest := filepath.Join(gaia.Cfg.PipelinePath, appendTypeToName(p.Pipeline.Name, p.Pipeline.Type))

// Copy binary
if err := copyFileContents(src, dest); err != nil {
return err
}

// Set +x (execution right) for pipeline
return os.Chmod(dest, 0766)
}

// SavePipeline saves the current pipeline configuration.
func (b *BuildPipelinePython) SavePipeline(p *gaia.Pipeline) error {
dest := filepath.Join(gaia.Cfg.PipelinePath, appendTypeToName(p.Name, p.Type))
p.ExecPath = dest
p.Type = gaia.PTypePython
p.Name = strings.TrimSuffix(filepath.Base(dest), typeDelimiter+gaia.PTypePython.String())
p.Created = time.Now()
// Our pipeline is finished constructing. Save it.
storeService, _ := services.StorageService()
return storeService.PipelinePut(p)
}

0 comments on commit 312ae68

Please sign in to comment.