2014-08-08 05:22:04 +00:00
|
|
|
package main
|
|
|
|
|
|
|
|
import (
|
|
|
|
"fmt"
|
|
|
|
"io/ioutil"
|
|
|
|
"os"
|
|
|
|
"path/filepath"
|
|
|
|
"time"
|
|
|
|
|
|
|
|
"github.com/drone/drone/shared/build"
|
|
|
|
"github.com/drone/drone/shared/build/docker"
|
|
|
|
"github.com/drone/drone/shared/build/log"
|
|
|
|
"github.com/drone/drone/shared/build/repo"
|
|
|
|
"github.com/drone/drone/shared/build/script"
|
|
|
|
|
|
|
|
"github.com/codegangsta/cli"
|
|
|
|
)
|
|
|
|
|
|
|
|
const EXIT_STATUS = 1
|
|
|
|
|
|
|
|
// NewBuildCommand returns the CLI command for "build".
|
|
|
|
func NewBuildCommand() cli.Command {
|
|
|
|
return cli.Command{
|
|
|
|
Name: "build",
|
|
|
|
Usage: "run a local build",
|
|
|
|
Flags: []cli.Flag{
|
|
|
|
cli.StringFlag{
|
|
|
|
Name: "i",
|
|
|
|
Value: "",
|
|
|
|
Usage: "identify file injected in the container",
|
|
|
|
},
|
2014-10-12 21:09:55 +00:00
|
|
|
cli.BoolFlag{
|
2014-08-08 05:22:04 +00:00
|
|
|
Name: "p",
|
|
|
|
Usage: "runs drone build in a privileged container",
|
|
|
|
},
|
2014-10-12 21:09:55 +00:00
|
|
|
cli.BoolFlag{
|
2014-10-12 21:07:28 +00:00
|
|
|
Name: "deploy",
|
|
|
|
Usage: "runs drone build with deployments enabled",
|
|
|
|
},
|
2014-10-12 21:09:55 +00:00
|
|
|
cli.BoolFlag{
|
2014-10-12 21:07:28 +00:00
|
|
|
Name: "publish",
|
|
|
|
Usage: "runs drone build with publishing enabled",
|
|
|
|
},
|
2014-10-21 08:17:12 +00:00
|
|
|
cli.StringFlag{
|
|
|
|
Name: "docker-host",
|
2014-12-20 14:26:33 +00:00
|
|
|
Value: getHost(),
|
2014-10-21 08:17:12 +00:00
|
|
|
Usage: "docker daemon address",
|
|
|
|
},
|
|
|
|
cli.StringFlag{
|
|
|
|
Name: "docker-cert",
|
2014-12-20 14:26:33 +00:00
|
|
|
Value: getCert(),
|
2014-10-21 08:17:12 +00:00
|
|
|
Usage: "docker daemon tls certificate",
|
|
|
|
},
|
|
|
|
cli.StringFlag{
|
|
|
|
Name: "docker-key",
|
2014-12-20 14:26:33 +00:00
|
|
|
Value: getKey(),
|
2014-10-21 08:17:12 +00:00
|
|
|
Usage: "docker daemon tls key",
|
|
|
|
},
|
2014-08-08 05:22:04 +00:00
|
|
|
},
|
|
|
|
Action: func(c *cli.Context) {
|
|
|
|
buildCommandFunc(c)
|
|
|
|
},
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// buildCommandFunc executes the "build" command.
|
|
|
|
func buildCommandFunc(c *cli.Context) {
|
|
|
|
var privileged = c.Bool("p")
|
|
|
|
var identity = c.String("i")
|
2014-10-12 21:07:28 +00:00
|
|
|
var deploy = c.Bool("deploy")
|
|
|
|
var publish = c.Bool("publish")
|
2014-08-08 05:22:04 +00:00
|
|
|
var path string
|
|
|
|
|
2014-10-21 08:17:12 +00:00
|
|
|
var dockerhost = c.String("docker-host")
|
|
|
|
var dockercert = c.String("docker-cert")
|
|
|
|
var dockerkey = c.String("docker-key")
|
|
|
|
|
2014-08-08 05:22:04 +00:00
|
|
|
// the path is provided as an optional argument that
|
|
|
|
// will otherwise default to $PWD/.drone.yml
|
|
|
|
if len(c.Args()) > 0 {
|
|
|
|
path = c.Args()[0]
|
|
|
|
}
|
|
|
|
|
|
|
|
switch len(path) {
|
|
|
|
case 0:
|
|
|
|
path, _ = os.Getwd()
|
|
|
|
path = filepath.Join(path, ".drone.yml")
|
|
|
|
default:
|
|
|
|
path = filepath.Clean(path)
|
|
|
|
path, _ = filepath.Abs(path)
|
|
|
|
path = filepath.Join(path, ".drone.yml")
|
|
|
|
}
|
|
|
|
|
|
|
|
// this configures the default Docker logging levels,
|
|
|
|
// and suffix and prefix values.
|
|
|
|
log.SetPrefix("\033[2m[DRONE] ")
|
|
|
|
log.SetSuffix("\033[0m\n")
|
|
|
|
log.SetOutput(os.Stdout)
|
|
|
|
log.SetPriority(log.LOG_DEBUG) //LOG_NOTICE
|
|
|
|
docker.Logging = false
|
|
|
|
|
2014-10-21 08:17:12 +00:00
|
|
|
var exit, _ = run(path, identity, dockerhost, dockercert, dockerkey, publish, deploy, privileged)
|
2014-08-08 05:22:04 +00:00
|
|
|
os.Exit(exit)
|
|
|
|
}
|
|
|
|
|
2014-10-21 08:17:12 +00:00
|
|
|
// TODO this has gotten a bit out of hand. refactor input params
|
|
|
|
func run(path, identity, dockerhost, dockercert, dockerkey string, publish, deploy, privileged bool) (int, error) {
|
|
|
|
dockerClient, err := docker.NewHostCertFile(dockerhost, dockercert, dockerkey)
|
|
|
|
if err != nil {
|
|
|
|
log.Err(err.Error())
|
|
|
|
return EXIT_STATUS, err
|
|
|
|
}
|
2014-08-08 05:22:04 +00:00
|
|
|
|
2014-10-12 20:57:29 +00:00
|
|
|
// parse the private environment variables
|
|
|
|
envs := getParamMap("DRONE_ENV_")
|
|
|
|
|
2014-08-08 05:22:04 +00:00
|
|
|
// parse the Drone yml file
|
2014-12-07 16:26:43 +00:00
|
|
|
s, err := script.ParseBuildFile(path, envs)
|
2014-08-08 05:22:04 +00:00
|
|
|
if err != nil {
|
|
|
|
log.Err(err.Error())
|
|
|
|
return EXIT_STATUS, err
|
|
|
|
}
|
|
|
|
|
2014-10-12 20:57:29 +00:00
|
|
|
// inject private environment variables into build script
|
|
|
|
for key, val := range envs {
|
|
|
|
s.Env = append(s.Env, key+"="+val)
|
|
|
|
}
|
|
|
|
|
2014-10-12 21:07:28 +00:00
|
|
|
if deploy == false {
|
2014-12-07 15:45:14 +00:00
|
|
|
s.Deploy = nil
|
2014-10-12 21:07:28 +00:00
|
|
|
}
|
|
|
|
if publish == false {
|
2014-12-07 15:45:14 +00:00
|
|
|
s.Publish = nil
|
2014-10-12 21:07:28 +00:00
|
|
|
}
|
2014-08-08 05:22:04 +00:00
|
|
|
|
|
|
|
// get the repository root directory
|
|
|
|
dir := filepath.Dir(path)
|
|
|
|
code := repo.Repo{
|
|
|
|
Name: filepath.Base(dir),
|
|
|
|
Branch: "HEAD", // should we do this?
|
|
|
|
Path: dir,
|
|
|
|
}
|
|
|
|
|
|
|
|
// does the local repository match the
|
|
|
|
// $GOPATH/src/{package} pattern? This is
|
|
|
|
// important so we know the target location
|
|
|
|
// where the code should be copied inside
|
|
|
|
// the container.
|
|
|
|
if gopath, ok := getRepoPath(dir); ok {
|
|
|
|
code.Dir = gopath
|
|
|
|
|
|
|
|
} else if gopath, ok := getGoPath(dir); ok {
|
|
|
|
// in this case we found a GOPATH and
|
|
|
|
// reverse engineered the package path
|
|
|
|
code.Dir = gopath
|
|
|
|
|
|
|
|
} else {
|
|
|
|
// otherwise just use directory name
|
|
|
|
code.Dir = filepath.Base(dir)
|
|
|
|
}
|
|
|
|
|
|
|
|
// this is where the code gets uploaded to the container
|
|
|
|
// TODO move this code to the build package
|
|
|
|
code.Dir = filepath.Join("/var/cache/drone/src", filepath.Clean(code.Dir))
|
|
|
|
|
|
|
|
// ssh key to import into container
|
|
|
|
var key []byte
|
|
|
|
if len(identity) != 0 {
|
|
|
|
key, err = ioutil.ReadFile(identity)
|
|
|
|
if err != nil {
|
|
|
|
fmt.Printf("[Error] Could not find or read identity file %s\n", identity)
|
|
|
|
return EXIT_STATUS, err
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// loop through and create builders
|
|
|
|
builder := build.New(dockerClient)
|
|
|
|
builder.Build = s
|
|
|
|
builder.Repo = &code
|
|
|
|
builder.Key = key
|
|
|
|
builder.Stdout = os.Stdout
|
|
|
|
builder.Timeout = 300 * time.Minute
|
|
|
|
builder.Privileged = privileged
|
|
|
|
|
|
|
|
// execute the build
|
|
|
|
if err := builder.Run(); err != nil {
|
|
|
|
log.Errf("Error executing build: %s", err.Error())
|
|
|
|
return EXIT_STATUS, err
|
|
|
|
}
|
|
|
|
|
|
|
|
fmt.Printf("\nDrone Build Results \033[90m(%s)\033[0m\n", dir)
|
|
|
|
|
|
|
|
// loop through and print results
|
|
|
|
|
|
|
|
build := builder.Build
|
|
|
|
res := builder.BuildState
|
|
|
|
duration := time.Duration(res.Finished - res.Started)
|
|
|
|
switch {
|
|
|
|
case builder.BuildState.ExitCode == 0:
|
|
|
|
fmt.Printf(" \033[32m\u2713\033[0m %v \033[90m(%v)\033[0m\n", build.Name, humanizeDuration(duration*time.Second))
|
|
|
|
case builder.BuildState.ExitCode != 0:
|
|
|
|
fmt.Printf(" \033[31m\u2717\033[0m %v \033[90m(%v)\033[0m\n", build.Name, humanizeDuration(duration*time.Second))
|
|
|
|
}
|
|
|
|
|
|
|
|
return builder.BuildState.ExitCode, nil
|
|
|
|
}
|
2014-12-20 14:26:33 +00:00
|
|
|
|
|
|
|
func getHost() string {
|
|
|
|
return os.Getenv("DOCKER_HOST")
|
|
|
|
}
|
|
|
|
|
|
|
|
func getCert() string {
|
|
|
|
if os.Getenv("DOCKER_CERT_PATH") != "" && os.Getenv("DOCKER_TLS_VERIFY") == "1" {
|
|
|
|
return filepath.Join(os.Getenv("DOCKER_CERT_PATH"), "cert.pem")
|
|
|
|
} else {
|
|
|
|
return ""
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
func getKey() string {
|
|
|
|
if os.Getenv("DOCKER_CERT_PATH") != "" && os.Getenv("DOCKER_TLS_VERIFY") == "1" {
|
|
|
|
return filepath.Join(os.Getenv("DOCKER_CERT_PATH"), "key.pem")
|
|
|
|
} else {
|
|
|
|
return ""
|
|
|
|
}
|
|
|
|
}
|