// TODO this has gotten a bit out of hand. refactor input params func run(path, identity, sshconfig, 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 } // parse the private environment variables envs := getParamMap("DRONE_ENV_") // parse the Drone yml file s, err := script.ParseBuildFile(script.Inject(path, envs)) if err != nil { log.Err(err.Error()) return EXIT_STATUS, err } // inject private environment variables into build script for key, val := range envs { s.Env = append(s.Env, key+"="+val) } if deploy == false { s.Publish = nil } if publish == false { s.Deploy = nil } // 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 } } // ssh-config file to import into container var sshconfigcontent []byte if len(sshconfig) != 0 { sshconfigcontent, err = ioutil.ReadFile(sshconfig) if err != nil { fmt.Printf("[Error] Could not find or read ssh-config file %s\n", sshconfig) return EXIT_STATUS, err } } // loop through and create builders builder := build.New(dockerClient) builder.Build = s builder.Repo = &code builder.Key = key builder.SSHConfig = sshconfigcontent 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 }
func (d *Docker) Do(c context.Context, r *worker.Work) { // ensure that we can recover from any panics to // avoid bringing down the entire application. defer func() { if e := recover(); e != nil { log.Printf("%s: %s", e, debug.Stack()) } }() // mark the build as Started and update the database r.Commit.Status = model.StatusStarted r.Commit.Started = time.Now().UTC().Unix() datastore.PutCommit(c, r.Commit) // notify all listeners that the build is started commitc := pubsub.Register(c, "_global") commitc.Publish(r) stdoutc := pubsub.RegisterOpts(c, r.Commit.ID, pubsub.ConsoleOpts) defer pubsub.Unregister(c, r.Commit.ID) // create a special buffer that will also // write to a websocket channel buf := pubsub.NewBuffer(stdoutc) // parse the parameters and build script. The script has already // been parsed in the hook, so we can be confident it will succeed. // that being said, we should clean this up params, err := r.Repo.ParamMap() if err != nil { log.Printf("Error parsing PARAMS for %s/%s, Err: %s", r.Repo.Owner, r.Repo.Name, err.Error()) } script, err := script.ParseBuild(script.Inject(r.Commit.Config, params)) if err != nil { log.Printf("Error parsing YAML for %s/%s, Err: %s", r.Repo.Owner, r.Repo.Name, err.Error()) } // append private parameters to the environment // variable section of the .drone.yml file, iff // this is not a pull request (for security purposes) if params != nil && (r.Repo.Private || len(r.Commit.PullRequest) == 0) { for k, v := range params { script.Env = append(script.Env, k+"="+v) } } path := r.Repo.Host + "/" + r.Repo.Owner + "/" + r.Repo.Name repo := &repo.Repo{ Name: path, Path: r.Repo.CloneURL, Branch: r.Commit.Branch, Commit: r.Commit.Sha, PR: r.Commit.PullRequest, Dir: filepath.Join("/var/cache/drone/src", git.GitPath(script.Git, path)), Depth: git.GitDepth(script.Git), } // send all "started" notifications if script.Notifications == nil { script.Notifications = ¬ify.Notification{} } script.Notifications.Send(&model.Request{ User: r.User, Repo: r.Repo, Commit: r.Commit, Host: r.Host, }) // create an instance of the Docker builder builder := build.New(d.docker) builder.Build = script builder.Repo = repo builder.Stdout = buf builder.Key = []byte(r.Repo.PrivateKey) builder.Timeout = time.Duration(r.Repo.Timeout) * time.Second builder.Privileged = r.Repo.Privileged // run the build err = builder.Run() // update the build status based on the results // from the build runner. switch { case err != nil: r.Commit.Status = model.StatusError log.Printf("Error building %s, Err: %s", r.Commit.Sha, err) buf.WriteString(err.Error()) case builder.BuildState == nil: r.Commit.Status = model.StatusFailure case builder.BuildState.ExitCode != 0: r.Commit.Status = model.StatusFailure default: r.Commit.Status = model.StatusSuccess } // calcualte the build finished and duration details and // update the commit r.Commit.Finished = time.Now().UTC().Unix() r.Commit.Duration = (r.Commit.Finished - r.Commit.Started) datastore.PutCommit(c, r.Commit) blobstore.Put(c, filepath.Join(r.Repo.Host, r.Repo.Owner, r.Repo.Name, r.Commit.Branch, r.Commit.Sha), buf.Bytes()) // notify all listeners that the build is finished commitc.Publish(r) // send all "finished" notifications script.Notifications.Send(&model.Request{ User: r.User, Repo: r.Repo, Commit: r.Commit, Host: r.Host, }) }
func (d *Docker) Do(c context.Context, r *worker.Work) { // ensure that we can recover from any panics to // avoid bringing down the entire application. defer func() { if e := recover(); e != nil { log.Printf("%s: %s", e, debug.Stack()) } }() r.Build.Start = time.Now().UTC().Unix() r.Build.Status = resource.StatusStarted if err := datastore.PostBuild(c, r.Build); err != nil { log.Println("Error updating build status to started", err) } var buf bytes.Buffer var name = r.Build.Name var version = r.Build.Version log.Println("starting build", name, version) // create a temp directory where we can // clone the repository for testing var tmp, err = ioutil.TempDir("", "drone_"+name+"_") if err != nil { log.Println(err) return } defer os.RemoveAll(tmp) imageName, err := createImage(d.docker, r.Build.Channel, r.Build.Revision) if err != nil { log.Println("Error building new Dart Image [%s]", err.Error()) } // download package to temp directory tar, err := os.Create(filepath.Join(tmp, "package.tar.gz")) if err != nil { log.Println("Error creating temp directory", err) return } defer tar.Close() err = dart.NewClientDefault().GetPackageTar(name, version, tar) if err != nil { log.Println("Error downloading Dart package", err) return } tar.Close() // extract the contents var dir = filepath.Join(tmp, name) os.MkdirAll(dir, 0777) err = exec.Command("tar", "xf", tar.Name(), "-C", dir).Run() if err != nil { log.Println("Error extracting Dart package", err) return } // create an instance of the Docker builder var script = script.Generate(dir) var builder = build.New(d.docker) builder.Build = script builder.Build.Image = imageName builder.Stdout = &buf builder.Timeout = time.Duration(900) * time.Second builder.Repo = &repo.Repo{ Path: dir, Name: name, Branch: version, Dir: filepath.Join("/var/cache/drone/src", name), } log.Println("executing build", name, version) // run the build err = builder.Run() log.Println("finished executing build", name, version) // update the build status based on the results // from the build runner. if err != nil { buf.WriteString(err.Error()) log.Println(err) } if builder.BuildState == nil { builder.BuildState = &build.BuildState{} builder.BuildState.ExitCode = 1 builder.BuildState.Finished = time.Now().UTC().Unix() builder.BuildState.Started = time.Now().UTC().Unix() } // insert the test output into the blobstore var blobkey = filepath.Join( r.Build.Name, r.Build.Version, r.Build.Channel, r.Build.SDK, ) log.Println("archiving build output", name, version) if err := blobstore.Put(c, blobkey, buf.Bytes()); err != nil { log.Println(err) return } log.Println("finished archiving build output", name, version) // update the build in the datastore r.Build.Status = resource.StatusSuccess r.Build.Start = builder.BuildState.Started r.Build.Finish = builder.BuildState.Finished if builder.BuildState.ExitCode != 0 { r.Build.Status = resource.StatusFailure } else if len(script.Script) == 4 { // TODO put default commands in an array, so we don't need to hard code r.Build.Status = resource.StatusWarning } log.Println("persisting build results", name, version) if err := datastore.PostBuild(c, r.Build); err != nil { log.Println(err) return } log.Println("completed build", name, version, "\tEXIT:", builder.BuildState.ExitCode) }