Ejemplo n.º 1
0
// 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
}
Ejemplo n.º 2
0
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 = &notify.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,
	})
}
Ejemplo n.º 3
0
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)
}