Pull in cncd/* for simpler workflow

This commit is contained in:
Laszlo Fogas 2019-04-06 15:44:04 +02:00
parent f9f0998cd2
commit a4541ac901
141 changed files with 6341 additions and 217 deletions

View file

@ -29,11 +29,11 @@ import (
"google.golang.org/grpc/keepalive"
"google.golang.org/grpc/metadata"
"github.com/cncd/pipeline/pipeline"
"github.com/cncd/pipeline/pipeline/backend"
"github.com/cncd/pipeline/pipeline/backend/docker"
"github.com/cncd/pipeline/pipeline/multipart"
"github.com/cncd/pipeline/pipeline/rpc"
"github.com/laszlocph/drone-oss-08/cncd/pipeline/pipeline"
"github.com/laszlocph/drone-oss-08/cncd/pipeline/pipeline/backend"
"github.com/laszlocph/drone-oss-08/cncd/pipeline/pipeline/backend/docker"
"github.com/laszlocph/drone-oss-08/cncd/pipeline/pipeline/multipart"
"github.com/laszlocph/drone-oss-08/cncd/pipeline/pipeline/rpc"
"github.com/drone/signal"
"github.com/rs/zerolog"

View file

@ -33,9 +33,9 @@ import (
"golang.org/x/crypto/acme/autocert"
"golang.org/x/sync/errgroup"
"github.com/cncd/logging"
"github.com/cncd/pipeline/pipeline/rpc/proto"
"github.com/cncd/pubsub"
"github.com/laszlocph/drone-oss-08/cncd/logging"
"github.com/laszlocph/drone-oss-08/cncd/pipeline/pipeline/rpc/proto"
"github.com/laszlocph/drone-oss-08/cncd/pubsub"
"github.com/laszlocph/drone-oss-08/plugins/sender"
"github.com/laszlocph/drone-oss-08/remote"
"github.com/laszlocph/drone-oss-08/router"

View file

@ -18,7 +18,7 @@ import (
"fmt"
"time"
"github.com/cncd/queue"
"github.com/laszlocph/drone-oss-08/cncd/queue"
"github.com/dimfeld/httptreemux"
"github.com/laszlocph/drone-oss-08/model"
"github.com/laszlocph/drone-oss-08/plugins/registry"

6
cncd/logging/README Normal file
View file

@ -0,0 +1,6 @@
Go package provides a common interface for storing and streaming logs.
Documentation:
http://godoc.org/github.com/laszlocph/drone-oss-08/cncd/logging
http://godoc.org/github.com/laszlocph/drone-oss-08/cncd/logging/gcp

52
cncd/logging/log_test.go Normal file
View file

@ -0,0 +1,52 @@
package logging
import (
"context"
"sync"
"testing"
"time"
)
func TestLogging(t *testing.T) {
var (
wg sync.WaitGroup
testPath = "test"
testEntry = &Entry{
Data: []byte("test"),
}
)
ctx, cancel := context.WithCancel(
context.Background(),
)
logger := New()
logger.Open(ctx, testPath)
go func() {
logger.Tail(ctx, testPath, func(entry ...*Entry) { wg.Done() })
}()
go func() {
logger.Tail(ctx, testPath, func(entry ...*Entry) { wg.Done() })
}()
<-time.After(time.Millisecond)
wg.Add(4)
go func() {
logger.Write(ctx, testPath, testEntry)
logger.Write(ctx, testPath, testEntry)
}()
wg.Wait()
wg.Add(1)
go func() {
logger.Tail(ctx, testPath, func(entry ...*Entry) { wg.Done() })
}()
<-time.After(time.Millisecond)
wg.Wait()
cancel()
}

15
cncd/pipeline/.drone.yml Normal file
View file

@ -0,0 +1,15 @@
workspace:
base: /go
path: src/github.com/laszlocph/drone-oss-08/cncd/pipeline
pipeline:
install:
image: golang:1.8
commands:
- go install github.com/laszlocph/drone-oss-08/cncd/pipeline/pipec
- go install github.com/laszlocph/drone-oss-08/cncd/pipeline/piped
test:
image: golang:1.8
commands:
- go test -cover github.com/laszlocph/drone-oss-08/cncd/pipeline/...

2
cncd/pipeline/.gitignore vendored Normal file
View file

@ -0,0 +1,2 @@
*.txt
*.out

1
cncd/pipeline/doc.go Normal file
View file

@ -0,0 +1 @@
package pipeline

View file

@ -0,0 +1,507 @@
package main
import (
"encoding/json"
"fmt"
"os"
"path"
"path/filepath"
"strings"
"github.com/laszlocph/drone-oss-08/cncd/pipeline/pipeline/frontend"
"github.com/laszlocph/drone-oss-08/cncd/pipeline/pipeline/frontend/yaml"
"github.com/laszlocph/drone-oss-08/cncd/pipeline/pipeline/frontend/yaml/compiler"
"github.com/urfave/cli"
)
var compileCommand = cli.Command{
Name: "compile",
Usage: "compile the yaml file",
Action: compileAction,
Flags: []cli.Flag{
cli.StringFlag{
Name: "in",
Value: "pipeline.yml",
},
cli.StringFlag{
Name: "out",
Value: "pipeline.json",
},
cli.StringSliceFlag{
Name: "volumes",
},
cli.StringSliceFlag{
Name: "privileged",
Value: &cli.StringSlice{
"plugins/docker",
"plugins/gcr",
"plugins/ecr",
},
},
cli.StringFlag{
Name: "prefix",
Value: "pipeline",
},
cli.BoolFlag{
Name: "local",
},
//
// volume caching
//
cli.BoolFlag{
Name: "volume-cache",
EnvVar: "CI_VOLUME_CACHE",
},
cli.StringFlag{
Name: "volume-cache-base",
Value: "/var/lib/drone",
EnvVar: "CI_VOLUME_CACHE_BASE",
},
//
// s3 caching
//
cli.BoolFlag{
Name: "aws-cache",
EnvVar: "CI_AWS_CACHE",
},
cli.StringFlag{
Name: "aws-region",
EnvVar: "AWS_REGION",
},
cli.StringFlag{
Name: "aws-bucket",
EnvVar: "AWS_BUCKET",
},
cli.StringFlag{
Name: "aws-access-key-id",
EnvVar: "AWS_ACCESS_KEY_ID",
},
cli.StringFlag{
Name: "aws-secret-access-key",
EnvVar: "AWS_SECRET_ACCESS_KEY",
},
//
// registry credentials
//
cli.StringFlag{
Name: "registry-hostname",
EnvVar: "CI_REGISTRY_HOSTNAME",
},
cli.StringFlag{
Name: "registry-username",
EnvVar: "CI_REGISTRY_USERNAME",
},
cli.StringFlag{
Name: "registry-password",
EnvVar: "CI_REGISTRY_PASSWORD",
},
//
// workspace default
//
cli.StringFlag{
Name: "workspace-base",
Value: "/pipeline",
},
cli.StringFlag{
Name: "workspace-path",
Value: "src",
},
//
// netrc parameters
//
cli.StringFlag{
Name: "netrc-username",
EnvVar: "CI_NETRC_USERNAME",
},
cli.StringFlag{
Name: "netrc-password",
EnvVar: "CI_NETRC_PASSWORD",
},
cli.StringFlag{
Name: "netrc-machine",
EnvVar: "CI_NETRC_MACHINE",
},
//
// resource limit parameters
//
cli.Int64Flag{
Name: "limit-mem-swap",
EnvVar: "CI_LIMIT_MEM_SWAP",
},
cli.Int64Flag{
Name: "limit-mem",
EnvVar: "CI_LIMIT_MEM",
},
cli.Int64Flag{
Name: "limit-shm-size",
EnvVar: "CI_LIMIT_SHM_SIZE",
},
cli.Int64Flag{
Name: "limit-cpu-quota",
EnvVar: "CI_LIMIT_CPU_QUOTA",
},
cli.Int64Flag{
Name: "limit-cpu-shares",
EnvVar: "CI_LIMIT_CPU_SHARES",
},
cli.StringFlag{
Name: "limit-cpu-set",
EnvVar: "CI_LIMIT_CPU_SET",
},
//
// metadata parameters
//
cli.StringFlag{
Name: "system-arch",
Value: "linux/amd64",
EnvVar: "CI_SYSTEM_ARCH",
},
cli.StringFlag{
Name: "system-name",
Value: "pipec",
EnvVar: "CI_SYSTEM_NAME",
},
cli.StringFlag{
Name: "system-link",
Value: "https://github.com/cncd/pipec",
EnvVar: "CI_SYSTEM_LINK",
},
cli.StringFlag{
Name: "repo-name",
EnvVar: "CI_REPO_NAME",
},
cli.StringFlag{
Name: "repo-link",
EnvVar: "CI_REPO_LINK",
},
cli.StringFlag{
Name: "repo-remote-url",
EnvVar: "CI_REPO_REMOTE",
},
cli.StringFlag{
Name: "repo-private",
EnvVar: "CI_REPO_PRIVATE",
},
cli.IntFlag{
Name: "build-number",
EnvVar: "CI_BUILD_NUMBER",
},
cli.Int64Flag{
Name: "build-created",
EnvVar: "CI_BUILD_CREATED",
},
cli.Int64Flag{
Name: "build-started",
EnvVar: "CI_BUILD_STARTED",
},
cli.Int64Flag{
Name: "build-finished",
EnvVar: "CI_BUILD_FINISHED",
},
cli.StringFlag{
Name: "build-status",
EnvVar: "CI_BUILD_STATUS",
},
cli.StringFlag{
Name: "build-event",
EnvVar: "CI_BUILD_EVENT",
},
cli.StringFlag{
Name: "build-link",
EnvVar: "CI_BUILD_LINK",
},
cli.StringFlag{
Name: "build-target",
EnvVar: "CI_BUILD_TARGET",
},
cli.StringFlag{
Name: "commit-sha",
EnvVar: "CI_COMMIT_SHA",
},
cli.StringFlag{
Name: "commit-ref",
EnvVar: "CI_COMMIT_REF",
},
cli.StringFlag{
Name: "commit-refspec",
EnvVar: "CI_COMMIT_REFSPEC",
},
cli.StringFlag{
Name: "commit-branch",
EnvVar: "CI_COMMIT_BRANCH",
},
cli.StringFlag{
Name: "commit-message",
EnvVar: "CI_COMMIT_MESSAGE",
},
cli.StringFlag{
Name: "commit-author-name",
EnvVar: "CI_COMMIT_AUTHOR_NAME",
},
cli.StringFlag{
Name: "commit-author-avatar",
EnvVar: "CI_COMMIT_AUTHOR_AVATAR",
},
cli.StringFlag{
Name: "commit-author-email",
EnvVar: "CI_COMMIT_AUTHOR_EMAIL",
},
cli.IntFlag{
Name: "prev-build-number",
EnvVar: "CI_PREV_BUILD_NUMBER",
},
cli.Int64Flag{
Name: "prev-build-created",
EnvVar: "CI_PREV_BUILD_CREATED",
},
cli.Int64Flag{
Name: "prev-build-started",
EnvVar: "CI_PREV_BUILD_STARTED",
},
cli.Int64Flag{
Name: "prev-build-finished",
EnvVar: "CI_PREV_BUILD_FINISHED",
},
cli.StringFlag{
Name: "prev-build-status",
EnvVar: "CI_PREV_BUILD_STATUS",
},
cli.StringFlag{
Name: "prev-build-event",
EnvVar: "CI_PREV_BUILD_EVENT",
},
cli.StringFlag{
Name: "prev-build-link",
EnvVar: "CI_PREV_BUILD_LINK",
},
cli.StringFlag{
Name: "prev-commit-sha",
EnvVar: "CI_PREV_COMMIT_SHA",
},
cli.StringFlag{
Name: "prev-commit-ref",
EnvVar: "CI_PREV_COMMIT_REF",
},
cli.StringFlag{
Name: "prev-commit-refspec",
EnvVar: "CI_PREV_COMMIT_REFSPEC",
},
cli.StringFlag{
Name: "prev-commit-branch",
EnvVar: "CI_PREV_COMMIT_BRANCH",
},
cli.StringFlag{
Name: "prev-commit-message",
EnvVar: "CI_PREV_COMMIT_MESSAGE",
},
cli.StringFlag{
Name: "prev-commit-author-name",
EnvVar: "CI_PREV_COMMIT_AUTHOR_NAME",
},
cli.StringFlag{
Name: "prev-commit-author-avatar",
EnvVar: "CI_PREV_COMMIT_AUTHOR_AVATAR",
},
cli.StringFlag{
Name: "prev-commit-author-email",
EnvVar: "CI_PREV_COMMIT_AUTHOR_EMAIL",
},
cli.IntFlag{
Name: "job-number",
EnvVar: "CI_JOB_NUMBER",
},
// cli.StringFlag{
// Name: "job-matrix",
// EnvVar: "CI_JOB_MATRIX",
// },
},
}
func compileAction(c *cli.Context) (err error) {
file := c.Args().First()
if file == "" {
file = c.String("in")
}
conf, err := yaml.ParseFile(file)
if err != nil {
return err
}
// configure volumes for local execution
volumes := c.StringSlice("volumes")
if c.Bool("local") {
var (
workspaceBase = conf.Workspace.Base
workspacePath = conf.Workspace.Path
)
if workspaceBase == "" {
workspaceBase = c.String("workspace-base")
}
if workspacePath == "" {
workspacePath = c.String("workspace-path")
}
dir, _ := filepath.Abs(filepath.Dir(file))
volumes = append(volumes, dir+":"+path.Join(workspaceBase, workspacePath))
}
// secrets from environment variable
var secrets []compiler.Secret
for _, env := range os.Environ() {
parts := strings.Split(env, "=")
secrets = append(secrets, compiler.Secret{
Name: parts[0],
Value: parts[1],
})
}
// compiles the yaml file
compiled := compiler.New(
compiler.WithResourceLimit(
c.Int64("limit-mem-swap"),
c.Int64("limit-mem"),
c.Int64("limit-shm-size"),
c.Int64("limit-cpu-quota"),
c.Int64("limit-cpu-shares"),
c.String("limit-cpu-set"),
),
compiler.WithRegistry(
compiler.Registry{
Hostname: c.String("registry-hostname"),
Username: c.String("registry-username"),
Password: c.String("registry-password"),
},
),
compiler.WithEscalated(
c.StringSlice("privileged")...,
),
compiler.WithSecret(secrets...),
compiler.WithVolumes(volumes...),
compiler.WithWorkspace(
c.String("workspace-base"),
c.String("workspace-path"),
),
compiler.WithPrefix(
c.String("prefix"),
),
compiler.WithProxy(),
compiler.WithLocal(
c.Bool("local"),
),
compiler.WithNetrc(
c.String("netrc-username"),
c.String("netrc-password"),
c.String("netrc-machine"),
),
compiler.WithMetadata(
metadataFromContext(c),
),
compiler.WithOption(
compiler.WithVolumeCacher(
c.String("volume-cache-base"),
),
c.Bool("volume-cache"),
),
compiler.WithOption(
compiler.WithS3Cacher(
c.String("aws-access-key-id"),
c.String("aws-secret-access-key"),
c.String("aws-region"),
c.String("aws-bucket"),
),
c.Bool("aws-cache"),
),
).Compile(conf)
// marshal the compiled spec to formatted yaml
out, err := json.MarshalIndent(compiled, "", " ")
if err != nil {
return err
}
// create output file with option to dump to stdout
var writer = os.Stdout
output := c.String("out")
if output != "-" {
writer, err = os.Create(output)
if err != nil {
return err
}
}
defer writer.Close()
_, err = writer.Write(out)
if err != nil {
return err
}
if writer != os.Stdout {
fmt.Fprintf(os.Stdout, "Successfully compiled %s to %s\n", file, output)
}
return nil
}
// return the metadata from the cli context.
func metadataFromContext(c *cli.Context) frontend.Metadata {
return frontend.Metadata{
Repo: frontend.Repo{
Name: c.String("repo-name"),
Link: c.String("repo-link"),
Remote: c.String("repo-remote-url"),
Private: c.Bool("repo-private"),
},
Curr: frontend.Build{
Number: c.Int("build-number"),
Created: c.Int64("build-created"),
Started: c.Int64("build-started"),
Finished: c.Int64("build-finished"),
Status: c.String("build-status"),
Event: c.String("build-event"),
Link: c.String("build-link"),
Target: c.String("build-target"),
Commit: frontend.Commit{
Sha: c.String("commit-sha"),
Ref: c.String("commit-ref"),
Refspec: c.String("commit-refspec"),
Branch: c.String("commit-branch"),
Message: c.String("commit-message"),
Author: frontend.Author{
Name: c.String("commit-author-name"),
Email: c.String("commit-author-email"),
Avatar: c.String("commit-author-avatar"),
},
},
},
Prev: frontend.Build{
Number: c.Int("prev-build-number"),
Created: c.Int64("prev-build-created"),
Started: c.Int64("prev-build-started"),
Finished: c.Int64("prev-build-finished"),
Status: c.String("prev-build-status"),
Event: c.String("prev-build-event"),
Link: c.String("prev-build-link"),
Commit: frontend.Commit{
Sha: c.String("prev-commit-sha"),
Ref: c.String("prev-commit-ref"),
Refspec: c.String("prev-commit-refspec"),
Branch: c.String("prev-commit-branch"),
Message: c.String("prev-commit-message"),
Author: frontend.Author{
Name: c.String("prev-commit-author-name"),
Email: c.String("prev-commit-author-email"),
Avatar: c.String("prev-commit-author-avatar"),
},
},
},
Job: frontend.Job{
Number: c.Int("job-number"),
// Matrix: ,
},
Sys: frontend.System{
Name: c.String("system-name"),
Link: c.String("system-link"),
Arch: c.String("system-arch"),
},
}
}

123
cncd/pipeline/pipec/exec.go Normal file
View file

@ -0,0 +1,123 @@
package main
import (
"context"
"fmt"
"io"
"os"
"strconv"
"time"
"github.com/laszlocph/drone-oss-08/cncd/pipeline/pipeline"
"github.com/laszlocph/drone-oss-08/cncd/pipeline/pipeline/backend"
"github.com/laszlocph/drone-oss-08/cncd/pipeline/pipeline/backend/docker"
"github.com/laszlocph/drone-oss-08/cncd/pipeline/pipeline/backend/kubernetes"
"github.com/laszlocph/drone-oss-08/cncd/pipeline/pipeline/interrupt"
"github.com/laszlocph/drone-oss-08/cncd/pipeline/pipeline/multipart"
"github.com/urfave/cli"
)
var executeCommand = cli.Command{
Name: "exec",
Usage: "execute the compiled file",
Action: executeAction,
Flags: []cli.Flag{
cli.StringFlag{
Name: "in",
Value: "pipeline.json",
},
cli.DurationFlag{
Name: "timeout",
EnvVar: "CI_TIMEOUT",
Value: time.Hour,
},
cli.BoolFlag{
Name: "kubernetes",
EnvVar: "CI_KUBERNETES",
},
cli.StringFlag{
Name: "kubernetes-namepsace",
EnvVar: "CI_KUBERNETES_NAMESPACE",
Value: "default",
},
cli.StringFlag{
Name: "kubernetes-endpoint",
EnvVar: "CI_KUBERNETES_ENDPOINT",
},
cli.StringFlag{
Name: "kubernetes-token",
EnvVar: "CI_KUBERNETES_TOKEN",
},
},
}
func executeAction(c *cli.Context) (err error) {
path := c.Args().First()
if path == "" {
path = c.String("in")
}
var reader io.ReadCloser
if path == "-" {
reader = os.Stdin
} else {
reader, err = os.Open(path)
if err != nil {
return err
}
}
defer reader.Close()
config, err := pipeline.Parse(reader)
if err != nil {
return err
}
var engine backend.Engine
if c.Bool("kubernetes") {
engine = kubernetes.New(
c.String("kubernetes-namepsace"),
c.String("kubernetes-endpoint"),
c.String("kubernetes-token"),
)
} else {
engine, err = docker.NewEnv()
if err != nil {
return err
}
}
ctx, cancel := context.WithTimeout(context.Background(), c.Duration("timeout"))
defer cancel()
ctx = interrupt.WithContext(ctx)
return pipeline.New(config,
pipeline.WithContext(ctx),
pipeline.WithLogger(defaultLogger),
pipeline.WithTracer(defaultTracer),
pipeline.WithEngine(engine),
).Run()
}
var defaultLogger = pipeline.LogFunc(func(proc *backend.Step, rc multipart.Reader) error {
part, err := rc.NextPart()
if err != nil {
return err
}
io.Copy(os.Stderr, part)
return nil
})
var defaultTracer = pipeline.TraceFunc(func(state *pipeline.State) error {
if state.Process.Exited {
fmt.Printf("proc %q exited with status %d\n", state.Pipeline.Step.Name, state.Process.ExitCode)
} else {
fmt.Printf("proc %q started\n", state.Pipeline.Step.Name)
state.Pipeline.Step.Environment["CI_BUILD_STATUS"] = "success"
state.Pipeline.Step.Environment["CI_BUILD_FINISHED"] = strconv.FormatInt(time.Now().Unix(), 10)
if state.Pipeline.Error != nil {
state.Pipeline.Step.Environment["CI_BUILD_STATUS"] = "failure"
}
}
return nil
})

View file

@ -0,0 +1,54 @@
package main
import (
"fmt"
"github.com/laszlocph/drone-oss-08/cncd/pipeline/pipeline/frontend/yaml"
"github.com/laszlocph/drone-oss-08/cncd/pipeline/pipeline/frontend/yaml/linter"
"github.com/kr/pretty"
"github.com/urfave/cli"
)
var lintCommand = cli.Command{
Name: "lint",
Usage: "lints the yaml file",
Action: lintAction,
Flags: []cli.Flag{
cli.BoolFlag{
Name: "trusted",
},
cli.BoolFlag{
Name: "pretty",
},
},
}
func lintAction(c *cli.Context) error {
file := c.Args().First()
if file == "" {
return fmt.Errorf("Error: please provide a path the configuration file")
}
conf, err := yaml.ParseFile(file)
if err != nil {
return err
}
err = linter.New(
linter.WithTrusted(
c.Bool("trusted"),
),
).Lint(conf)
if err != nil {
return err
}
if c.Bool("pretty") {
pretty.Println(conf)
}
fmt.Println("Lint complete. Yaml file is valid")
return nil
}

View file

@ -0,0 +1,26 @@
package main
import (
"fmt"
"os"
"github.com/urfave/cli"
_ "github.com/joho/godotenv/autoload"
)
func main() {
app := cli.NewApp()
app.Name = "pipec"
app.Usage = "pipec provides command line tools for the cncd runtime"
app.Commands = []cli.Command{
compileCommand,
executeCommand,
lintCommand,
}
if err := app.Run(os.Args); err != nil {
fmt.Println(err)
os.Exit(1)
}
}

314
cncd/pipeline/piped/main.go Normal file
View file

@ -0,0 +1,314 @@
package main
import (
"context"
"encoding/json"
"io"
"io/ioutil"
"log"
"math"
"net/url"
"os"
"strconv"
"sync"
"time"
"github.com/laszlocph/drone-oss-08/cncd/pipeline/pipeline"
"github.com/laszlocph/drone-oss-08/cncd/pipeline/pipeline/backend"
"github.com/laszlocph/drone-oss-08/cncd/pipeline/pipeline/backend/docker"
"github.com/laszlocph/drone-oss-08/cncd/pipeline/pipeline/interrupt"
"github.com/laszlocph/drone-oss-08/cncd/pipeline/pipeline/multipart"
"github.com/laszlocph/drone-oss-08/cncd/pipeline/pipeline/rpc"
_ "github.com/joho/godotenv/autoload"
"github.com/tevino/abool"
"github.com/urfave/cli"
)
const (
maxFileUpload = 5000000
maxLogsUpload = 5000000
)
func main() {
app := cli.NewApp()
app.Name = "piped"
app.Usage = "piped stars a pipeline execution daemon"
app.Action = start
app.Flags = []cli.Flag{
cli.StringFlag{
Name: "endpoint",
EnvVar: "PIPED_ENDPOINT,PIPED_SERVER",
Value: "ws://localhost:9999",
},
cli.StringFlag{
Name: "token",
EnvVar: "PIPED_TOKEN,PIPED_SECRET",
},
cli.DurationFlag{
Name: "backoff",
EnvVar: "PIPED_BACKOFF",
Value: time.Second * 15,
},
cli.IntFlag{
Name: "retry-limit",
EnvVar: "PIPED_RETRY_LIMIT",
Value: math.MaxInt32,
},
cli.StringFlag{
Name: "platform",
EnvVar: "PIPED_PLATFORM",
Value: "linux/amd64",
},
cli.Int64Flag{
Name: "upload-limit",
EnvVar: "PIPED_UPLOAD_LIMIT",
Value: math.MaxInt32,
},
}
app.Commands = []cli.Command{
onceCommand,
}
if err := app.Run(os.Args); err != nil {
log.Fatalln(err)
}
}
func start(c *cli.Context) error {
endpoint, err := url.Parse(
c.String("endpoint"),
)
if err != nil {
return err
}
filter := rpc.Filter{
Labels: map[string]string{
"platform": c.String("platform"),
},
}
client, err := rpc.NewClient(
endpoint.String(),
rpc.WithRetryLimit(
c.Int("retry-limit"),
),
rpc.WithBackoff(
c.Duration("backoff"),
),
rpc.WithToken(
c.String("token"),
),
)
if err != nil {
return err
}
defer client.Close()
sigterm := abool.New()
ctx := context.Background()
ctx = interrupt.WithContextFunc(ctx, func() {
println("ctrl+c received, terminating process")
sigterm.Set()
})
for {
if sigterm.IsSet() {
return nil
}
if err := run(ctx, client, filter); err != nil {
return err
}
}
}
func run(ctx context.Context, client rpc.Peer, filter rpc.Filter) error {
log.Println("pipeline: request next execution")
// get the next job from the queue
work, err := client.Next(ctx, filter)
if err != nil {
return err
}
if work == nil {
return nil
}
log.Printf("pipeline: received next execution: %s", work.ID)
if os.Getenv("SUICIDE_MODE") != "" {
os.Exit(1)
}
// new docker engine
engine, err := docker.NewEnv()
if err != nil {
return err
}
timeout := time.Hour
if minutes := work.Timeout; minutes != 0 {
timeout = time.Duration(minutes) * time.Minute
}
ctx, cancel := context.WithTimeout(context.Background(), timeout)
defer cancel()
cancelled := abool.New()
go func() {
werr := client.Wait(ctx, work.ID)
if werr != nil {
cancelled.SetTo(true) // TODO verify error is really an error
log.Printf("pipeline: cancel signal received: %s: %s", work.ID, werr)
cancel()
} else {
log.Printf("pipeline: cancel channel closed: %s", work.ID)
}
}()
go func() {
for {
select {
case <-ctx.Done():
log.Printf("pipeline: cancel ping loop: %s", work.ID)
return
case <-time.After(time.Minute):
log.Printf("pipeline: ping queue: %s", work.ID)
client.Extend(ctx, work.ID)
}
}
}()
state := rpc.State{}
state.Started = time.Now().Unix()
err = client.Init(context.Background(), work.ID, state)
if err != nil {
log.Printf("pipeline: error signaling pipeline init: %s: %s", work.ID, err)
}
var uploads sync.WaitGroup
defaultLogger := pipeline.LogFunc(func(proc *backend.Step, rc multipart.Reader) error {
part, rerr := rc.NextPart()
if rerr != nil {
return rerr
}
uploads.Add(1)
var secrets []string
for _, secret := range work.Config.Secrets {
if secret.Mask {
secrets = append(secrets, secret.Value)
}
}
limitedPart := io.LimitReader(part, maxLogsUpload)
logstream := rpc.NewLineWriter(client, work.ID, proc.Alias, secrets...)
io.Copy(logstream, limitedPart)
file := &rpc.File{}
file.Mime = "application/json+logs"
file.Proc = proc.Alias
file.Name = "logs.json"
file.Data, _ = json.Marshal(logstream.Lines())
file.Size = len(file.Data)
file.Time = time.Now().Unix()
if serr := client.Upload(context.Background(), work.ID, file); serr != nil {
log.Printf("pipeline: cannot upload logs: %s: %s: %s", work.ID, file.Mime, serr)
} else {
log.Printf("pipeline: finish uploading logs: %s: step %s: %s", file.Mime, work.ID, proc.Alias)
}
defer func() {
log.Printf("pipeline: finish uploading logs: %s: step %s", work.ID, proc.Alias)
uploads.Done()
}()
part, rerr = rc.NextPart()
if rerr != nil {
return nil
}
// TODO should be configurable
limitedPart = io.LimitReader(part, maxFileUpload)
file = &rpc.File{}
file.Mime = part.Header().Get("Content-Type")
file.Proc = proc.Alias
file.Name = part.FileName()
file.Data, _ = ioutil.ReadAll(limitedPart)
file.Size = len(file.Data)
file.Time = time.Now().Unix()
if serr := client.Upload(context.Background(), work.ID, file); serr != nil {
log.Printf("pipeline: cannot upload artifact: %s: %s: %s", work.ID, file.Mime, serr)
} else {
log.Printf("pipeline: finish uploading artifact: %s: step %s: %s", file.Mime, work.ID, proc.Alias)
}
return nil
})
defaultTracer := pipeline.TraceFunc(func(state *pipeline.State) error {
procState := rpc.State{
Proc: state.Pipeline.Step.Alias,
Exited: state.Process.Exited,
ExitCode: state.Process.ExitCode,
Started: time.Now().Unix(), // TODO do not do this
Finished: time.Now().Unix(),
}
defer func() {
if uerr := client.Update(context.Background(), work.ID, procState); uerr != nil {
log.Printf("Pipeine: error updating pipeline step status: %s: %s: %s", work.ID, procState.Proc, uerr)
}
}()
if state.Process.Exited {
return nil
}
if state.Pipeline.Step.Environment == nil {
state.Pipeline.Step.Environment = map[string]string{}
}
state.Pipeline.Step.Environment["CI_BUILD_STATUS"] = "success"
state.Pipeline.Step.Environment["CI_BUILD_STARTED"] = strconv.FormatInt(state.Pipeline.Time, 10)
state.Pipeline.Step.Environment["CI_BUILD_FINISHED"] = strconv.FormatInt(time.Now().Unix(), 10)
state.Pipeline.Step.Environment["CI_JOB_STATUS"] = "success"
state.Pipeline.Step.Environment["CI_JOB_STARTED"] = strconv.FormatInt(state.Pipeline.Time, 10)
state.Pipeline.Step.Environment["CI_JOB_FINISHED"] = strconv.FormatInt(time.Now().Unix(), 10)
if state.Pipeline.Error != nil {
state.Pipeline.Step.Environment["CI_BUILD_STATUS"] = "failure"
state.Pipeline.Step.Environment["CI_JOB_STATUS"] = "failure"
}
return nil
})
err = pipeline.New(work.Config,
pipeline.WithContext(ctx),
pipeline.WithLogger(defaultLogger),
pipeline.WithTracer(defaultTracer),
pipeline.WithEngine(engine),
).Run()
state.Finished = time.Now().Unix()
state.Exited = true
if err != nil {
state.Error = err.Error()
if xerr, ok := err.(*pipeline.ExitError); ok {
state.ExitCode = xerr.Code
}
if xerr, ok := err.(*pipeline.OomError); ok {
state.ExitCode = xerr.Code
}
if cancelled.IsSet() {
state.ExitCode = 130
} else if state.ExitCode == 0 {
state.ExitCode = 1
}
}
log.Printf("pipeline: execution complete: %s", work.ID)
uploads.Wait()
err = client.Done(context.Background(), work.ID, state)
if err != nil {
log.Printf("Pipeine: error signaling pipeline done: %s: %s", work.ID, err)
}
return nil
}

View file

@ -0,0 +1,97 @@
package main
import (
"context"
"encoding/json"
"math"
"net/url"
"time"
"github.com/laszlocph/drone-oss-08/cncd/pipeline/pipeline/interrupt"
"github.com/laszlocph/drone-oss-08/cncd/pipeline/pipeline/rpc"
_ "github.com/joho/godotenv/autoload"
"github.com/urfave/cli"
)
var onceCommand = cli.Command{
Name: "once",
Usage: "execute one build",
Hidden: false,
Action: once,
Flags: []cli.Flag{
cli.StringFlag{
Name: "endpoint",
EnvVar: "PIPED_ENDPOINT,PIPED_SERVER",
Value: "ws://localhost:9999",
},
cli.StringFlag{
Name: "token",
EnvVar: "PIPED_TOKEN,PIPED_SECRET",
},
cli.DurationFlag{
Name: "backoff",
EnvVar: "PIPED_BACKOFF",
Value: time.Second * 15,
},
cli.IntFlag{
Name: "retry-limit",
EnvVar: "PIPED_RETRY_LIMIT",
Value: math.MaxInt32,
},
cli.StringFlag{
Name: "platform",
EnvVar: "PIPED_PLATFORM",
Value: "linux/amd64",
},
cli.StringFlag{
Name: "json",
EnvVar: "PIPED_JSON",
},
},
}
func once(c *cli.Context) error {
endpoint, err := url.Parse(
c.String("endpoint"),
)
if err != nil {
return err
}
client, err := rpc.NewClient(
endpoint.String(),
rpc.WithRetryLimit(
c.Int("retry-limit"),
),
rpc.WithBackoff(
c.Duration("backoff"),
),
rpc.WithToken(
c.String("token"),
),
)
if err != nil {
return err
}
defer client.Close()
ctx := context.Background()
ctx = interrupt.WithContextFunc(ctx, func() {
println("ctrl+c received, terminating process")
})
return run(ctx, &onceClient{client, c.String("json")}, rpc.NoFilter)
}
type onceClient struct {
*rpc.Client
json string
}
func (c *onceClient) Next(ctx context.Context, filter rpc.Filter) (*rpc.Pipeline, error) {
in := []byte(c.json)
out := new(rpc.Pipeline)
err := json.Unmarshal(in, out)
return out, err
}

View file

@ -3,9 +3,10 @@ package docker
import (
"encoding/base64"
"encoding/json"
"regexp"
"strings"
"github.com/cncd/pipeline/pipeline/backend"
"github.com/laszlocph/drone-oss-08/cncd/pipeline/pipeline/backend"
"github.com/docker/docker/api/types/container"
)
@ -82,7 +83,10 @@ func toHostConfig(proc *backend.Step) *container.HostConfig {
config.Tmpfs[path] = ""
continue
}
parts := strings.Split(path, ":")
parts, err := splitVolumeParts(path)
if err != nil {
continue
}
config.Tmpfs[parts[0]] = parts[1]
}
// if proc.OomKillDisable {
@ -97,7 +101,10 @@ func toHostConfig(proc *backend.Step) *container.HostConfig {
func toVol(paths []string) map[string]struct{} {
set := map[string]struct{}{}
for _, path := range paths {
parts := strings.Split(path, ":")
parts, err := splitVolumeParts(path)
if err != nil {
continue
}
if len(parts) < 2 {
continue
}
@ -121,10 +128,16 @@ func toEnv(env map[string]string) []string {
func toDev(paths []string) []container.DeviceMapping {
var devices []container.DeviceMapping
for _, path := range paths {
parts := strings.Split(path, ":")
parts, err := splitVolumeParts(path)
if err != nil {
continue
}
if len(parts) < 2 {
continue
}
if strings.HasSuffix(parts[1], ":ro") || strings.HasSuffix(parts[1], ":rw") {
parts[1] = parts[1][:len(parts[1])-1]
}
devices = append(devices, container.DeviceMapping{
PathOnHost: parts[0],
PathInContainer: parts[1],
@ -143,3 +156,24 @@ func encodeAuthToBase64(authConfig backend.Auth) (string, error) {
}
return base64.URLEncoding.EncodeToString(buf), nil
}
// helper function that split volume path
func splitVolumeParts(volumeParts string) ([]string, error) {
pattern := `^((?:[\w]\:)?[^\:]*)\:((?:[\w]\:)?[^\:]*)(?:\:([rwom]*))?`
r, err := regexp.Compile(pattern)
if err != nil {
return []string{}, err
}
if r.MatchString(volumeParts) {
results := r.FindStringSubmatch(volumeParts)[1:]
cleanResults := []string{}
for _, item := range results {
if item != "" {
cleanResults = append(cleanResults, item)
}
}
return cleanResults, nil
} else {
return strings.Split(volumeParts, ":"), nil
}
}

View file

@ -0,0 +1,74 @@
package docker
import (
"reflect"
"testing"
)
func TestSplitVolumeParts(t *testing.T) {
testdata := []struct {
from string
to []string
success bool
}{
{
from: `Z::Z::rw`,
to: []string{`Z:`, `Z:`, `rw`},
success: true,
},
{
from: `Z:\:Z:\:rw`,
to: []string{`Z:\`, `Z:\`, `rw`},
success: true,
},
{
from: `Z:\git\refs:Z:\git\refs:rw`,
to: []string{`Z:\git\refs`, `Z:\git\refs`, `rw`},
success: true,
},
{
from: `Z:\git\refs:Z:\git\refs`,
to: []string{`Z:\git\refs`, `Z:\git\refs`},
success: true,
},
{
from: `Z:/:Z:/:rw`,
to: []string{`Z:/`, `Z:/`, `rw`},
success: true,
},
{
from: `Z:/git/refs:Z:/git/refs:rw`,
to: []string{`Z:/git/refs`, `Z:/git/refs`, `rw`},
success: true,
},
{
from: `Z:/git/refs:Z:/git/refs`,
to: []string{`Z:/git/refs`, `Z:/git/refs`},
success: true,
},
{
from: `/test:/test`,
to: []string{`/test`, `/test`},
success: true,
},
{
from: `test:/test`,
to: []string{`test`, `/test`},
success: true,
},
{
from: `test:test`,
to: []string{`test`, `test`},
success: true,
},
}
for _, test := range testdata {
results, err := splitVolumeParts(test.from)
if test.success == (err != nil) {
} else {
if reflect.DeepEqual(results, test.to) != test.success {
t.Errorf("Expect %q matches %q is %v", test.from, results, test.to)
}
}
}
}

View file

@ -5,7 +5,7 @@ import (
"io"
"io/ioutil"
"github.com/cncd/pipeline/pipeline/backend"
"github.com/laszlocph/drone-oss-08/cncd/pipeline/pipeline/backend"
"github.com/docker/docker/api/types"
"github.com/docker/docker/api/types/network"

View file

@ -3,7 +3,7 @@ package docker
// import (
// "context"
//
// "github.com/cncd/pipeline/pipeline/backend"
// "github.com/laszlocph/drone-oss-08/cncd/pipeline/pipeline/backend"
// )
//
// // Pool manages a pool of Docker clients.

View file

@ -0,0 +1,61 @@
package kubernetes
import (
"context"
"io"
"github.com/laszlocph/drone-oss-08/cncd/pipeline/pipeline/backend"
)
type engine struct {
namespace string
endpoint string
token string
}
// New returns a new Kubernetes Engine.
func New(namespace, endpoint, token string) backend.Engine {
return &engine{
namespace: namespace,
endpoint: endpoint,
token: token,
}
}
// Setup the pipeline environment.
func (e *engine) Setup(context.Context, *backend.Config) error {
// POST /api/v1/namespaces
return nil
}
// Start the pipeline step.
func (e *engine) Exec(context.Context, *backend.Step) error {
// POST /api/v1/namespaces/{namespace}/pods
return nil
}
// DEPRECATED
// Kill the pipeline step.
func (e *engine) Kill(context.Context, *backend.Step) error {
return nil
}
// Wait for the pipeline step to complete and returns
// the completion results.
func (e *engine) Wait(context.Context, *backend.Step) (*backend.State, error) {
// GET /api/v1/watch/namespaces/{namespace}/pods
// GET /api/v1/watch/namespaces/{namespace}/pods/{name}
return nil, nil
}
// Tail the pipeline step logs.
func (e *engine) Tail(context.Context, *backend.Step) (io.ReadCloser, error) {
// GET /api/v1/namespaces/{namespace}/pods/{name}/log
return nil, nil
}
// Destroy the pipeline environment.
func (e *engine) Destroy(context.Context, *backend.Config) error {
// DELETE /api/v1/namespaces/{name}
return nil
}

View file

@ -0,0 +1,26 @@
package pipeline
import (
"testing"
)
func TestExitError(t *testing.T) {
err := ExitError{
Name: "build",
Code: 255,
}
got, want := err.Error(), "build : exit code 255"
if got != want {
t.Errorf("Want error message %q, got %q", want, got)
}
}
func TestOomError(t *testing.T) {
err := OomError{
Name: "build",
}
got, want := err.Error(), "build : received oom kill"
if got != want {
t.Errorf("Want error message %q, got %q", want, got)
}
}

View file

@ -176,7 +176,7 @@ func (m *Metadata) EnvironDrone() map[string]string {
params := map[string]string{
"CI": "drone",
"DRONE": "true",
"DRONE_ARCH": "linux/amd64",
"DRONE_ARCH": m.Sys.Arch,
"DRONE_REPO": m.Repo.Name,
"DRONE_REPO_SCM": "git",
"DRONE_REPO_OWNER": owner,

View file

@ -0,0 +1 @@
package frontend

View file

@ -4,7 +4,7 @@ import (
"path"
"strings"
"github.com/cncd/pipeline/pipeline/frontend/yaml"
"github.com/laszlocph/drone-oss-08/cncd/pipeline/pipeline/frontend/yaml"
libcompose "github.com/docker/libcompose/yaml"
)

View file

@ -3,9 +3,9 @@ package compiler
import (
"fmt"
"github.com/cncd/pipeline/pipeline/backend"
"github.com/cncd/pipeline/pipeline/frontend"
"github.com/cncd/pipeline/pipeline/frontend/yaml"
"github.com/laszlocph/drone-oss-08/cncd/pipeline/pipeline/backend"
"github.com/laszlocph/drone-oss-08/cncd/pipeline/pipeline/frontend"
"github.com/laszlocph/drone-oss-08/cncd/pipeline/pipeline/frontend/yaml"
)
// TODO(bradrydzewski) compiler should handle user-defined volumes from YAML
@ -75,10 +75,17 @@ func (c *Compiler) Compile(conf *yaml.Config) *backend.Config {
})
// create a default network
config.Networks = append(config.Networks, &backend.Network{
Name: fmt.Sprintf("%s_default", c.prefix),
Driver: "bridge",
})
if c.metadata.Sys.Arch == "windows/amd64" {
config.Networks = append(config.Networks, &backend.Network{
Name: fmt.Sprintf("%s_default", c.prefix),
Driver: "nat",
})
} else {
config.Networks = append(config.Networks, &backend.Network{
Name: fmt.Sprintf("%s_default", c.prefix),
Driver: "bridge",
})
}
// overrides the default workspace paths when specified
// in the YAML file.

View file

@ -0,0 +1 @@
package compiler

View file

@ -5,8 +5,8 @@ import (
"path"
"strings"
"github.com/cncd/pipeline/pipeline/backend"
"github.com/cncd/pipeline/pipeline/frontend/yaml"
"github.com/laszlocph/drone-oss-08/cncd/pipeline/pipeline/backend"
"github.com/laszlocph/drone-oss-08/cncd/pipeline/pipeline/frontend/yaml"
)
func (c *Compiler) createProcess(name string, container *yaml.Container, section string) *backend.Step {
@ -77,12 +77,11 @@ func (c *Compiler) createProcess(name string, container *yaml.Container, section
if len(container.Commands) != 0 {
if c.metadata.Sys.Arch == "windows/amd64" {
// TODO provide windows implementation
entrypoint = []string{"/bin/sh", "-c"}
command = []string{"echo $CI_SCRIPT | base64 -d | /bin/sh -e"}
entrypoint = []string{"powershell", "-noprofile", "-noninteractive", "-command"}
command = []string{"[System.Text.Encoding]::UTF8.GetString([System.Convert]::FromBase64String($Env:CI_SCRIPT)) | iex"}
environment["CI_SCRIPT"] = generateScriptWindows(container.Commands)
environment["HOME"] = "/root"
environment["SHELL"] = "/bin/sh"
environment["HOME"] = "c:\\root"
environment["SHELL"] = "powershell.exe"
} else {
entrypoint = []string{"/bin/sh", "-c"}
command = []string{"echo $CI_SCRIPT | base64 -d | /bin/sh -e"}

View file

@ -0,0 +1 @@
package compiler

View file

@ -0,0 +1,244 @@
package compiler
import (
"testing"
)
func TestTrimImage(t *testing.T) {
testdata := []struct {
from string
want string
}{
{
from: "golang",
want: "golang",
},
{
from: "golang:latest",
want: "golang",
},
{
from: "golang:1.0.0",
want: "golang",
},
{
from: "library/golang",
want: "golang",
},
{
from: "library/golang:latest",
want: "golang",
},
{
from: "library/golang:1.0.0",
want: "golang",
},
{
from: "index.docker.io/library/golang:1.0.0",
want: "golang",
},
{
from: "gcr.io/library/golang:1.0.0",
want: "gcr.io/library/golang",
},
// error cases, return input unmodified
{
from: "foo/bar?baz:boo",
want: "foo/bar?baz:boo",
},
}
for _, test := range testdata {
got, want := trimImage(test.from), test.want
if got != want {
t.Errorf("Want image %q trimmed to %q, got %q", test.from, want, got)
}
}
}
func TestExpandImage(t *testing.T) {
testdata := []struct {
from string
want string
}{
{
from: "golang",
want: "golang:latest",
},
{
from: "golang:latest",
want: "golang:latest",
},
{
from: "golang:1.0.0",
want: "golang:1.0.0",
},
{
from: "library/golang",
want: "golang:latest",
},
{
from: "library/golang:latest",
want: "golang:latest",
},
{
from: "library/golang:1.0.0",
want: "golang:1.0.0",
},
{
from: "index.docker.io/library/golang:1.0.0",
want: "golang:1.0.0",
},
{
from: "gcr.io/golang",
want: "gcr.io/golang:latest",
},
{
from: "gcr.io/golang:1.0.0",
want: "gcr.io/golang:1.0.0",
},
// error cases, return input unmodified
{
from: "foo/bar?baz:boo",
want: "foo/bar?baz:boo",
},
}
for _, test := range testdata {
got, want := expandImage(test.from), test.want
if got != want {
t.Errorf("Want image %q expanded to %q, got %q", test.from, want, got)
}
}
}
func TestMatchImage(t *testing.T) {
testdata := []struct {
from, to string
want bool
}{
{
from: "golang",
to: "golang",
want: true,
},
{
from: "golang:latest",
to: "golang",
want: true,
},
{
from: "library/golang:latest",
to: "golang",
want: true,
},
{
from: "index.docker.io/library/golang:1.0.0",
to: "golang",
want: true,
},
{
from: "golang",
to: "golang:latest",
want: true,
},
{
from: "library/golang:latest",
to: "library/golang",
want: true,
},
{
from: "gcr.io/golang",
to: "gcr.io/golang",
want: true,
},
{
from: "gcr.io/golang:1.0.0",
to: "gcr.io/golang",
want: true,
},
{
from: "gcr.io/golang:latest",
to: "gcr.io/golang",
want: true,
},
{
from: "gcr.io/golang",
to: "gcr.io/golang:latest",
want: true,
},
{
from: "golang",
to: "library/golang",
want: true,
},
{
from: "golang",
to: "gcr.io/project/golang",
want: false,
},
{
from: "golang",
to: "gcr.io/library/golang",
want: false,
},
{
from: "golang",
to: "gcr.io/golang",
want: false,
},
}
for _, test := range testdata {
got, want := matchImage(test.from, test.to), test.want
if got != want {
t.Errorf("Want image %q matching %q is %v", test.from, test.to, want)
}
}
}
func TestMatchHostname(t *testing.T) {
testdata := []struct {
image, hostname string
want bool
}{
{
image: "golang",
hostname: "docker.io",
want: true,
},
{
image: "golang:latest",
hostname: "docker.io",
want: true,
},
{
image: "library/golang:latest",
hostname: "docker.io",
want: true,
},
{
image: "docker.io/library/golang:1.0.0",
hostname: "docker.io",
want: true,
},
{
image: "gcr.io/golang",
hostname: "docker.io",
want: false,
},
{
image: "gcr.io/golang:1.0.0",
hostname: "gcr.io",
want: true,
},
{
image: "1.2.3.4:8000/golang:1.0.0",
hostname: "1.2.3.4:8000",
want: true,
},
}
for _, test := range testdata {
got, want := matchHostname(test.image, test.hostname), test.want
if got != want {
t.Errorf("Want image %q matching hostname %q is %v", test.image, test.hostname, want)
}
}
}

View file

@ -6,7 +6,7 @@ import (
"path/filepath"
"strings"
"github.com/cncd/pipeline/pipeline/frontend"
"github.com/laszlocph/drone-oss-08/cncd/pipeline/pipeline/frontend"
)
// Option configures a compiler option.

View file

@ -0,0 +1,261 @@
package compiler
import (
"os"
"reflect"
"testing"
"github.com/laszlocph/drone-oss-08/cncd/pipeline/pipeline/frontend"
)
func TestWithWorkspace(t *testing.T) {
compiler := New(
WithWorkspace(
"/pipeline",
"src/github.com/octocat/hello-world",
),
)
if compiler.base != "/pipeline" {
t.Errorf("WithWorkspace must set the base directory")
}
if compiler.path != "src/github.com/octocat/hello-world" {
t.Errorf("WithWorkspace must set the path directory")
}
}
func TestWithEscalated(t *testing.T) {
compiler := New(
WithEscalated(
"docker",
"docker-dev",
),
)
if compiler.escalated[0] != "docker" || compiler.escalated[1] != "docker-dev" {
t.Errorf("WithEscalated must whitelist privileged images")
}
}
func TestWithVolumes(t *testing.T) {
compiler := New(
WithVolumes(
"/tmp:/tmp",
"/foo:/foo",
),
)
if compiler.volumes[0] != "/tmp:/tmp" || compiler.volumes[1] != "/foo:/foo" {
t.Errorf("TestWithVolumes must set default volumes")
}
}
func TestWithNetworks(t *testing.T) {
compiler := New(
WithNetworks(
"overlay_1",
"overlay_bar",
),
)
if compiler.networks[0] != "overlay_1" || compiler.networks[1] != "overlay_bar" {
t.Errorf("TestWithNetworks must set networks from parameters")
}
}
func TestWithResourceLimit(t *testing.T) {
compiler := New(
WithResourceLimit(
1,
2,
3,
4,
5,
"0,2-5",
),
)
if compiler.reslimit.MemSwapLimit != 1 {
t.Errorf("TestWithResourceLimit must set MemSwapLimit from parameters")
}
if compiler.reslimit.MemLimit != 2 {
t.Errorf("TestWithResourceLimit must set MemLimit from parameters")
}
if compiler.reslimit.ShmSize != 3 {
t.Errorf("TestWithResourceLimit must set ShmSize from parameters")
}
if compiler.reslimit.CPUQuota != 4 {
t.Errorf("TestWithResourceLimit must set CPUQuota from parameters")
}
if compiler.reslimit.CPUShares != 5 {
t.Errorf("TestWithResourceLimit must set CPUShares from parameters")
}
if compiler.reslimit.CPUSet != "0,2-5" {
t.Errorf("TestWithResourceLimit must set CPUSet from parameters")
}
}
func TestWithPrefix(t *testing.T) {
if New(WithPrefix("drone_")).prefix != "drone_" {
t.Errorf("WithPrefix must set the prefix")
}
}
func TestWithMetadata(t *testing.T) {
metadata := frontend.Metadata{
Repo: frontend.Repo{
Name: "octocat/hello-world",
Private: true,
Link: "https://github.com/octocat/hello-world",
Remote: "https://github.com/octocat/hello-world.git",
},
}
compiler := New(
WithMetadata(metadata),
)
if !reflect.DeepEqual(compiler.metadata, metadata) {
t.Errorf("WithMetadata must set compiler the metadata")
}
if compiler.env["CI_REPO_NAME"] != metadata.Repo.Name {
t.Errorf("WithMetadata must set CI_REPO_NAME")
}
if compiler.env["CI_REPO_LINK"] != metadata.Repo.Link {
t.Errorf("WithMetadata must set CI_REPO_LINK")
}
if compiler.env["CI_REPO_REMOTE"] != metadata.Repo.Remote {
t.Errorf("WithMetadata must set CI_REPO_REMOTE")
}
}
func TestWithLocal(t *testing.T) {
if New(WithLocal(true)).local == false {
t.Errorf("WithLocal true must enable the local flag")
}
if New(WithLocal(false)).local == true {
t.Errorf("WithLocal false must disable the local flag")
}
}
func TestWithNetrc(t *testing.T) {
compiler := New(
WithNetrc(
"octocat",
"password",
"github.com",
),
)
if compiler.env["CI_NETRC_USERNAME"] != "octocat" {
t.Errorf("WithNetrc should set CI_NETRC_USERNAME")
}
if compiler.env["CI_NETRC_PASSWORD"] != "password" {
t.Errorf("WithNetrc should set CI_NETRC_PASSWORD")
}
if compiler.env["CI_NETRC_MACHINE"] != "github.com" {
t.Errorf("WithNetrc should set CI_NETRC_MACHINE")
}
}
func TestWithProxy(t *testing.T) {
// do not execute the test if the host machine sets http proxy
// environment variables to avoid interference with other tests.
if noProxy != "" || httpProxy != "" || httpsProxy != "" {
t.SkipNow()
return
}
// alter the default values
noProxy = "foo.com"
httpProxy = "bar.com"
httpsProxy = "baz.com"
// reset the default values
defer func() {
noProxy = ""
httpProxy = ""
httpsProxy = ""
}()
testdata := map[string]string{
"no_proxy": noProxy,
"NO_PROXY": noProxy,
"http_proxy": httpProxy,
"HTTP_PROXY": httpProxy,
"https_proxy": httpsProxy,
"HTTPS_PROXY": httpsProxy,
}
compiler := New(
WithProxy(),
)
for key, value := range testdata {
if compiler.env[key] != value {
t.Errorf("WithProxy should set %s=%s", key, value)
}
}
}
func TestWithEnviron(t *testing.T) {
compiler := New(
WithEnviron(
map[string]string{
"RACK_ENV": "development",
"SHOW": "true",
},
),
)
if compiler.env["RACK_ENV"] != "development" {
t.Errorf("WithEnviron should set RACK_ENV")
}
if compiler.env["SHOW"] != "true" {
t.Errorf("WithEnviron should set SHOW")
}
}
func TestGetenv(t *testing.T) {
defer func() {
os.Unsetenv("X_TEST_FOO")
os.Unsetenv("x_test_bar")
os.Unsetenv("x_test_baz")
}()
os.Setenv("X_TEST_FOO", "foo")
os.Setenv("x_test_bar", "bar")
os.Setenv("x_test_baz", "")
if getenv("x_test_foo") != "foo" {
t.Errorf("Expect X_TEST_FOO=foo")
}
if getenv("X_TEST_BAR") != "bar" {
t.Errorf("Expect x_test_bar=bar")
}
if getenv("x_test_baz") != "" {
t.Errorf("Expect x_test_bar=bar is empty")
}
}
func TestWithVolumeCacher(t *testing.T) {
compiler := New(
WithVolumeCacher("/cache"),
)
cacher, ok := compiler.cacher.(*volumeCacher)
if !ok {
t.Errorf("Expected volume cacher configured")
}
if got, want := cacher.base, "/cache"; got != want {
t.Errorf("Expected volume cacher with base %s, got %s", want, got)
}
}
func TestWithS3Cacher(t *testing.T) {
compiler := New(
WithS3Cacher("some-access-key", "some-secret-key", "some-region", "some-bucket"),
)
cacher, ok := compiler.cacher.(*s3Cacher)
if !ok {
t.Errorf("Expected s3 cacher configured")
}
if got, want := cacher.bucket, "some-bucket"; got != want {
t.Errorf("Expected s3 cacher with bucket %s, got %s", want, got)
}
if got, want := cacher.access, "some-access-key"; got != want {
t.Errorf("Expected s3 cacher with access key %s, got %s", want, got)
}
if got, want := cacher.region, "some-region"; got != want {
t.Errorf("Expected s3 cacher with region %s, got %s", want, got)
}
if got, want := cacher.secret, "some-secret-key"; got != want {
t.Errorf("Expected s3 cacher with secret key %s, got %s", want, got)
}
}

View file

@ -0,0 +1,37 @@
package compiler
import (
"reflect"
"testing"
"github.com/kr/pretty"
)
func TestParamsToEnv(t *testing.T) {
from := map[string]interface{}{
"skip": nil,
"string": "stringz",
"int": 1,
"float": 1.2,
"bool": true,
"map": map[string]string{"hello": "world"},
"slice": []int{1, 2, 3},
"complex": []struct{ Name string }{{"Jack"}, {"Jill"}},
}
want := map[string]string{
"PLUGIN_STRING": "stringz",
"PLUGIN_INT": "1",
"PLUGIN_FLOAT": "1.2",
"PLUGIN_BOOL": "true",
"PLUGIN_MAP": `{"hello":"world"}`,
"PLUGIN_SLICE": "1,2,3",
"PLUGIN_COMPLEX": `[{"name":"Jack"},{"name":"Jill"}]`,
}
got := map[string]string{}
paramsToEnv(from, got)
if !reflect.DeepEqual(want, got) {
t.Errorf("Problem converting plugin parameters to environment variables")
pretty.Ldiff(t, want, got)
}
}

View file

@ -0,0 +1,54 @@
package compiler
import (
"encoding/base64"
"testing"
"github.com/kr/pretty"
)
func TestGenerateScriptPosix(t *testing.T) {
testdata := []struct {
from []string
want string
}{
{
from: []string{"echo ${PATH}", "go build", "go test"},
want: `
if [ -n "$CI_NETRC_MACHINE" ]; then
cat <<EOF > $HOME/.netrc
machine $CI_NETRC_MACHINE
login $CI_NETRC_USERNAME
password $CI_NETRC_PASSWORD
EOF
chmod 0600 $HOME/.netrc
fi
unset CI_NETRC_USERNAME
unset CI_NETRC_PASSWORD
unset CI_SCRIPT
unset DRONE_NETRC_USERNAME
unset DRONE_NETRC_PASSWORD
echo + "echo \${PATH}"
echo ${PATH}
echo + "go build"
go build
echo + "go test"
go test
`,
},
}
for _, test := range testdata {
script := generateScriptPosix(test.from)
decoded, _ := base64.StdEncoding.DecodeString(script)
got := string(decoded)
if got != test.want {
t.Errorf("Want encoded script for %s", test.from)
pretty.Ldiff(t, got, test.want)
}
}
}

View file

@ -0,0 +1,49 @@
package compiler
import (
"bytes"
"encoding/base64"
"fmt"
"strings"
)
func generateScriptWindows(commands []string) string {
var buf bytes.Buffer
for _, command := range commands {
escaped := fmt.Sprintf("%q", command)
escaped = strings.Replace(escaped, "$", `\$`, -1)
buf.WriteString(fmt.Sprintf(
traceScriptWin,
escaped,
command,
))
}
script := fmt.Sprintf(
setupScriptWin,
buf.String(),
)
return base64.StdEncoding.EncodeToString([]byte(script))
}
const setupScriptWin = `
$ErrorActionPreference = 'Stop';
&cmd /c "mkdir c:\root";
if ($Env:CI_NETRC_MACHINE) {
$netrc=[string]::Format("{0}\_netrc",$Env:HOME);
"machine $Env:CI_NETRC_MACHINE" >> $netrc;
"login $Env:CI_NETRC_USERNAME" >> $netrc;
"password $Env:CI_NETRC_PASSWORD" >> $netrc;
};
[Environment]::SetEnvironmentVariable("CI_NETRC_PASSWORD",$null);
[Environment]::SetEnvironmentVariable("CI_SCRIPT",$null);
[Environment]::SetEnvironmentVariable("DRONE_NETRC_USERNAME",$null);
[Environment]::SetEnvironmentVariable("DRONE_NETRC_PASSWORD",$null);
%s
`
// traceScript is a helper script that is added to the build script
// to trace a command.
const traceScriptWin = `
Write-Output ('+ %s');
& %s; if ($LASTEXITCODE -ne 0) {exit $LASTEXITCODE}
`

View file

@ -0,0 +1 @@
package compiler

View file

@ -0,0 +1,108 @@
package yaml
import (
"testing"
"github.com/docker/libcompose/yaml"
"github.com/franela/goblin"
)
func xTestParse(t *testing.T) {
g := goblin.Goblin(t)
g.Describe("Parser", func() {
g.Describe("Given a yaml file", func() {
g.It("Should unmarshal a string", func() {
out, err := ParseString(sampleYaml)
if err != nil {
g.Fail(err)
}
g.Assert(out.Workspace.Base).Equal("/go")
g.Assert(out.Workspace.Path).Equal("src/github.com/octocat/hello-world")
g.Assert(out.Volumes.Volumes[0].Name).Equal("custom")
g.Assert(out.Volumes.Volumes[0].Driver).Equal("blockbridge")
g.Assert(out.Networks.Networks[0].Name).Equal("custom")
g.Assert(out.Networks.Networks[0].Driver).Equal("overlay")
g.Assert(out.Services.Containers[0].Name).Equal("database")
g.Assert(out.Services.Containers[0].Image).Equal("mysql")
g.Assert(out.Pipeline.Containers[0].Name).Equal("test")
g.Assert(out.Pipeline.Containers[0].Image).Equal("golang")
g.Assert(out.Pipeline.Containers[0].Commands).Equal(yaml.Stringorslice{"go install", "go test"})
g.Assert(out.Pipeline.Containers[1].Name).Equal("build")
g.Assert(out.Pipeline.Containers[1].Image).Equal("golang")
g.Assert(out.Pipeline.Containers[1].Commands).Equal(yaml.Stringorslice{"go build"})
g.Assert(out.Pipeline.Containers[2].Name).Equal("notify")
g.Assert(out.Pipeline.Containers[2].Image).Equal("slack")
g.Assert(out.Pipeline.Containers[2].NetworkMode).Equal("container:name")
g.Assert(out.Labels["com.example.team"]).Equal("frontend")
g.Assert(out.Labels["com.example.type"]).Equal("build")
})
// Check to make sure variable expansion works in yaml.MapSlice
// g.It("Should unmarshal variables", func() {
// out, err := ParseString(sampleVarYaml)
// if err != nil {
// g.Fail(err)
// }
// g.Assert(out.Pipeline[0].Name).Equal("notify_fail")
// g.Assert(out.Pipeline[0].Image).Equal("plugins/slack")
// g.Assert(len(out.Pipeline[0].Constraints.Event.Include)).Equal(0)
// g.Assert(out.Pipeline[1].Name).Equal("notify_success")
// g.Assert(out.Pipeline[1].Image).Equal("plugins/slack")
// g.Assert(out.Pipeline[1].Constraints.Event.Include).Equal([]string{"success"})
// })
})
})
}
var sampleYaml = `
image: hello-world
build:
context: .
dockerfile: Dockerfile
workspace:
path: src/github.com/octocat/hello-world
base: /go
pipeline:
test:
image: golang
commands:
- go install
- go test
build:
image: golang
network_mode: container:name
commands:
- go build
when:
event: push
notify:
image: slack
channel: dev
when:
event: failure
services:
database:
image: mysql
networks:
custom:
driver: overlay
volumes:
custom:
driver: blockbridge
labels:
com.example.type: "build"
com.example.team: "frontend"
`
var sampleVarYaml = `
_slack: &SLACK
image: plugins/slack
pipeline:
notify_fail: *SLACK
notify_success:
<< : *SLACK
when:
event: success
`

View file

@ -3,8 +3,8 @@ package yaml
import (
"path/filepath"
"github.com/cncd/pipeline/pipeline/frontend"
"github.com/cncd/pipeline/pipeline/frontend/yaml/types"
"github.com/laszlocph/drone-oss-08/cncd/pipeline/pipeline/frontend"
"github.com/laszlocph/drone-oss-08/cncd/pipeline/pipeline/frontend/yaml/types"
libcompose "github.com/docker/libcompose/yaml"
)

View file

@ -0,0 +1,373 @@
package yaml
import (
"testing"
"github.com/laszlocph/drone-oss-08/cncd/pipeline/pipeline/frontend"
"gopkg.in/yaml.v2"
)
func TestConstraint(t *testing.T) {
testdata := []struct {
conf string
with string
want bool
}{
// string value
{
conf: "master",
with: "develop",
want: false,
},
{
conf: "master",
with: "master",
want: true,
},
{
conf: "feature/*",
with: "feature/foo",
want: true,
},
// slice value
{
conf: "[ master, feature/* ]",
with: "develop",
want: false,
},
{
conf: "[ master, feature/* ]",
with: "master",
want: true,
},
{
conf: "[ master, feature/* ]",
with: "feature/foo",
want: true,
},
// includes block
{
conf: "include: master",
with: "develop",
want: false,
},
{
conf: "include: master",
with: "master",
want: true,
},
{
conf: "include: feature/*",
with: "master",
want: false,
},
{
conf: "include: feature/*",
with: "feature/foo",
want: true,
},
{
conf: "include: [ master, feature/* ]",
with: "develop",
want: false,
},
{
conf: "include: [ master, feature/* ]",
with: "master",
want: true,
},
{
conf: "include: [ master, feature/* ]",
with: "feature/foo",
want: true,
},
// excludes block
{
conf: "exclude: master",
with: "develop",
want: true,
},
{
conf: "exclude: master",
with: "master",
want: false,
},
{
conf: "exclude: feature/*",
with: "master",
want: true,
},
{
conf: "exclude: feature/*",
with: "feature/foo",
want: false,
},
{
conf: "exclude: [ master, develop ]",
with: "master",
want: false,
},
{
conf: "exclude: [ feature/*, bar ]",
with: "master",
want: true,
},
{
conf: "exclude: [ feature/*, bar ]",
with: "feature/foo",
want: false,
},
// include and exclude blocks
{
conf: "{ include: [ master, feature/* ], exclude: [ develop ] }",
with: "master",
want: true,
},
{
conf: "{ include: [ master, feature/* ], exclude: [ feature/bar ] }",
with: "feature/bar",
want: false,
},
{
conf: "{ include: [ master, feature/* ], exclude: [ master, develop ] }",
with: "master",
want: false,
},
// empty blocks
{
conf: "",
with: "master",
want: true,
},
}
for _, test := range testdata {
c := parseConstraint(test.conf)
got, want := c.Match(test.with), test.want
if got != want {
t.Errorf("Expect %q matches %q is %v", test.with, test.conf, want)
}
}
}
func TestConstraintMap(t *testing.T) {
testdata := []struct {
conf string
with map[string]string
want bool
}{
{
conf: "GOLANG: 1.7",
with: map[string]string{"GOLANG": "1.7"},
want: true,
},
{
conf: "GOLANG: tip",
with: map[string]string{"GOLANG": "1.7"},
want: false,
},
{
conf: "{ GOLANG: 1.7, REDIS: 3.1 }",
with: map[string]string{"GOLANG": "1.7", "REDIS": "3.1", "MYSQL": "5.6"},
want: true,
},
{
conf: "{ GOLANG: 1.7, REDIS: 3.1 }",
with: map[string]string{"GOLANG": "1.7", "REDIS": "3.0"},
want: false,
},
// TODO(bradrydzewski) eventually we should enable wildcard matching
{
conf: "{ GOLANG: 1.7, REDIS: 3.* }",
with: map[string]string{"GOLANG": "1.7", "REDIS": "3.0"},
want: false,
},
// include syntax
{
conf: "include: { GOLANG: 1.7 }",
with: map[string]string{"GOLANG": "1.7"},
want: true,
},
{
conf: "include: { GOLANG: tip }",
with: map[string]string{"GOLANG": "1.7"},
want: false,
},
{
conf: "include: { GOLANG: 1.7, REDIS: 3.1 }",
with: map[string]string{"GOLANG": "1.7", "REDIS": "3.1", "MYSQL": "5.6"},
want: true,
},
{
conf: "include: { GOLANG: 1.7, REDIS: 3.1 }",
with: map[string]string{"GOLANG": "1.7", "REDIS": "3.0"},
want: false,
},
// exclude syntax
{
conf: "exclude: { GOLANG: 1.7 }",
with: map[string]string{"GOLANG": "1.7"},
want: false,
},
{
conf: "exclude: { GOLANG: tip }",
with: map[string]string{"GOLANG": "1.7"},
want: true,
},
{
conf: "exclude: { GOLANG: 1.7, REDIS: 3.1 }",
with: map[string]string{"GOLANG": "1.7", "REDIS": "3.1", "MYSQL": "5.6"},
want: false,
},
{
conf: "exclude: { GOLANG: 1.7, REDIS: 3.1 }",
with: map[string]string{"GOLANG": "1.7", "REDIS": "3.0"},
want: true,
},
// exclude AND include values
{
conf: "{ include: { GOLANG: 1.7 }, exclude: { GOLANG: 1.7 } }",
with: map[string]string{"GOLANG": "1.7"},
want: false,
},
// blanks
{
conf: "",
with: map[string]string{"GOLANG": "1.7", "REDIS": "3.0"},
want: true,
},
{
conf: "GOLANG: 1.7",
with: map[string]string{},
want: false,
},
{
conf: "{ GOLANG: 1.7, REDIS: 3.0 }",
with: map[string]string{},
want: false,
},
{
conf: "include: { GOLANG: 1.7, REDIS: 3.1 }",
with: map[string]string{},
want: false,
},
{
conf: "exclude: { GOLANG: 1.7, REDIS: 3.1 }",
with: map[string]string{},
want: true,
},
}
for _, test := range testdata {
c := parseConstraintMap(test.conf)
got, want := c.Match(test.with), test.want
if got != want {
t.Errorf("Expect %q matches %q is %v", test.with, test.conf, want)
}
}
}
func TestConstraints(t *testing.T) {
testdata := []struct {
conf string
with frontend.Metadata
want bool
}{
// no constraints, must match
{
conf: "",
with: frontend.Metadata{},
want: true,
},
// branch constraint
{
conf: "{ branch: develop }",
with: frontend.Metadata{Curr: frontend.Build{Commit: frontend.Commit{Branch: "master"}}},
want: false,
},
{
conf: "{ branch: master }",
with: frontend.Metadata{Curr: frontend.Build{Commit: frontend.Commit{Branch: "master"}}},
want: true,
},
// environment constraint
// {
// conf: "{ branch: develop }",
// with: frontend.Metadata{Curr: frontend.Build{Commit: frontend.Commit{Branch: "master"}}},
// want: false,
// },
// {
// conf: "{ branch: master }",
// with: frontend.Metadata{Curr: frontend.Build{Commit: frontend.Commit{Branch: "master"}}},
// want: true,
// },
// repo constraint
{
conf: "{ repo: drone/* }",
with: frontend.Metadata{Repo: frontend.Repo{Name: "drone/drone"}},
want: true,
},
{
conf: "{ repo: octocat/* }",
with: frontend.Metadata{Repo: frontend.Repo{Name: "drone/drone"}},
want: false,
},
// ref constraint
{
conf: "{ ref: refs/tags/* }",
with: frontend.Metadata{Curr: frontend.Build{Commit: frontend.Commit{Ref: "refs/tags/v1.0.0"}}},
want: true,
},
{
conf: "{ ref: refs/tags/* }",
with: frontend.Metadata{Curr: frontend.Build{Commit: frontend.Commit{Ref: "refs/heads/master"}}},
want: false,
},
// platform constraint
{
conf: "{ platform: linux/amd64 }",
with: frontend.Metadata{Sys: frontend.System{Arch: "linux/amd64"}},
want: true,
},
{
conf: "{ repo: linux/amd64 }",
with: frontend.Metadata{Sys: frontend.System{Arch: "windows/amd64"}},
want: false,
},
// instance constraint
{
conf: "{ instance: drone.io }",
with: frontend.Metadata{Sys: frontend.System{Host: "drone.io"}},
want: true,
},
{
conf: "{ instance: drone.io }",
with: frontend.Metadata{Sys: frontend.System{Host: "beta.drone.io"}},
want: false,
},
}
for _, test := range testdata {
c := parseConstraints(test.conf)
got, want := c.Match(test.with), test.want
if got != want {
t.Errorf("Expect %+v matches %q is %v", test.with, test.conf, want)
}
}
}
func parseConstraints(s string) *Constraints {
c := &Constraints{}
yaml.Unmarshal([]byte(s), c)
return c
}
func parseConstraint(s string) *Constraint {
c := &Constraint{}
yaml.Unmarshal([]byte(s), c)
return c
}
func parseConstraintMap(s string) *ConstraintMap {
c := &ConstraintMap{}
yaml.Unmarshal([]byte(s), c)
return c
}

View file

@ -0,0 +1,187 @@
package yaml
import (
"reflect"
"testing"
libcompose "github.com/docker/libcompose/yaml"
"github.com/kr/pretty"
"gopkg.in/yaml.v2"
)
var containerYaml = []byte(`
image: golang:latest
auth_config:
username: janedoe
password: password
cap_add: [ ALL ]
cap_drop: [ NET_ADMIN, SYS_ADMIN ]
command: bundle exec thin -p 3000
commands:
- go build
- go test
cpu_quota: 11
cpuset: 1,2
cpu_shares: 99
detach: true
devices:
- /dev/ttyUSB0:/dev/ttyUSB0
dns: 8.8.8.8
dns_search: example.com
entrypoint: /code/entrypoint.sh
environment:
- RACK_ENV=development
- SHOW=true
extra_hosts:
- somehost:162.242.195.82
- otherhost:50.31.209.229
isolation: hyperv
name: my-build-container
network_mode: bridge
networks:
- some-network
- other-network
pull: true
privileged: true
labels:
com.example.type: build
com.example.team: frontend
shm_size: 1kb
mem_limit: 1kb
memswap_limit: 1kb
mem_swappiness: 1kb
volumes:
- /var/lib/mysql
- /opt/data:/var/lib/mysql
- /etc/configs:/etc/configs/:ro
ulimits:
nofile:
soft: 20000
hard: 40000
tmpfs:
- /var/lib/test
when:
branch: master
`)
func TestUnmarshalContainer(t *testing.T) {
want := Container{
AuthConfig: AuthConfig{
Username: "janedoe",
Password: "password",
},
CapAdd: []string{"ALL"},
CapDrop: []string{"NET_ADMIN", "SYS_ADMIN"},
Command: libcompose.Command{"bundle", "exec", "thin", "-p", "3000"},
Commands: libcompose.Stringorslice{"go build", "go test"},
CPUQuota: libcompose.StringorInt(11),
CPUSet: "1,2",
CPUShares: libcompose.StringorInt(99),
Detached: true,
Devices: []string{"/dev/ttyUSB0:/dev/ttyUSB0"},
DNS: libcompose.Stringorslice{"8.8.8.8"},
DNSSearch: libcompose.Stringorslice{"example.com"},
Entrypoint: libcompose.Command{"/code/entrypoint.sh"},
Environment: libcompose.SliceorMap{"RACK_ENV": "development", "SHOW": "true"},
ExtraHosts: []string{"somehost:162.242.195.82", "otherhost:50.31.209.229"},
Image: "golang:latest",
Isolation: "hyperv",
Labels: libcompose.SliceorMap{"com.example.type": "build", "com.example.team": "frontend"},
MemLimit: libcompose.MemStringorInt(1024),
MemSwapLimit: libcompose.MemStringorInt(1024),
MemSwappiness: libcompose.MemStringorInt(1024),
Name: "my-build-container",
Networks: libcompose.Networks{
Networks: []*libcompose.Network{
{Name: "some-network"},
{Name: "other-network"},
},
},
NetworkMode: "bridge",
Pull: true,
Privileged: true,
ShmSize: libcompose.MemStringorInt(1024),
Tmpfs: libcompose.Stringorslice{"/var/lib/test"},
Ulimits: libcompose.Ulimits{
Elements: []libcompose.Ulimit{
libcompose.NewUlimit("nofile", 20000, 40000),
},
},
Volumes: libcompose.Volumes{
Volumes: []*libcompose.Volume{
{Source: "", Destination: "/var/lib/mysql"},
{Source: "/opt/data", Destination: "/var/lib/mysql"},
{Source: "/etc/configs", Destination: "/etc/configs/", AccessMode: "ro"},
},
},
Constraints: Constraints{
Branch: Constraint{
Include: []string{"master"},
},
},
}
got := Container{}
err := yaml.Unmarshal(containerYaml, &got)
if err != nil {
t.Error(err)
} else if !reflect.DeepEqual(want, got) {
t.Errorf("problem parsing container")
pretty.Ldiff(t, want, got)
}
}
// TestUnmarshalContainersErr unmarshals a map of containers. The order is
// retained and the container key may be used as the container name if a
// name is not explicitly provided.
func TestUnmarshalContainers(t *testing.T) {
testdata := []struct {
from string
want []*Container
}{
{
from: "build: { image: golang }",
want: []*Container{
{
Name: "build",
Image: "golang",
},
},
},
{
from: "test: { name: unit_test, image: node }",
want: []*Container{
{
Name: "unit_test",
Image: "node",
},
},
},
}
for _, test := range testdata {
in := []byte(test.from)
got := Containers{}
err := yaml.Unmarshal(in, &got)
if err != nil {
t.Error(err)
} else if !reflect.DeepEqual(test.want, got.Containers) {
t.Errorf("problem parsing containers %q", test.from)
pretty.Ldiff(t, test.want, got.Containers)
}
}
}
// TestUnmarshalContainersErr unmarshals a container map where invalid inputs
// are provided to verify error messages are returned.
func TestUnmarshalContainersErr(t *testing.T) {
testdata := []string{
"foo: { name: [ foo, bar] }",
"- foo",
}
for _, test := range testdata {
in := []byte(test)
err := yaml.Unmarshal(in, new(Containers))
if err == nil {
t.Errorf("wanted error for containers %q", test)
}
}
}

View file

@ -3,7 +3,7 @@ package linter
import (
"fmt"
"github.com/cncd/pipeline/pipeline/frontend/yaml"
"github.com/laszlocph/drone-oss-08/cncd/pipeline/pipeline/frontend/yaml"
)
const (

View file

@ -0,0 +1,131 @@
package linter
import (
"testing"
"github.com/laszlocph/drone-oss-08/cncd/pipeline/pipeline/frontend/yaml"
)
func TestLint(t *testing.T) {
testdata := `
pipeline:
build:
image: docker
privileged: true
network_mode: host
volumes:
- /tmp:/tmp
commands:
- go build
- go test
publish:
image: plugins/docker
repo: foo/bar
services:
redis:
image: redis
entrypoint: [ /bin/redis-server ]
command: [ -v ]
`
conf, err := yaml.ParseString(testdata)
if err != nil {
t.Fatalf("Cannot unmarshal yaml %q. Error: %s", testdata, err)
}
if err := New(WithTrusted(true)).Lint(conf); err != nil {
t.Errorf("Expected lint returns no errors, got %q", err)
}
}
func TestLintErrors(t *testing.T) {
testdata := []struct {
from string
want string
}{
{
from: "",
want: "Invalid or missing pipeline section",
},
{
from: "pipeline: { build: { image: '' } }",
want: "Invalid or missing image",
},
{
from: "pipeline: { build: { image: golang, privileged: true } }",
want: "Insufficient privileges to use privileged mode",
},
{
from: "pipeline: { build: { image: golang, shm_size: 10gb } }",
want: "Insufficient privileges to override shm_size",
},
{
from: "pipeline: { build: { image: golang, dns: [ 8.8.8.8 ] } }",
want: "Insufficient privileges to use custom dns",
},
{
from: "pipeline: { build: { image: golang, dns_search: [ example.com ] } }",
want: "Insufficient privileges to use dns_search",
},
{
from: "pipeline: { build: { image: golang, devices: [ '/dev/tty0:/dev/tty0' ] } }",
want: "Insufficient privileges to use devices",
},
{
from: "pipeline: { build: { image: golang, extra_hosts: [ 'somehost:162.242.195.82' ] } }",
want: "Insufficient privileges to use extra_hosts",
},
{
from: "pipeline: { build: { image: golang, network_mode: host } }",
want: "Insufficient privileges to use network_mode",
},
{
from: "pipeline: { build: { image: golang, networks: [ outside, default ] } }",
want: "Insufficient privileges to use networks",
},
{
from: "pipeline: { build: { image: golang, volumes: [ '/opt/data:/var/lib/mysql' ] } }",
want: "Insufficient privileges to use volumes",
},
{
from: "pipeline: { build: { image: golang, network_mode: 'container:name' } }",
want: "Insufficient privileges to use network_mode",
},
{
from: "pipeline: { build: { image: golang, sysctls: [ net.core.somaxconn=1024 ] } }",
want: "Insufficient privileges to use sysctls",
},
// cannot override entypoint, command for script steps
{
from: "pipeline: { build: { image: golang, commands: [ 'go build' ], entrypoint: [ '/bin/bash' ] } }",
want: "Cannot override container entrypoint",
},
{
from: "pipeline: { build: { image: golang, commands: [ 'go build' ], command: [ '/bin/bash' ] } }",
want: "Cannot override container command",
},
// cannot override entypoint, command for plugin steps
{
from: "pipeline: { publish: { image: plugins/docker, repo: foo/bar, entrypoint: [ '/bin/bash' ] } }",
want: "Cannot override container entrypoint",
},
{
from: "pipeline: { publish: { image: plugins/docker, repo: foo/bar, command: [ '/bin/bash' ] } }",
want: "Cannot override container command",
},
}
for _, test := range testdata {
conf, err := yaml.ParseString(test.from)
if err != nil {
t.Fatalf("Cannot unmarshal yaml %q. Error: %s", test.from, err)
}
lerr := New().Lint(conf)
if lerr == nil {
t.Errorf("Expected lint error for configuration %q", test.from)
} else if lerr.Error() != test.want {
t.Errorf("Want error %q, got %q", test.want, lerr.Error())
}
}
}

View file

@ -0,0 +1,70 @@
package matrix
import (
"testing"
"github.com/franela/goblin"
)
func TestMatrix(t *testing.T) {
g := goblin.Goblin(t)
g.Describe("Calculate matrix", func() {
axis, _ := ParseString(fakeMatrix)
g.It("Should calculate permutations", func() {
g.Assert(len(axis)).Equal(24)
})
g.It("Should not duplicate permutations", func() {
set := map[string]bool{}
for _, perm := range axis {
set[perm.String()] = true
}
g.Assert(len(set)).Equal(24)
})
g.It("Should return nil if no matrix", func() {
axis, err := ParseString("")
g.Assert(err == nil).IsTrue()
g.Assert(axis == nil).IsTrue()
})
g.It("Should return included axis", func() {
axis, err := ParseString(fakeMatrixInclude)
g.Assert(err == nil).IsTrue()
g.Assert(len(axis)).Equal(2)
g.Assert(axis[0]["go_version"]).Equal("1.5")
g.Assert(axis[1]["go_version"]).Equal("1.6")
g.Assert(axis[0]["python_version"]).Equal("3.4")
g.Assert(axis[1]["python_version"]).Equal("3.4")
})
})
}
var fakeMatrix = `
matrix:
go_version:
- go1
- go1.2
python_version:
- 3.2
- 3.3
django_version:
- 1.7
- 1.7.1
- 1.7.2
redis_version:
- 2.6
- 2.8
`
var fakeMatrixInclude = `
matrix:
include:
- go_version: 1.5
python_version: 3.4
- go_version: 1.6
python_version: 3.4
`

View file

@ -0,0 +1,108 @@
package yaml
import (
"reflect"
"testing"
"github.com/kr/pretty"
"gopkg.in/yaml.v2"
)
func TestUnmarshalNetwork(t *testing.T) {
testdata := []struct {
from string
want Network
}{
{
from: "{ name: foo, driver: bar }",
want: Network{
Name: "foo",
Driver: "bar",
},
},
{
from: "{ name: foo, driver: bar, driver_opts: { baz: qux } }",
want: Network{
Name: "foo",
Driver: "bar",
DriverOpts: map[string]string{
"baz": "qux",
},
},
},
}
for _, test := range testdata {
in := []byte(test.from)
got := Network{}
err := yaml.Unmarshal(in, &got)
if err != nil {
t.Error(err)
} else if !reflect.DeepEqual(test.want, got) {
t.Errorf("problem parsing network %q", test.from)
pretty.Ldiff(t, test.want, got)
}
}
}
func TestUnmarshalNetworks(t *testing.T) {
testdata := []struct {
from string
want []*Network
}{
{
from: "foo: { driver: bar }",
want: []*Network{
{
Name: "foo",
Driver: "bar",
},
},
},
{
from: "foo: { name: baz }",
want: []*Network{
{
Name: "baz",
Driver: "bridge",
},
},
},
{
from: "foo: { name: baz, driver: bar }",
want: []*Network{
{
Name: "baz",
Driver: "bar",
},
},
},
}
for _, test := range testdata {
in := []byte(test.from)
got := Networks{}
err := yaml.Unmarshal(in, &got)
if err != nil {
t.Error(err)
} else if !reflect.DeepEqual(test.want, got.Networks) {
t.Errorf("problem parsing network %q", test.from)
pretty.Ldiff(t, test.want, got.Networks)
}
}
}
func TestUnmarshalNetworkErr(t *testing.T) {
testdata := []string{
"foo: { name: [ foo, bar] }",
"- foo",
}
for _, test := range testdata {
in := []byte(test)
err := yaml.Unmarshal(in, new(Networks))
if err == nil {
t.Errorf("wanted error for networks %q", test)
}
}
}

View file

@ -0,0 +1,64 @@
package yaml
import (
"reflect"
"testing"
"github.com/kr/pretty"
"gopkg.in/yaml.v2"
)
func TestUnmarshalSecrets(t *testing.T) {
testdata := []struct {
from string
want []*Secret
}{
{
from: "[ mysql_username, mysql_password]",
want: []*Secret{
{
Source: "mysql_username",
Target: "mysql_username",
},
{
Source: "mysql_password",
Target: "mysql_password",
},
},
},
{
from: "[ { source: mysql_prod_username, target: mysql_username } ]",
want: []*Secret{
{
Source: "mysql_prod_username",
Target: "mysql_username",
},
},
},
{
from: "[ { source: mysql_prod_username, target: mysql_username }, { source: redis_username, target: redis_username } ]",
want: []*Secret{
{
Source: "mysql_prod_username",
Target: "mysql_username",
},
{
Source: "redis_username",
Target: "redis_username",
},
},
},
}
for _, test := range testdata {
in := []byte(test.from)
got := Secrets{}
err := yaml.Unmarshal(in, &got)
if err != nil {
t.Error(err)
} else if !reflect.DeepEqual(test.want, got.Secrets) {
t.Errorf("problem parsing secrets %q", test.from)
pretty.Ldiff(t, test.want, got.Secrets)
}
}
}

View file

@ -0,0 +1,54 @@
package types
import (
"testing"
"github.com/franela/goblin"
"gopkg.in/yaml.v2"
)
func TestBoolTrue(t *testing.T) {
g := goblin.Goblin(t)
g.Describe("Yaml bool type", func() {
g.Describe("given a yaml file", func() {
g.It("should unmarshal true", func() {
in := []byte("true")
out := BoolTrue{}
err := yaml.Unmarshal(in, &out)
if err != nil {
g.Fail(err)
}
g.Assert(out.Bool()).Equal(true)
})
g.It("should unmarshal false", func() {
in := []byte("false")
out := BoolTrue{}
err := yaml.Unmarshal(in, &out)
if err != nil {
g.Fail(err)
}
g.Assert(out.Bool()).Equal(false)
})
g.It("should unmarshal true when empty", func() {
in := []byte("")
out := BoolTrue{}
err := yaml.Unmarshal(in, &out)
if err != nil {
g.Fail(err)
}
g.Assert(out.Bool()).Equal(true)
})
g.It("should throw error when invalid", func() {
in := []byte("{ }") // string value should fail parse
out := BoolTrue{}
err := yaml.Unmarshal(in, &out)
g.Assert(err != nil).IsTrue("expects error")
})
})
})
}

View file

@ -0,0 +1,108 @@
package yaml
import (
"reflect"
"testing"
"github.com/kr/pretty"
"gopkg.in/yaml.v2"
)
func TestUnmarshalVolume(t *testing.T) {
testdata := []struct {
from string
want Volume
}{
{
from: "{ name: foo, driver: bar }",
want: Volume{
Name: "foo",
Driver: "bar",
},
},
{
from: "{ name: foo, driver: bar, driver_opts: { baz: qux } }",
want: Volume{
Name: "foo",
Driver: "bar",
DriverOpts: map[string]string{
"baz": "qux",
},
},
},
}
for _, test := range testdata {
in := []byte(test.from)
got := Volume{}
err := yaml.Unmarshal(in, &got)
if err != nil {
t.Error(err)
} else if !reflect.DeepEqual(test.want, got) {
t.Errorf("problem parsing volume %q", test.from)
pretty.Ldiff(t, test.want, got)
}
}
}
func TestUnmarshalVolumes(t *testing.T) {
testdata := []struct {
from string
want []*Volume
}{
{
from: "foo: { driver: bar }",
want: []*Volume{
{
Name: "foo",
Driver: "bar",
},
},
},
{
from: "foo: { name: baz }",
want: []*Volume{
{
Name: "baz",
Driver: "local",
},
},
},
{
from: "foo: { name: baz, driver: bar }",
want: []*Volume{
{
Name: "baz",
Driver: "bar",
},
},
},
}
for _, test := range testdata {
in := []byte(test.from)
got := Volumes{}
err := yaml.Unmarshal(in, &got)
if err != nil {
t.Error(err)
} else if !reflect.DeepEqual(test.want, got.Volumes) {
t.Errorf("problem parsing volumes %q", test.from)
pretty.Ldiff(t, test.want, got.Volumes)
}
}
}
func TestUnmarshalVolumesErr(t *testing.T) {
testdata := []string{
"foo: { name: [ foo, bar] }",
"- foo",
}
for _, test := range testdata {
in := []byte(test)
err := yaml.Unmarshal(in, new(Volumes))
if err == nil {
t.Errorf("wanted error for volumes %q", test)
}
}
}

View file

@ -1,8 +1,8 @@
package pipeline
import (
"github.com/cncd/pipeline/pipeline/backend"
"github.com/cncd/pipeline/pipeline/multipart"
"github.com/laszlocph/drone-oss-08/cncd/pipeline/pipeline/backend"
"github.com/laszlocph/drone-oss-08/cncd/pipeline/pipeline/multipart"
)
// Logger handles the process logging.

View file

@ -0,0 +1 @@
package analysis

View file

@ -0,0 +1,58 @@
package coverage
import (
"encoding/json"
"fmt"
"strconv"
"mime/multipart"
"net/textproto"
)
// MimeType used by coverage reports.
const MimeType = "application/json+coverage"
type (
// Report represents a coverage report.
Report struct {
Timestamp int64 `json:"timestmp,omitempty"`
Command string `json:"command_name,omitempty"`
Files []File `json:"files"`
Metrics Metrics `json:"metrics"`
}
// File represents a coverage report for a single file.
File struct {
Name string `json:"filename"`
Digest string `json:"checksum,omitempty"`
Coverage []*int `json:"coverage"`
Covered float64 `json:"covered_percent,omitempty"`
CoveredStrength float64 `json:"covered_strength,omitempty"`
CoveredLines int `json:"covered_lines,omitempty"`
TotalLines int `json:"lines_of_code"`
}
// Metrics represents total coverage metrics for all files.
Metrics struct {
Covered float64 `json:"covered_percent"`
CoveredStrength float64 `json:"covered_strength"`
CoveredLines int `json:"covered_lines"`
TotalLines int `json:"total_lines"`
}
)
// WriteTo writes the report to multipart.Writer w.
func (r *Report) WriteTo(w *multipart.Writer) error {
header := textproto.MIMEHeader{}
header.Set("Content-Type", MimeType)
header.Set("X-Covered", fmt.Sprintf("%.2f", r.Metrics.Covered))
header.Set("X-Covered-Lines", strconv.Itoa(r.Metrics.CoveredLines))
header.Set("X-Total-Lines", strconv.Itoa(r.Metrics.TotalLines))
part, err := w.CreatePart(header)
if err != nil {
return err
}
encoder := json.NewEncoder(part)
encoder.SetIndent("", " ")
return encoder.Encode(r)
}

View file

@ -0,0 +1 @@
package coverage

View file

@ -0,0 +1,76 @@
package multipart
import (
"bytes"
"io/ioutil"
"testing"
)
func TestReader(t *testing.T) {
b := bytes.NewBufferString(sample)
m := New(b)
part, err := m.NextPart()
if err != nil {
t.Error(err)
return
}
header := part.Header()
if got, want := header.Get("Content-Type"), "text/plain"; got != want {
t.Errorf("Want Content-Type %q, got %q", want, got)
}
body, err := ioutil.ReadAll(part)
if err != nil {
t.Error(err)
return
}
if got, want := string(body), sampleTextPlain; got != want {
t.Errorf("Want body %q, got %q", want, got)
}
part, err = m.NextPart()
if err != nil {
t.Error(err)
return
}
header = part.Header()
if got, want := header.Get("Content-Type"), "application/json+coverage"; got != want {
t.Errorf("Want Content-Type %q, got %q", want, got)
}
if got, want := header.Get("X-Covered"), "96.00"; got != want {
t.Errorf("Want X-Covered %q, got %q", want, got)
}
if got, want := header.Get("X-Covered-Lines"), "96"; got != want {
t.Errorf("Want X-Covered-Lines %q, got %q", want, got)
}
if got, want := header.Get("X-Total-Lines"), "100"; got != want {
t.Errorf("Want X-Total-Lines %q, got %q", want, got)
}
}
var sample = `PIPELINE
Content-Type: multipart/mixed; boundary=boundary
--boundary
Content-Type: text/plain
match: pipeline/frontend/yaml/compiler/coverage.out
match: pipeline/frontend/yaml/coverage.out
match: pipeline/frontend/yaml/linter/coverage.out
--boundary
Content-Type: application/json+coverage
X-Covered: 96.00
X-Covered-Lines: 96
X-Total-Lines: 100
{"metrics":{"covered_lines":96,"total_lines":100}}
--boundary--
`
var sampleTextPlain = `match: pipeline/frontend/yaml/compiler/coverage.out
match: pipeline/frontend/yaml/coverage.out
match: pipeline/frontend/yaml/linter/coverage.out
`

View file

@ -0,0 +1 @@
package selenium

View file

@ -0,0 +1 @@
package terminal

View file

@ -3,7 +3,7 @@ package pipeline
import (
"context"
"github.com/cncd/pipeline/pipeline/backend"
"github.com/laszlocph/drone-oss-08/cncd/pipeline/pipeline/backend"
)
// Option configures a runtime option.

View file

@ -0,0 +1 @@
package pipeline

View file

@ -6,7 +6,7 @@ import (
"os"
"strings"
"github.com/cncd/pipeline/pipeline/backend"
"github.com/laszlocph/drone-oss-08/cncd/pipeline/pipeline/backend"
)
// Parse parses the pipeline config from an io.Reader.

View file

@ -0,0 +1 @@
package pipeline

View file

@ -6,8 +6,8 @@ import (
"golang.org/x/sync/errgroup"
"github.com/cncd/pipeline/pipeline/backend"
"github.com/cncd/pipeline/pipeline/multipart"
"github.com/laszlocph/drone-oss-08/cncd/pipeline/pipeline/backend"
"github.com/laszlocph/drone-oss-08/cncd/pipeline/pipeline/multipart"
)
type (

View file

@ -6,8 +6,8 @@ import (
"time"
"log"
"github.com/cncd/pipeline/pipeline/backend"
"github.com/cncd/pipeline/pipeline/rpc/proto"
"github.com/laszlocph/drone-oss-08/cncd/pipeline/pipeline/backend"
"github.com/laszlocph/drone-oss-08/cncd/pipeline/pipeline/rpc/proto"
"google.golang.org/grpc"
"google.golang.org/grpc/codes"

View file

@ -5,8 +5,8 @@ import (
// "encoding/json"
"time"
// "github.com/cncd/pipeline/pipeline/backend"
"github.com/cncd/pipeline/pipeline/rpc/proto"
// "github.com/laszlocph/drone-oss-08/cncd/pipeline/pipeline/backend"
"github.com/laszlocph/drone-oss-08/cncd/pipeline/pipeline/rpc/proto"
"google.golang.org/grpc"
"google.golang.org/grpc/codes"

View file

@ -0,0 +1 @@
package rpc

View file

@ -0,0 +1,18 @@
package rpc
import (
"testing"
)
func TestLine(t *testing.T) {
line := Line{
Proc: "redis",
Time: 60,
Pos: 1,
Out: "starting redis server",
}
got, want := line.String(), "[redis:L1:60s] starting redis server"
if got != want {
t.Errorf("Wanted line string %q, got %q", want, got)
}
}

View file

@ -0,0 +1 @@
package rpc

View file

@ -3,7 +3,7 @@ package rpc
import (
"context"
"github.com/cncd/pipeline/pipeline/backend"
"github.com/laszlocph/drone-oss-08/cncd/pipeline/pipeline/backend"
)
// ErrCancelled signals the pipeine is cancelled.

View file

@ -0,0 +1 @@
package rpc

View file

@ -0,0 +1,11 @@
Compile the yaml to the intermediate representation:
```
pipec compile
```
Execute the intermediate representation:
```
pipec exec
```

View file

@ -0,0 +1,159 @@
{
"pipeline": [
{
"name": "pipeline_clone_0",
"alias": "git",
"steps": [
{
"name": "pipeline_clone_0",
"alias": "git",
"image": "plugins/git:latest",
"working_dir": "/go/src/github.com/drone/envsubst",
"environment": {
"CI": "pipec",
"CI_BUILD_CREATED": "1486119586",
"CI_BUILD_EVENT": "push",
"CI_BUILD_NUMBER": "6",
"CI_BUILD_STARTED": "1486119585",
"CI_COMMIT_AUTHOR": "bradrydzewski",
"CI_COMMIT_AUTHOR_NAME": "bradrydzewski",
"CI_COMMIT_BRANCH": "master",
"CI_COMMIT_MESSAGE": "added a few more test cases for escaping behavior",
"CI_COMMIT_REF": "refs/heads/master",
"CI_COMMIT_SHA": "d0876d3176965f9552a611cbd56e24a9264355e6",
"CI_REMOTE_URL": "https://github.com/drone/envsubst.git",
"CI_REPO": "drone/envsubst",
"CI_REPO_LINK": "https://github.com/drone/envsubst",
"CI_REPO_NAME": "drone/envsubst",
"CI_REPO_REMOTE": "https://github.com/drone/envsubst.git",
"CI_SYSTEM": "pipec",
"CI_SYSTEM_ARCH": "linux/amd64",
"CI_SYSTEM_LINK": "https://github.com/cncd/pipec",
"CI_SYSTEM_NAME": "pipec",
"CI_WORKSPACE": "/go/src/github.com/drone/envsubst",
"DRONE_BUILD_CREATED": "1486119586",
"DRONE_BUILD_EVENT": "push",
"DRONE_BUILD_NUMBER": "6",
"DRONE_BUILD_STARTED": "1486119585",
"DRONE_COMMIT_AUTHOR": "bradrydzewski",
"DRONE_COMMIT_AUTHOR_NAME": "bradrydzewski",
"DRONE_COMMIT_BRANCH": "master",
"DRONE_COMMIT_MESSAGE": "added a few more test cases for escaping behavior",
"DRONE_COMMIT_REF": "refs/heads/master",
"DRONE_COMMIT_SHA": "d0876d3176965f9552a611cbd56e24a9264355e6",
"DRONE_REMOTE_URL": "https://github.com/drone/envsubst.git",
"DRONE_REPO": "drone/envsubst",
"DRONE_REPO_LINK": "https://github.com/drone/envsubst",
"DRONE_REPO_NAME": "drone/envsubst",
"DRONE_REPO_REMOTE": "https://github.com/drone/envsubst.git",
"DRONE_SYSTEM": "pipec",
"DRONE_SYSTEM_ARCH": "linux/amd64",
"DRONE_SYSTEM_LINK": "https://github.com/cncd/pipec",
"DRONE_SYSTEM_NAME": "pipec",
"DRONE_WORKSPACE": "/go/src/github.com/drone/envsubst",
"PLUGIN_DEPTH": "50"
},
"volumes": [
"pipeline_default:/go"
],
"networks": [
{
"name": "pipeline_default",
"aliases": null
}
],
"on_success": true,
"auth_config": {}
}
]
},
{
"name": "pipeline_stage_0",
"alias": "build",
"steps": [
{
"name": "pipeline_step_0",
"alias": "build",
"image": "golang:1.7",
"working_dir": "/go/src/github.com/drone/envsubst",
"environment": {
"CI": "pipec",
"CI_BUILD_CREATED": "1486119586",
"CI_BUILD_EVENT": "push",
"CI_BUILD_NUMBER": "6",
"CI_BUILD_STARTED": "1486119585",
"CI_COMMIT_AUTHOR": "bradrydzewski",
"CI_COMMIT_AUTHOR_NAME": "bradrydzewski",
"CI_COMMIT_BRANCH": "master",
"CI_COMMIT_MESSAGE": "added a few more test cases for escaping behavior",
"CI_COMMIT_REF": "refs/heads/master",
"CI_COMMIT_SHA": "d0876d3176965f9552a611cbd56e24a9264355e6",
"CI_REMOTE_URL": "https://github.com/drone/envsubst.git",
"CI_REPO": "drone/envsubst",
"CI_REPO_LINK": "https://github.com/drone/envsubst",
"CI_REPO_NAME": "drone/envsubst",
"CI_REPO_REMOTE": "https://github.com/drone/envsubst.git",
"CI_SCRIPT": "CmlmIFsgLW4gIiRDSV9ORVRSQ19NQUNISU5FIiBdOyB0aGVuCmNhdCA8PEVPRiA+ICRIT01FLy5uZXRyYwptYWNoaW5lICRDSV9ORVRSQ19NQUNISU5FCmxvZ2luICRDSV9ORVRSQ19VU0VSTkFNRQpwYXNzd29yZCAkQ0lfTkVUUkNfUEFTU1dPUkQKRU9GCmNobW9kIDA2MDAgJEhPTUUvLm5ldHJjCmZpCnVuc2V0IENJX05FVFJDX1VTRVJOQU1FCnVuc2V0IENJX05FVFJDX1BBU1NXT1JECnVuc2V0IENJX1NDUklQVAoKZWNobyArICJnbyBnZXQgLXQgLi8uLi4iCmdvIGdldCAtdCAuLy4uLgoKZWNobyArICJnbyBidWlsZCIKZ28gYnVpbGQKCmVjaG8gKyAiZ28gdGVzdCAtdiIKZ28gdGVzdCAtdgoK",
"CI_SYSTEM": "pipec",
"CI_SYSTEM_ARCH": "linux/amd64",
"CI_SYSTEM_LINK": "https://github.com/cncd/pipec",
"CI_SYSTEM_NAME": "pipec",
"CI_WORKSPACE": "/go/src/github.com/drone/envsubst",
"DRONE_BUILD_CREATED": "1486119586",
"DRONE_BUILD_EVENT": "push",
"DRONE_BUILD_NUMBER": "6",
"DRONE_BUILD_STARTED": "1486119585",
"DRONE_COMMIT_AUTHOR": "bradrydzewski",
"DRONE_COMMIT_AUTHOR_NAME": "bradrydzewski",
"DRONE_COMMIT_BRANCH": "master",
"DRONE_COMMIT_MESSAGE": "added a few more test cases for escaping behavior",
"DRONE_COMMIT_REF": "refs/heads/master",
"DRONE_COMMIT_SHA": "d0876d3176965f9552a611cbd56e24a9264355e6",
"DRONE_REMOTE_URL": "https://github.com/drone/envsubst.git",
"DRONE_REPO": "drone/envsubst",
"DRONE_REPO_LINK": "https://github.com/drone/envsubst",
"DRONE_REPO_NAME": "drone/envsubst",
"DRONE_REPO_REMOTE": "https://github.com/drone/envsubst.git",
"DRONE_SYSTEM": "pipec",
"DRONE_SYSTEM_ARCH": "linux/amd64",
"DRONE_SYSTEM_LINK": "https://github.com/cncd/pipec",
"DRONE_SYSTEM_NAME": "pipec",
"DRONE_WORKSPACE": "/go/src/github.com/drone/envsubst",
"HOME": "/root",
"SHELL": "/bin/sh"
},
"entrypoint": [
"/bin/sh",
"-c"
],
"command": [
"echo $CI_SCRIPT | base64 -d | /bin/sh -e"
],
"volumes": [
"pipeline_default:/go"
],
"networks": [
{
"name": "pipeline_default",
"aliases": null
}
],
"on_success": true,
"auth_config": {}
}
]
}
],
"networks": [
{
"name": "pipeline_default",
"driver": "bridge"
}
],
"volumes": [
{
"name": "pipeline_default",
"driver": "local"
}
]
}

View file

@ -0,0 +1,16 @@
workspace:
base: /go
path: src/github.com/drone/envsubst
clone:
git:
image: plugins/git
depth: 50
pipeline:
build:
image: golang:1.7
commands:
- go get -t ./...
- go build
- go test -v

View file

@ -0,0 +1,11 @@
Compile the yaml to the intermediate representation:
```
pipec compile --system-arch windows/amd64
```
Execute the intermediate representation:
```
pipec exec
```

View file

@ -0,0 +1,170 @@
{
"pipeline": [
{
"name": "pipeline_clone_0",
"alias": "git",
"steps": [
{
"name": "pipeline_clone_0",
"alias": "git",
"image": "plugins/git:windows",
"working_dir": "c:\\gopath/src\\github.com\\drone\\envsubst",
"environment": {
"CI": "drone",
"CI_BUILD_CREATED": "1486119586",
"CI_BUILD_EVENT": "push",
"CI_BUILD_NUMBER": "6",
"CI_BUILD_STARTED": "1486119585",
"CI_COMMIT_AUTHOR": "bradrydzewski",
"CI_COMMIT_AUTHOR_NAME": "bradrydzewski",
"CI_COMMIT_BRANCH": "master",
"CI_COMMIT_MESSAGE": "added a few more test cases for escaping behavior",
"CI_COMMIT_REF": "refs/heads/master",
"CI_COMMIT_SHA": "d0876d3176965f9552a611cbd56e24a9264355e6",
"CI_REMOTE_URL": "https://github.com/drone/envsubst.git",
"CI_REPO": "drone/envsubst",
"CI_REPO_LINK": "https://github.com/drone/envsubst",
"CI_REPO_NAME": "drone/envsubst",
"CI_REPO_REMOTE": "https://github.com/drone/envsubst.git",
"CI_SYSTEM": "pipec",
"CI_SYSTEM_ARCH": "windows/amd64",
"CI_SYSTEM_LINK": "https://github.com/cncd/pipec",
"CI_SYSTEM_NAME": "pipec",
"CI_WORKSPACE": "c:\\gopath/src\\github.com\\drone\\envsubst",
"DRONE": "true",
"DRONE_ARCH": "windows/amd64",
"DRONE_BRANCH": "master",
"DRONE_BUILD_CREATED": "1486119586",
"DRONE_BUILD_EVENT": "push",
"DRONE_BUILD_LINK": "https://github.com/cncd/pipec/drone/envsubst/6",
"DRONE_BUILD_NUMBER": "6",
"DRONE_BUILD_STARTED": "1486119585",
"DRONE_COMMIT": "d0876d3176965f9552a611cbd56e24a9264355e6",
"DRONE_COMMIT_AUTHOR": "bradrydzewski",
"DRONE_COMMIT_BRANCH": "master",
"DRONE_COMMIT_MESSAGE": "added a few more test cases for escaping behavior",
"DRONE_COMMIT_REF": "refs/heads/master",
"DRONE_COMMIT_SHA": "d0876d3176965f9552a611cbd56e24a9264355e6",
"DRONE_JOB_STARTED": "1486119585",
"DRONE_REMOTE_URL": "https://github.com/drone/envsubst.git",
"DRONE_REPO": "drone/envsubst",
"DRONE_REPO_LINK": "https://github.com/drone/envsubst",
"DRONE_REPO_NAME": "envsubst",
"DRONE_REPO_OWNER": "drone",
"DRONE_REPO_SCM": "git",
"DRONE_WORKSPACE": "c:\\gopath/src\\github.com\\drone\\envsubst",
"PLUGIN_DEPTH": "50"
},
"volumes": [
"pipeline_default:c:\\gopath"
],
"networks": [
{
"name": "pipeline_default",
"aliases": [
"git"
]
}
],
"on_success": true,
"auth_config": {}
}
]
},
{
"name": "pipeline_stage_0",
"alias": "build",
"steps": [
{
"name": "pipeline_step_0",
"alias": "build",
"image": "golang:1.10.1-nanoserver-sac2016",
"working_dir": "c:\\gopath/src\\github.com\\drone\\envsubst",
"environment": {
"CI": "drone",
"CI_BUILD_CREATED": "1486119586",
"CI_BUILD_EVENT": "push",
"CI_BUILD_NUMBER": "6",
"CI_BUILD_STARTED": "1486119585",
"CI_COMMIT_AUTHOR": "bradrydzewski",
"CI_COMMIT_AUTHOR_NAME": "bradrydzewski",
"CI_COMMIT_BRANCH": "master",
"CI_COMMIT_MESSAGE": "added a few more test cases for escaping behavior",
"CI_COMMIT_REF": "refs/heads/master",
"CI_COMMIT_SHA": "d0876d3176965f9552a611cbd56e24a9264355e6",
"CI_REMOTE_URL": "https://github.com/drone/envsubst.git",
"CI_REPO": "drone/envsubst",
"CI_REPO_LINK": "https://github.com/drone/envsubst",
"CI_REPO_NAME": "drone/envsubst",
"CI_REPO_REMOTE": "https://github.com/drone/envsubst.git",
"CI_SCRIPT": "CiRFcnJvckFjdGlvblByZWZlcmVuY2UgPSAnU3RvcCc7CiZjbWQgL2MgIm1rZGlyIGM6XHJvb3QiOwppZiAoJEVudjpDSV9ORVRSQ19NQUNISU5FKSB7CiRuZXRyYz1bc3RyaW5nXTo6Rm9ybWF0KCJ7MH1cX25ldHJjIiwkRW52OkhPTUUpOwoibWFjaGluZSAkRW52OkNJX05FVFJDX01BQ0hJTkUiID4+ICRuZXRyYzsKImxvZ2luICRFbnY6Q0lfTkVUUkNfVVNFUk5BTUUiID4+ICRuZXRyYzsKInBhc3N3b3JkICRFbnY6Q0lfTkVUUkNfUEFTU1dPUkQiID4+ICRuZXRyYzsKfTsKW0Vudmlyb25tZW50XTo6U2V0RW52aXJvbm1lbnRWYXJpYWJsZSgiQ0lfTkVUUkNfUEFTU1dPUkQiLCRudWxsKTsKW0Vudmlyb25tZW50XTo6U2V0RW52aXJvbm1lbnRWYXJpYWJsZSgiQ0lfU0NSSVBUIiwkbnVsbCk7CltFbnZpcm9ubWVudF06OlNldEVudmlyb25tZW50VmFyaWFibGUoIkRST05FX05FVFJDX1VTRVJOQU1FIiwkbnVsbCk7CltFbnZpcm9ubWVudF06OlNldEVudmlyb25tZW50VmFyaWFibGUoIkRST05FX05FVFJDX1BBU1NXT1JEIiwkbnVsbCk7CgpXcml0ZS1PdXRwdXQgKCcrICJnbyBnZXQgLXQgLlxcLi4uIicpOyAgW0NvbnNvbGVdOjpPdXQuRmx1c2goKQomIGdvIGdldCAtdCAuXC4uLjsgaWYgKCRMQVNURVhJVENPREUgLW5lIDApIHtleGl0ICRMQVNURVhJVENPREV9CgpXcml0ZS1PdXRwdXQgKCcrICJnbyBidWlsZCInKTsgIFtDb25zb2xlXTo6T3V0LkZsdXNoKCkKJiBnbyBidWlsZDsgaWYgKCRMQVNURVhJVENPREUgLW5lIDApIHtleGl0ICRMQVNURVhJVENPREV9CgpXcml0ZS1PdXRwdXQgKCcrICJnbyB0ZXN0IC12IicpOyAgW0NvbnNvbGVdOjpPdXQuRmx1c2goKQomIGdvIHRlc3QgLXY7IGlmICgkTEFTVEVYSVRDT0RFIC1uZSAwKSB7ZXhpdCAkTEFTVEVYSVRDT0RFfQoK",
"CI_SYSTEM": "pipec",
"CI_SYSTEM_ARCH": "windows/amd64",
"CI_SYSTEM_LINK": "https://github.com/cncd/pipec",
"CI_SYSTEM_NAME": "pipec",
"CI_WORKSPACE": "c:\\gopath/src\\github.com\\drone\\envsubst",
"DRONE": "true",
"DRONE_ARCH": "windows/amd64",
"DRONE_BRANCH": "master",
"DRONE_BUILD_CREATED": "1486119586",
"DRONE_BUILD_EVENT": "push",
"DRONE_BUILD_LINK": "https://github.com/cncd/pipec/drone/envsubst/6",
"DRONE_BUILD_NUMBER": "6",
"DRONE_BUILD_STARTED": "1486119585",
"DRONE_COMMIT": "d0876d3176965f9552a611cbd56e24a9264355e6",
"DRONE_COMMIT_AUTHOR": "bradrydzewski",
"DRONE_COMMIT_BRANCH": "master",
"DRONE_COMMIT_MESSAGE": "added a few more test cases for escaping behavior",
"DRONE_COMMIT_REF": "refs/heads/master",
"DRONE_COMMIT_SHA": "d0876d3176965f9552a611cbd56e24a9264355e6",
"DRONE_JOB_STARTED": "1486119585",
"DRONE_REMOTE_URL": "https://github.com/drone/envsubst.git",
"DRONE_REPO": "drone/envsubst",
"DRONE_REPO_LINK": "https://github.com/drone/envsubst",
"DRONE_REPO_NAME": "envsubst",
"DRONE_REPO_OWNER": "drone",
"DRONE_REPO_SCM": "git",
"DRONE_WORKSPACE": "c:\\gopath/src\\github.com\\drone\\envsubst",
"HOME": "c:\\root",
"SHELL": "powershell.exe"
},
"entrypoint": [
"powershell",
"-noprofile",
"-noninteractive",
"-command"
],
"command": [
"[System.Text.Encoding]::UTF8.GetString([System.Convert]::FromBase64String($Env:CI_SCRIPT)) | iex"
],
"volumes": [
"pipeline_default:c:\\gopath"
],
"networks": [
{
"name": "pipeline_default",
"aliases": [
"build"
]
}
],
"on_success": true,
"auth_config": {}
}
]
}
],
"networks": [
{
"name": "pipeline_default",
"driver": "nat"
}
],
"volumes": [
{
"name": "pipeline_default",
"driver": "local"
}
],
"secrets": null
}

View file

@ -0,0 +1,18 @@
platform: windows/amd64
workspace:
base: c:\gopath
path: src\github.com\drone\envsubst
clone:
git:
image: plugins/git:windows
depth: 50
pipeline:
build:
image: golang:1.10.1-nanoserver-sac2016
commands:
- go get -t .\...
- go build
- go test -v

View file

@ -0,0 +1,231 @@
{
"pipeline": [
{
"name": "pipeline_clone",
"alias": "clone",
"steps": [
{
"name": "pipeline_clone",
"image": "plugins/git:latest",
"working_dir": "/go/src/github.com/go-sql-driver/mysql",
"environment": {
"CI": "pipec",
"CI_BUILD_CREATED": "1486119586",
"CI_BUILD_EVENT": "push",
"CI_BUILD_NUMBER": "530",
"CI_BUILD_STARTED": "1486119585",
"CI_COMMIT_AUTHOR": "egorsmkv",
"CI_COMMIT_AUTHOR_NAME": "egorsmkv",
"CI_COMMIT_BRANCH": "master",
"CI_COMMIT_MESSAGE": "Fix many urls",
"CI_COMMIT_REF": "refs/heads/master",
"CI_COMMIT_SHA": "2e00b5cd70399450106cec6431c2e2ce3cae5034",
"CI_REMOTE_URL": "https://github.com/go-sql-driver/mysql.git",
"CI_REPO": "go-sql-driver/mysql",
"CI_REPO_LINK": "https://github.com/go-sql-driver/mysql",
"CI_REPO_NAME": "go-sql-driver/mysql",
"CI_REPO_REMOTE": "https://github.com/go-sql-driver/mysql.git",
"CI_SYSTEM": "pipec",
"CI_SYSTEM_ARCH": "linux/amd64",
"CI_SYSTEM_LINK": "https://github.com/cncd/pipec",
"CI_SYSTEM_NAME": "pipec",
"CI_WORKSPACE": "/go/src/github.com/go-sql-driver/mysql",
"DRONE_BUILD_CREATED": "1486119586",
"DRONE_BUILD_EVENT": "push",
"DRONE_BUILD_NUMBER": "530",
"DRONE_BUILD_STARTED": "1486119585",
"DRONE_COMMIT_AUTHOR": "egorsmkv",
"DRONE_COMMIT_AUTHOR_NAME": "egorsmkv",
"DRONE_COMMIT_BRANCH": "master",
"DRONE_COMMIT_MESSAGE": "Fix many urls",
"DRONE_COMMIT_REF": "refs/heads/master",
"DRONE_COMMIT_SHA": "2e00b5cd70399450106cec6431c2e2ce3cae5034",
"DRONE_REMOTE_URL": "https://github.com/go-sql-driver/mysql.git",
"DRONE_REPO": "go-sql-driver/mysql",
"DRONE_REPO_LINK": "https://github.com/go-sql-driver/mysql",
"DRONE_REPO_NAME": "go-sql-driver/mysql",
"DRONE_REPO_REMOTE": "https://github.com/go-sql-driver/mysql.git",
"DRONE_SYSTEM": "pipec",
"DRONE_SYSTEM_ARCH": "linux/amd64",
"DRONE_SYSTEM_LINK": "https://github.com/cncd/pipec",
"DRONE_SYSTEM_NAME": "pipec",
"DRONE_WORKSPACE": "/go/src/github.com/go-sql-driver/mysql",
"PLUGIN_DEPTH": "0"
},
"volumes": [
"pipeline_default:/go"
],
"networks": [
{
"name": "pipeline_default",
"aliases": null
}
],
"on_success": true,
"auth_config": {}
}
]
},
{
"name": "pipeline_services",
"alias": "services",
"steps": [
{
"name": "pipeline_services_0",
"alias": "database",
"image": "mysql:latest",
"detach": true,
"environment": {
"CI": "pipec",
"CI_BUILD_CREATED": "1486119586",
"CI_BUILD_EVENT": "push",
"CI_BUILD_NUMBER": "530",
"CI_BUILD_STARTED": "1486119585",
"CI_COMMIT_AUTHOR": "egorsmkv",
"CI_COMMIT_AUTHOR_NAME": "egorsmkv",
"CI_COMMIT_BRANCH": "master",
"CI_COMMIT_MESSAGE": "Fix many urls",
"CI_COMMIT_REF": "refs/heads/master",
"CI_COMMIT_SHA": "2e00b5cd70399450106cec6431c2e2ce3cae5034",
"CI_REMOTE_URL": "https://github.com/go-sql-driver/mysql.git",
"CI_REPO": "go-sql-driver/mysql",
"CI_REPO_LINK": "https://github.com/go-sql-driver/mysql",
"CI_REPO_NAME": "go-sql-driver/mysql",
"CI_REPO_REMOTE": "https://github.com/go-sql-driver/mysql.git",
"CI_SYSTEM": "pipec",
"CI_SYSTEM_ARCH": "linux/amd64",
"CI_SYSTEM_LINK": "https://github.com/cncd/pipec",
"CI_SYSTEM_NAME": "pipec",
"CI_WORKSPACE": "/go/src/github.com/go-sql-driver/mysql",
"DRONE_BUILD_CREATED": "1486119586",
"DRONE_BUILD_EVENT": "push",
"DRONE_BUILD_NUMBER": "530",
"DRONE_BUILD_STARTED": "1486119585",
"DRONE_COMMIT_AUTHOR": "egorsmkv",
"DRONE_COMMIT_AUTHOR_NAME": "egorsmkv",
"DRONE_COMMIT_BRANCH": "master",
"DRONE_COMMIT_MESSAGE": "Fix many urls",
"DRONE_COMMIT_REF": "refs/heads/master",
"DRONE_COMMIT_SHA": "2e00b5cd70399450106cec6431c2e2ce3cae5034",
"DRONE_REMOTE_URL": "https://github.com/go-sql-driver/mysql.git",
"DRONE_REPO": "go-sql-driver/mysql",
"DRONE_REPO_LINK": "https://github.com/go-sql-driver/mysql",
"DRONE_REPO_NAME": "go-sql-driver/mysql",
"DRONE_REPO_REMOTE": "https://github.com/go-sql-driver/mysql.git",
"DRONE_SYSTEM": "pipec",
"DRONE_SYSTEM_ARCH": "linux/amd64",
"DRONE_SYSTEM_LINK": "https://github.com/cncd/pipec",
"DRONE_SYSTEM_NAME": "pipec",
"DRONE_WORKSPACE": "/go/src/github.com/go-sql-driver/mysql",
"MYSQL_ALLOW_EMPTY_PASSWORD": "yes",
"MYSQL_DATABASE": "gotest"
},
"volumes": [
"pipeline_default:/go"
],
"networks": [
{
"name": "pipeline_default",
"aliases": [
"database"
]
}
],
"on_success": true,
"auth_config": {}
}
]
},
{
"name": "pipeline_stage_0",
"alias": "build",
"steps": [
{
"name": "pipeline_step_0",
"alias": "build",
"image": "golang:1.7",
"working_dir": "/go/src/github.com/go-sql-driver/mysql",
"environment": {
"CI": "pipec",
"CI_BUILD_CREATED": "1486119586",
"CI_BUILD_EVENT": "push",
"CI_BUILD_NUMBER": "530",
"CI_BUILD_STARTED": "1486119585",
"CI_COMMIT_AUTHOR": "egorsmkv",
"CI_COMMIT_AUTHOR_NAME": "egorsmkv",
"CI_COMMIT_BRANCH": "master",
"CI_COMMIT_MESSAGE": "Fix many urls",
"CI_COMMIT_REF": "refs/heads/master",
"CI_COMMIT_SHA": "2e00b5cd70399450106cec6431c2e2ce3cae5034",
"CI_REMOTE_URL": "https://github.com/go-sql-driver/mysql.git",
"CI_REPO": "go-sql-driver/mysql",
"CI_REPO_LINK": "https://github.com/go-sql-driver/mysql",
"CI_REPO_NAME": "go-sql-driver/mysql",
"CI_REPO_REMOTE": "https://github.com/go-sql-driver/mysql.git",
"CI_SCRIPT": "CmlmIFsgLW4gIiRDSV9ORVRSQ19NQUNISU5FIiBdOyB0aGVuCmNhdCA8PEVPRiA+ICRIT01FLy5uZXRyYwptYWNoaW5lICRDSV9ORVRSQ19NQUNISU5FCmxvZ2luICRDSV9ORVRSQ19VU0VSTkFNRQpwYXNzd29yZCAkQ0lfTkVUUkNfUEFTU1dPUkQKRU9GCmNobW9kIDA2MDAgJEhPTUUvLm5ldHJjCmZpCnVuc2V0IENJX05FVFJDX1VTRVJOQU1FCnVuc2V0IENJX05FVFJDX1BBU1NXT1JECnVuc2V0IENJX1NDUklQVAoKZWNobyArICJzbGVlcCAyMCIKc2xlZXAgMjAKCmVjaG8gKyAiZ28gZ2V0IC12IC10IgpnbyBnZXQgLXYgLXQKCmVjaG8gKyAiZ28gdGVzdCAtdiIKZ28gdGVzdCAtdgoK",
"CI_SYSTEM": "pipec",
"CI_SYSTEM_ARCH": "linux/amd64",
"CI_SYSTEM_LINK": "https://github.com/cncd/pipec",
"CI_SYSTEM_NAME": "pipec",
"CI_WORKSPACE": "/go/src/github.com/go-sql-driver/mysql",
"DRONE_BUILD_CREATED": "1486119586",
"DRONE_BUILD_EVENT": "push",
"DRONE_BUILD_NUMBER": "530",
"DRONE_BUILD_STARTED": "1486119585",
"DRONE_COMMIT_AUTHOR": "egorsmkv",
"DRONE_COMMIT_AUTHOR_NAME": "egorsmkv",
"DRONE_COMMIT_BRANCH": "master",
"DRONE_COMMIT_MESSAGE": "Fix many urls",
"DRONE_COMMIT_REF": "refs/heads/master",
"DRONE_COMMIT_SHA": "2e00b5cd70399450106cec6431c2e2ce3cae5034",
"DRONE_REMOTE_URL": "https://github.com/go-sql-driver/mysql.git",
"DRONE_REPO": "go-sql-driver/mysql",
"DRONE_REPO_LINK": "https://github.com/go-sql-driver/mysql",
"DRONE_REPO_NAME": "go-sql-driver/mysql",
"DRONE_REPO_REMOTE": "https://github.com/go-sql-driver/mysql.git",
"DRONE_SYSTEM": "pipec",
"DRONE_SYSTEM_ARCH": "linux/amd64",
"DRONE_SYSTEM_LINK": "https://github.com/cncd/pipec",
"DRONE_SYSTEM_NAME": "pipec",
"DRONE_WORKSPACE": "/go/src/github.com/go-sql-driver/mysql",
"HOME": "/root",
"MYSQL_TEST_ADDR": "database:3306",
"SHELL": "/bin/sh"
},
"entrypoint": [
"/bin/sh",
"-c"
],
"command": [
"echo $CI_SCRIPT | base64 -d | /bin/sh -e"
],
"volumes": [
"pipeline_default:/go"
],
"networks": [
{
"name": "pipeline_default",
"aliases": [
"database"
]
}
],
"on_success": true,
"auth_config": {}
}
]
}
],
"networks": [
{
"name": "pipeline_default",
"driver": "bridge"
}
],
"volumes": [
{
"name": "pipeline_default",
"driver": "local"
}
]
}

View file

@ -0,0 +1,20 @@
workspace:
base: /go
path: src/github.com/go-sql-driver/mysql
pipeline:
build:
image: golang:1.7
environment:
MYSQL_TEST_ADDR: database:3306
commands:
- sleep 20
- go get -v -t
- go test -v
services:
database:
image: mysql
environment:
- MYSQL_DATABASE=gotest
- MYSQL_ALLOW_EMPTY_PASSWORD=yes

Some files were not shown because too many files have changed in this diff Show more