Skip to content

Commit 312ae68

Browse files
authored
Implemented python support (#78)
* Implemented python support * Added tests. Fixed small issues * Added more tests
1 parent 0351ed7 commit 312ae68

15 files changed

+555
-34
lines changed

frontend/client/views/overview/index.vue

+10-1
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@
99
<div class="outer-box">
1010
<router-link :to="{ path: '/pipeline/detail', query: { pipelineid: pipeline.p.id }}" class="hoveraction">
1111
<div class="outer-box-icon-image">
12-
<img :src="getImagePath(pipeline.p.type)" class="outer-box-image">
12+
<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' }">
1313
</div>
1414
<div>
1515
<span class="subtitle">{{ pipeline.p.name }}</span>
@@ -243,6 +243,15 @@ export default {
243243
transform: translate(-50%, -50%);
244244
}
245245
246+
.outer-box-image-python {
247+
position: absolute;
248+
width: 50px;
249+
height: 40px;
250+
top: 53%;
251+
left: 50%;
252+
transform: translate(-50%, -50%);
253+
}
254+
246255
.hoveraction:hover .outer-box-icon-image {
247256
border-color: #4da2fc !important;
248257
}

frontend/client/views/pipeline/create.vue

+2-2
Original file line numberDiff line numberDiff line change
@@ -66,8 +66,8 @@
6666
<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">
6767
<img src="~assets/java.png" class="typeimage">
6868
</div>
69-
<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">
70-
<img src="~assets/python.png" class="typeimage typeimagenotyetsupported">
69+
<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">
70+
<img src="~assets/python.png" class="typeimage">
7171
</div>
7272
</div>
7373
<div class="content" style="display: flex;">

gaia.go

+12
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,9 @@ const (
3131
// PTypeJava java plugin type
3232
PTypeJava PipelineType = "java"
3333

34+
// PTypePython python plugin type
35+
PTypePython PipelineType = "python"
36+
3437
// CreatePipelineFailed status
3538
CreatePipelineFailed CreatePipelineType = "failed"
3639

@@ -72,6 +75,15 @@ const (
7275

7376
// LogsFileName represents the file name of the logs output
7477
LogsFileName = "output.log"
78+
79+
// TmpFolder is the temp folder for temporary files
80+
TmpFolder = "tmp"
81+
82+
// TmpPythonFolder is the name of the python temporary folder
83+
TmpPythonFolder = "python"
84+
85+
// TmpGoFolder is the name of the golang temporary folder
86+
TmpGoFolder = "golang"
7587
)
7688

7789
// User is the user object

pipeline/build_golang.go

+2-3
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,6 @@ import (
1414

1515
const (
1616
golangBinaryName = "go"
17-
golangFolder = "golang"
1817
)
1918

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

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

5453
// Set command args for get dependencies
5554
args := []string{

pipeline/build_java.go

+1-1
Original file line numberDiff line numberDiff line change
@@ -33,7 +33,7 @@ func (b *BuildPipelineJava) PrepareEnvironment(p *gaia.CreatePipeline) error {
3333
uuid := uuid.Must(uuid.NewV4(), nil)
3434

3535
// Create local temp folder for clone
36-
rootPath := filepath.Join(gaia.Cfg.HomePath, tmpFolder, javaFolder)
36+
rootPath := filepath.Join(gaia.Cfg.HomePath, gaia.TmpFolder, javaFolder)
3737
cloneFolder := filepath.Join(rootPath, srcFolder, uuid.String())
3838
err := os.MkdirAll(cloneFolder, 0700)
3939
if err != nil {

pipeline/build_python.go

+118
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,118 @@
1+
package pipeline
2+
3+
import (
4+
"errors"
5+
"io/ioutil"
6+
"os"
7+
"os/exec"
8+
"path/filepath"
9+
"strings"
10+
"time"
11+
12+
"github.com/gaia-pipeline/gaia"
13+
"github.com/gaia-pipeline/gaia/services"
14+
"github.com/satori/go.uuid"
15+
)
16+
17+
var (
18+
pythonBinaryName = "python"
19+
)
20+
21+
// BuildPipelinePython is the real implementation of BuildPipeline for python
22+
type BuildPipelinePython struct {
23+
Type gaia.PipelineType
24+
}
25+
26+
// PrepareEnvironment prepares the environment before we start the build process.
27+
func (b *BuildPipelinePython) PrepareEnvironment(p *gaia.CreatePipeline) error {
28+
// create uuid for destination folder
29+
uuid := uuid.Must(uuid.NewV4(), nil)
30+
31+
// Create local temp folder for clone
32+
rootPath := filepath.Join(gaia.Cfg.HomePath, gaia.TmpFolder, gaia.TmpPythonFolder)
33+
cloneFolder := filepath.Join(rootPath, srcFolder, uuid.String())
34+
err := os.MkdirAll(cloneFolder, 0700)
35+
if err != nil {
36+
return err
37+
}
38+
39+
// Set new generated path in pipeline obj for later usage
40+
p.Pipeline.Repo.LocalDest = cloneFolder
41+
p.Pipeline.UUID = uuid.String()
42+
return err
43+
}
44+
45+
// ExecuteBuild executes the python build process
46+
func (b *BuildPipelinePython) ExecuteBuild(p *gaia.CreatePipeline) error {
47+
// Look for python executeable
48+
path, err := exec.LookPath(pythonBinaryName)
49+
if err != nil {
50+
gaia.Cfg.Logger.Debug("cannot find python executeable", "error", err.Error())
51+
return err
52+
}
53+
54+
// Set command args for build distribution package
55+
args := []string{
56+
"setup.py",
57+
"sdist",
58+
}
59+
60+
// Execute and wait until finish or timeout
61+
output, err := executeCmd(path, args, os.Environ(), p.Pipeline.Repo.LocalDest)
62+
if err != nil {
63+
gaia.Cfg.Logger.Debug("cannot generate python distribution package", "error", err.Error(), "output", string(output))
64+
p.Output = string(output)
65+
return err
66+
}
67+
68+
return nil
69+
}
70+
71+
// CopyBinary copies the final compiled archive to the
72+
// destination folder.
73+
func (b *BuildPipelinePython) CopyBinary(p *gaia.CreatePipeline) error {
74+
// find all files in dist folder
75+
distFolder := filepath.Join(p.Pipeline.Repo.LocalDest, "dist")
76+
files, err := ioutil.ReadDir(distFolder)
77+
if err != nil {
78+
return err
79+
}
80+
81+
// filter for archives
82+
archive := []os.FileInfo{}
83+
for _, file := range files {
84+
if strings.HasSuffix(file.Name(), ".tar.gz") {
85+
archive = append(archive, file)
86+
}
87+
}
88+
89+
// if we found more or less than one archive we have a problem
90+
if len(archive) != 1 {
91+
gaia.Cfg.Logger.Debug("cannot copy python package", "foundPackages", len(archive), "archives", files)
92+
return errors.New("cannot copy python package: not found")
93+
}
94+
95+
// Define src and destination
96+
src := filepath.Join(distFolder, archive[0].Name())
97+
dest := filepath.Join(gaia.Cfg.PipelinePath, appendTypeToName(p.Pipeline.Name, p.Pipeline.Type))
98+
99+
// Copy binary
100+
if err := copyFileContents(src, dest); err != nil {
101+
return err
102+
}
103+
104+
// Set +x (execution right) for pipeline
105+
return os.Chmod(dest, 0766)
106+
}
107+
108+
// SavePipeline saves the current pipeline configuration.
109+
func (b *BuildPipelinePython) SavePipeline(p *gaia.Pipeline) error {
110+
dest := filepath.Join(gaia.Cfg.PipelinePath, appendTypeToName(p.Name, p.Type))
111+
p.ExecPath = dest
112+
p.Type = gaia.PTypePython
113+
p.Name = strings.TrimSuffix(filepath.Base(dest), typeDelimiter+gaia.PTypePython.String())
114+
p.Created = time.Now()
115+
// Our pipeline is finished constructing. Save it.
116+
storeService, _ := services.StorageService()
117+
return storeService.PipelinePut(p)
118+
}

0 commit comments

Comments
 (0)