Example #1
0
// Helper method for saving a failed build or commit in the case where it never starts to build.
// This can happen if the yaml is bad or doesn't exist.
func saveFailedBuild(commit *Commit, msg string) error {

	// Set the commit to failed
	commit.Status = "Failure"
	commit.Created = time.Now().UTC()
	commit.Finished = commit.Created
	commit.Duration = 0
	if err := database.SaveCommit(commit); err != nil {
		return err
	}

	// save the build to the database
	build := &Build{}
	build.Slug = "1" // TODO: This should not be hardcoded
	build.CommitID = commit.ID
	build.Created = time.Now().UTC()
	build.Finished = build.Created
	commit.Duration = 0
	build.Status = "Failure"
	build.Stdout = msg
	if err := database.SaveBuild(build); err != nil {
		return err
	}

	// TODO: Should the status be Error instead of Failure?

	// TODO: Do we need to update the branch table too?

	return nil

}
Example #2
0
func TestSaveBbuild(t *testing.T) {
	Setup()
	defer Teardown()

	// get the build we plan to update
	build, err := database.GetBuild(1)
	if err != nil {
		t.Error(err)
	}

	// update fields
	build.Status = "Failing"

	// update the database
	if err := database.SaveBuild(build); err != nil {
		t.Error(err)
	}

	// get the updated build
	updatedBuild, err := database.GetBuild(1)
	if err != nil {
		t.Error(err)
	}

	if build.ID != updatedBuild.ID {
		t.Errorf("Exepected ID %d, got %d", updatedBuild.ID, build.ID)
	}

	if build.Slug != updatedBuild.Slug {
		t.Errorf("Exepected Slug %s, got %s", updatedBuild.Slug, build.Slug)
	}

	if build.Status != updatedBuild.Status {
		t.Errorf("Exepected Status %s, got %s", updatedBuild.Status, build.Status)
	}
}
Example #3
0
// execute will execute the build task and persist
// the results to the datastore.
func (w *worker) execute(task *BuildTask) error {
	// we need to be sure that we can recover
	// from any sort panic that could occur
	// to avoid brining down the entire application
	defer func() {
		if e := recover(); e != nil {
			task.Build.Finished = time.Now().UTC()
			task.Commit.Finished = time.Now().UTC()
			task.Build.Duration = task.Build.Finished.Unix() - task.Build.Started.Unix()
			task.Commit.Duration = task.Build.Finished.Unix() - task.Build.Started.Unix()
			task.Commit.Status = "Error"
			task.Build.Status = "Error"
			database.SaveBuild(task.Build)
			database.SaveCommit(task.Commit)
		}
	}()

	// update commit and build status
	task.Commit.Status = "Started"
	task.Build.Status = "Started"
	task.Build.Started = time.Now().UTC()
	task.Commit.Started = time.Now().UTC()

	// persist the commit to the database
	if err := database.SaveCommit(task.Commit); err != nil {
		return err
	}

	// persist the build to the database
	if err := database.SaveBuild(task.Build); err != nil {
		return err
	}

	// get settings
	settings, _ := database.GetSettings()

	// notification context
	context := &notify.Context{
		Repo:   task.Repo,
		Commit: task.Commit,
		Host:   settings.URL().String(),
	}

	// send all "started" notifications
	if task.Script.Notifications != nil {
		task.Script.Notifications.Send(context)
	}

	// Send "started" notification to Github
	if err := updateGitHubStatus(task.Repo, task.Commit); err != nil {
		log.Printf("error updating github status: %s\n", err.Error())
	}

	// make sure a channel exists for the repository,
	// the commit, and the commit output (TODO)
	reposlug := fmt.Sprintf("%s/%s/%s", task.Repo.Host, task.Repo.Owner, task.Repo.Name)
	commitslug := fmt.Sprintf("%s/%s/%s/commit/%s", task.Repo.Host, task.Repo.Owner, task.Repo.Name, task.Commit.Hash)
	consoleslug := fmt.Sprintf("%s/%s/%s/commit/%s/builds/%s", task.Repo.Host, task.Repo.Owner, task.Repo.Name, task.Commit.Hash, task.Build.Slug)
	channel.Create(reposlug)
	channel.Create(commitslug)
	channel.CreateStream(consoleslug)

	// notify the channels that the commit and build started
	channel.SendJSON(reposlug, task.Commit)
	channel.SendJSON(commitslug, task.Build)

	var buf = &bufferWrapper{channel: consoleslug}

	// append private parameters to the environment
	// variable section of the .drone.yml file, iff
	// this is not a pull request (for security purposes)
	if task.Repo.Params != nil && len(task.Commit.PullRequest) == 0 {
		for k, v := range task.Repo.Params {
			task.Script.Env = append(task.Script.Env, k+"="+v)
		}
	}

	defer func() {
		// update the status of the commit using the
		// GitHub status API.
		if err := updateGitHubStatus(task.Repo, task.Commit); err != nil {
			log.Printf("error updating github status: %s\n", err.Error())
		}
	}()

	// execute the build
	passed, buildErr := w.runBuild(task, buf)

	task.Build.Finished = time.Now().UTC()
	task.Commit.Finished = time.Now().UTC()
	task.Build.Duration = task.Build.Finished.UnixNano() - task.Build.Started.UnixNano()
	task.Commit.Duration = task.Build.Finished.UnixNano() - task.Build.Started.UnixNano()
	task.Commit.Status = "Success"
	task.Build.Status = "Success"
	task.Build.Stdout = buf.buf.String()

	// if exit code != 0 set to failure
	if passed {
		task.Commit.Status = "Failure"
		task.Build.Status = "Failure"
		if buildErr != nil && task.Build.Stdout == "" {
			// TODO: If you wanted to have very friendly error messages, you could do that here
			task.Build.Stdout = buildErr.Error() + "\n"
		}
	}

	// persist the build to the database
	if err := database.SaveBuild(task.Build); err != nil {
		return err
	}

	// persist the commit to the database
	if err := database.SaveCommit(task.Commit); err != nil {
		return err
	}

	// notify the channels that the commit and build finished
	channel.SendJSON(reposlug, task.Commit)
	channel.SendJSON(commitslug, task.Build)
	channel.Close(consoleslug)

	// send all "finished" notifications
	if task.Script.Notifications != nil {
		task.Script.Notifications.Send(context)
	}

	return nil
}
Example #4
0
// Processes a generic POST-RECEIVE hook and
// attempts to trigger a build.
func Hook(w http.ResponseWriter, r *http.Request) error {

	// if this is a pull request route
	// to a different handler
	if r.Header.Get("X-Github-Event") == "pull_request" {
		PullRequestHook(w, r)
		return nil
	}

	// get the payload of the message
	// this should contain a json representation of the
	// repository and commit details
	payload := r.FormValue("payload")

	// parse the github Hook payload
	hook, err := github.ParseHook([]byte(payload))
	if err != nil {
		println("could not parse hook")
		return RenderText(w, http.StatusText(http.StatusBadRequest), http.StatusBadRequest)
	}

	// make sure this is being triggered because of a commit
	// and not something like a tag deletion or whatever
	if hook.IsTag() || hook.IsGithubPages() ||
		hook.IsHead() == false || hook.IsDeleted() {
		return RenderText(w, http.StatusText(http.StatusOK), http.StatusOK)
	}

	// get the repo from the URL
	repoId := r.FormValue("id")

	// get the repo from the database, return error if not found
	repo, err := database.GetRepoSlug(repoId)
	if err != nil {
		return RenderText(w, http.StatusText(http.StatusNotFound), http.StatusNotFound)
	}

	// Get the user that owns the repository
	user, err := database.GetUser(repo.UserID)
	if err != nil {
		return RenderText(w, http.StatusText(http.StatusBadRequest), http.StatusBadRequest)
	}

	// Verify that the commit doesn't already exist.
	// We should never build the same commit twice.
	_, err = database.GetCommitHash(hook.Head.Id, repo.ID)
	if err != nil && err != sql.ErrNoRows {
		println("commit already exists")
		return RenderText(w, http.StatusText(http.StatusBadGateway), http.StatusBadGateway)
	}

	// we really only need:
	//  * repo owner
	//  * repo name
	//  * repo host (github)
	//  * commit hash
	//  * commit timestamp
	//  * commit branch
	//  * commit message
	//  * commit author
	//  * pull request

	// once we have this data we could just send directly to the queue
	// and let it handle everything else

	commit := &Commit{}
	commit.RepoID = repo.ID
	commit.Branch = hook.Branch()
	commit.Hash = hook.Head.Id
	commit.Status = "Pending"
	commit.Created = time.Now().UTC()

	// extract the author and message from the commit
	// this is kind of experimental, since I don't know
	// what I'm doing here.
	if hook.Head != nil && hook.Head.Author != nil {
		commit.Message = hook.Head.Message
		commit.Timestamp = hook.Head.Timestamp
		commit.SetAuthor(hook.Head.Author.Email)
	} else if hook.Commits != nil && len(hook.Commits) > 0 && hook.Commits[0].Author != nil {
		commit.Message = hook.Commits[0].Message
		commit.Timestamp = hook.Commits[0].Timestamp
		commit.SetAuthor(hook.Commits[0].Author.Email)
	}

	// get the drone.yml file from GitHub
	client := github.New(user.GithubToken)
	content, err := client.Contents.FindRef(repo.Owner, repo.Name, ".drone.yml", commit.Hash)
	if err != nil {
		msg := "No .drone.yml was found in this repository.  You need to add one.\n"
		if err := saveFailedBuild(commit, msg); err != nil {
			return RenderText(w, http.StatusText(http.StatusInternalServerError), http.StatusInternalServerError)
		}
		return RenderText(w, http.StatusText(http.StatusBadRequest), http.StatusBadRequest)
	}

	// decode the content.  Note: Not sure this will ever happen...it basically means a GitHub API issue
	raw, err := content.DecodeContent()
	if err != nil {
		msg := "Could not decode the yaml from GitHub.  Check that your .drone.yml is a valid yaml file.\n"
		if err := saveFailedBuild(commit, msg); err != nil {
			return RenderText(w, http.StatusText(http.StatusInternalServerError), http.StatusInternalServerError)
		}
		return RenderText(w, http.StatusText(http.StatusBadRequest), http.StatusBadRequest)
	}

	// parse the build script
	buildscript, err := script.ParseBuild(raw)
	if err != nil {
		msg := "Could not parse your .drone.yml file.  It needs to be a valid drone yaml file.\n\n" + err.Error() + "\n"
		if err := saveFailedBuild(commit, msg); err != nil {
			return RenderText(w, http.StatusText(http.StatusInternalServerError), http.StatusInternalServerError)
		}
		return RenderText(w, http.StatusText(http.StatusBadRequest), http.StatusBadRequest)
	}

	// save the commit to the database
	if err := database.SaveCommit(commit); err != nil {
		return RenderText(w, http.StatusText(http.StatusInternalServerError), http.StatusInternalServerError)
	}

	// save the build to the database
	build := &Build{}
	build.Slug = "1" // TODO
	build.CommitID = commit.ID
	build.Created = time.Now().UTC()
	build.Status = "Pending"
	if err := database.SaveBuild(build); err != nil {
		return RenderText(w, http.StatusText(http.StatusInternalServerError), http.StatusInternalServerError)
	}

	// notify websocket that a new build is pending
	//realtime.CommitPending(repo.UserID, repo.TeamID, repo.ID, commit.ID, repo.Private)
	//realtime.BuildPending(repo.UserID, repo.TeamID, repo.ID, commit.ID, build.ID, repo.Private)

	queue.Add(&queue.BuildTask{Repo: repo, Commit: commit, Build: build, Script: buildscript}) //Push(repo, commit, build, buildscript)

	// OK!
	return RenderText(w, http.StatusText(http.StatusOK), http.StatusOK)
}
Example #5
0
func PullRequestHook(w http.ResponseWriter, r *http.Request) {

	// get the payload of the message
	// this should contain a json representation of the
	// repository and commit details
	payload := r.FormValue("payload")

	println("GOT PR HOOK")
	println(payload)

	hook, err := github.ParsePullRequestHook([]byte(payload))
	if err != nil {
		RenderText(w, http.StatusText(http.StatusBadRequest), http.StatusBadRequest)
		return
	}

	// ignore these
	if hook.Action != "opened" && hook.Action != "synchronize" {
		RenderText(w, http.StatusText(http.StatusOK), http.StatusOK)
		return
	}

	// get the repo from the URL
	repoId := r.FormValue("id")

	// get the repo from the database, return error if not found
	repo, err := database.GetRepoSlug(repoId)
	if err != nil {
		RenderText(w, http.StatusText(http.StatusNotFound), http.StatusNotFound)
		return
	}

	// Get the user that owns the repository
	user, err := database.GetUser(repo.UserID)
	if err != nil {
		RenderText(w, http.StatusText(http.StatusBadRequest), http.StatusBadRequest)
		return
	}

	// Verify that the commit doesn't already exist.
	// We should enver build the same commit twice.
	_, err = database.GetCommitHash(hook.PullRequest.Head.Sha, repo.ID)
	if err != nil && err != sql.ErrNoRows {
		RenderText(w, http.StatusText(http.StatusBadGateway), http.StatusBadGateway)
		return
	}

	///////////////////////////////////////////////////////

	commit := &Commit{}
	commit.RepoID = repo.ID
	commit.Branch = hook.PullRequest.Head.Ref
	commit.Hash = hook.PullRequest.Head.Sha
	commit.Status = "Pending"
	commit.Created = time.Now().UTC()
	commit.Gravatar = hook.PullRequest.User.GravatarId
	commit.PullRequest = strconv.Itoa(hook.Number)
	commit.Message = hook.PullRequest.Title
	// label := p.PullRequest.Head.Labe

	// get the drone.yml file from GitHub
	client := github.New(user.GithubToken)
	content, err := client.Contents.FindRef(repo.Owner, repo.Name, ".drone.yml", commit.Hash) // TODO should this really be the hash??
	if err != nil {
		println(err.Error())
		RenderText(w, http.StatusText(http.StatusBadRequest), http.StatusBadRequest)
		return
	}

	// decode the content
	raw, err := content.DecodeContent()
	if err != nil {
		RenderText(w, http.StatusText(http.StatusBadRequest), http.StatusBadRequest)
		return
	}

	// parse the build script
	buildscript, err := script.ParseBuild(raw)
	if err != nil {
		// TODO if the YAML is invalid we should create a commit record
		// with an ERROR status so that the user knows why a build wasn't
		// triggered in the system
		RenderText(w, http.StatusText(http.StatusBadRequest), http.StatusBadRequest)
		return
	}

	// save the commit to the database
	if err := database.SaveCommit(commit); err != nil {
		RenderText(w, http.StatusText(http.StatusInternalServerError), http.StatusInternalServerError)
		return
	}

	// save the build to the database
	build := &Build{}
	build.Slug = "1" // TODO
	build.CommitID = commit.ID
	build.Created = time.Now().UTC()
	build.Status = "Pending"
	if err := database.SaveBuild(build); err != nil {
		RenderText(w, http.StatusText(http.StatusInternalServerError), http.StatusInternalServerError)
		return
	}

	// notify websocket that a new build is pending
	// TODO we should, for consistency, just put this inside Queue.Add()
	queue.Add(&queue.BuildTask{Repo: repo, Commit: commit, Build: build, Script: buildscript})

	// OK!
	RenderText(w, http.StatusText(http.StatusOK), http.StatusOK)
}
Example #6
0
// execute will execute the build task and persist
// the results to the datastore.
func (b *BuildTask) execute() error {
	// we need to be sure that we can recover
	// from any sort panic that could occur
	// to avoid brining down the entire application
	defer func() {
		if e := recover(); e != nil {
			b.Build.Finished = time.Now().UTC()
			b.Commit.Finished = time.Now().UTC()
			b.Build.Duration = b.Build.Finished.Unix() - b.Build.Started.Unix()
			b.Commit.Duration = b.Build.Finished.Unix() - b.Build.Started.Unix()
			b.Commit.Status = "Error"
			b.Build.Status = "Error"
			database.SaveBuild(b.Build)
			database.SaveCommit(b.Commit)
		}
	}()

	// update commit and build status
	b.Commit.Status = "Started"
	b.Build.Status = "Started"
	b.Build.Started = time.Now().UTC()
	b.Commit.Started = time.Now().UTC()

	// persist the commit to the database
	if err := database.SaveCommit(b.Commit); err != nil {
		return err
	}

	// persist the build to the database
	if err := database.SaveBuild(b.Build); err != nil {
		return err
	}

	// get settings
	settings, _ := database.GetSettings()

	// notification context
	context := &notification.Context{
		Repo:   b.Repo,
		Commit: b.Commit,
		Host:   settings.URL().String(),
	}

	// send all "started" notifications
	if b.Script.Notifications != nil {
		b.Script.Notifications.Send(context)
	}

	// make sure a channel exists for the repository,
	// the commit, and the commit output (TODO)
	reposlug := fmt.Sprintf("%s/%s/%s", b.Repo.Host, b.Repo.Owner, b.Repo.Name)
	commitslug := fmt.Sprintf("%s/%s/%s/commit/%s", b.Repo.Host, b.Repo.Owner, b.Repo.Name, b.Commit.Hash)
	consoleslug := fmt.Sprintf("%s/%s/%s/commit/%s/builds/%s", b.Repo.Host, b.Repo.Owner, b.Repo.Name, b.Commit.Hash, b.Build.Slug)
	channel.Create(reposlug)
	channel.Create(commitslug)
	channel.CreateStream(consoleslug)

	// notify the channels that the commit and build started
	channel.SendJSON(reposlug, b.Commit)
	channel.SendJSON(commitslug, b.Build)

	var buf = &bufferWrapper{channel: consoleslug}

	// append private parameters to the environment
	// variable section of the .drone.yml file
	if b.Repo.Params != nil {
		for k, v := range b.Repo.Params {
			b.Script.Env = append(b.Script.Env, k+"="+v)
		}
	}

	// execute the build
	builder := bldr.Builder{}
	builder.Build = b.Script
	builder.Repo = &r.Repo{Path: b.Repo.URL, Branch: b.Commit.Branch, Commit: b.Commit.Hash, PR: b.Commit.PullRequest, Dir: filepath.Join("/var/cache/drone/src", b.Repo.Slug)}
	builder.Key = []byte(b.Repo.PrivateKey)
	builder.Stdout = buf
	builder.Timeout = 300 * time.Minute

	defer func() {
		// update the status of the commit using the
		// GitHub status API.
		if err := updateGitHubStatus(b.Repo, b.Commit); err != nil {
			log.Printf("error updating github status: %s\n", err.Error())
		}
	}()

	buildErr := builder.Run()

	b.Build.Finished = time.Now().UTC()
	b.Commit.Finished = time.Now().UTC()
	b.Build.Duration = b.Build.Finished.UnixNano() - b.Build.Started.UnixNano()
	b.Commit.Duration = b.Build.Finished.UnixNano() - b.Build.Started.UnixNano()
	b.Commit.Status = "Success"
	b.Build.Status = "Success"
	b.Build.Stdout = buf.buf.String()

	// if exit code != 0 set to failure
	if builder.BuildState == nil || builder.BuildState.ExitCode != 0 {
		b.Commit.Status = "Failure"
		b.Build.Status = "Failure"
		if buildErr != nil && b.Build.Stdout == "" {
			// TODO: If you wanted to have very friendly error messages, you could do that here
			b.Build.Stdout = buildErr.Error() + "\n"
		}
	}

	// persist the build to the database
	if err := database.SaveBuild(b.Build); err != nil {
		return err
	}

	// persist the commit to the database
	if err := database.SaveCommit(b.Commit); err != nil {
		return err
	}

	// notify the channels that the commit and build finished
	channel.SendJSON(reposlug, b.Commit)
	channel.SendJSON(commitslug, b.Build)
	channel.Close(consoleslug)

	// add the smtp address to the notificaitons
	//if b.Script.Notifications != nil && b.Script.Notifications.Email != nil {
	//	b.Script.Notifications.Email.SetServer(settings.SmtpServer, settings.SmtpPort,
	//		settings.SmtpUsername, settings.SmtpPassword, settings.SmtpAddress)
	//}

	// send all "finished" notifications
	if b.Script.Notifications != nil {
		b.sendEmail(context) // send email from queue, not from inside /build/script package
		b.Script.Notifications.Send(context)
	}

	return nil
}
Example #7
0
// Processes a generic POST-RECEIVE Bitbucket hook and
// attempts to trigger a build.
func (h *BitbucketHandler) Hook(w http.ResponseWriter, r *http.Request) error {
	// get the payload from the request
	payload := r.FormValue("payload")

	// parse the post-commit hook
	hook, err := bitbucket.ParseHook([]byte(payload))
	if err != nil {
		return err
	}

	// get the repo from the URL
	repoId := r.FormValue("id")

	// get the repo from the database, return error if not found
	repo, err := database.GetRepoSlug(repoId)
	if err != nil {
		return RenderText(w, http.StatusText(http.StatusNotFound), http.StatusNotFound)
	}

	// Get the user that owns the repository
	user, err := database.GetUser(repo.UserID)
	if err != nil {
		return RenderText(w, http.StatusText(http.StatusBadRequest), http.StatusBadRequest)
	}

	// Verify that the commit doesn't already exist.
	// We should never build the same commit twice.
	_, err = database.GetCommitHash(hook.Commits[len(hook.Commits)-1].Hash, repo.ID)
	if err != nil && err != sql.ErrNoRows {
		return RenderText(w, http.StatusText(http.StatusBadGateway), http.StatusBadGateway)
	}

	commit := &Commit{}
	commit.RepoID = repo.ID
	commit.Branch = hook.Commits[len(hook.Commits)-1].Branch
	commit.Hash = hook.Commits[len(hook.Commits)-1].Hash
	commit.Status = "Pending"
	commit.Created = time.Now().UTC()
	commit.Message = hook.Commits[len(hook.Commits)-1].Message
	commit.Timestamp = time.Now().UTC().String()
	commit.SetAuthor(hook.Commits[len(hook.Commits)-1].Author)

	// get the github settings from the database
	settings := database.SettingsMust()

	// create the Bitbucket client
	client := bitbucket.New(
		settings.BitbucketKey,
		settings.BitbucketSecret,
		user.BitbucketToken,
		user.BitbucketSecret,
	)

	// get the yaml from the database
	raw, err := client.Sources.Find(repo.Owner, repo.Name, commit.Hash, ".drone.yml")
	if err != nil {
		return RenderText(w, http.StatusText(http.StatusNotFound), http.StatusNotFound)
	}

	// parse the build script
	buildscript, err := script.ParseBuild([]byte(raw.Data), repo.Params)
	if err != nil {
		msg := "Could not parse your .drone.yml file.  It needs to be a valid drone yaml file.\n\n" + err.Error() + "\n"
		if err := saveFailedBuild(commit, msg); err != nil {
			return RenderText(w, http.StatusText(http.StatusInternalServerError), http.StatusInternalServerError)
		}
		return RenderText(w, http.StatusText(http.StatusBadRequest), http.StatusBadRequest)
	}

	// save the commit to the database
	if err := database.SaveCommit(commit); err != nil {
		return RenderText(w, http.StatusText(http.StatusInternalServerError), http.StatusInternalServerError)
	}

	// save the build to the database
	build := &Build{}
	build.Slug = "1" // TODO
	build.CommitID = commit.ID
	build.Created = time.Now().UTC()
	build.Status = "Pending"
	if err := database.SaveBuild(build); err != nil {
		return RenderText(w, http.StatusText(http.StatusInternalServerError), http.StatusInternalServerError)
	}

	// send the build to the queue
	h.queue.Add(&queue.BuildTask{Repo: repo, Commit: commit, Build: build, Script: buildscript})

	// OK!
	return RenderText(w, http.StatusText(http.StatusOK), http.StatusOK)
}
Example #8
0
func (g *GitlabHandler) PullRequestHook(p *gogitlab.HookPayload, repo *Repo, user *User) error {
	obj := p.ObjectAttributes

	// Gitlab may trigger multiple hooks upon updating merge requests status
	// only build when it was just opened and the merge hasn't been checked yet.
	if !(obj.State == "opened" && obj.MergeStatus == "unchecked") {
		fmt.Println("Ignore GitLab Merge Requests")
		return nil
	}

	settings := database.SettingsMust()

	client := gogitlab.NewGitlab(settings.GitlabApiUrl, g.apiPath, user.GitlabToken)

	// GitLab merge-requests hook doesn't include repository data.
	// Have to fetch it manually
	src, err := client.RepoBranch(strconv.Itoa(obj.SourceProjectId), obj.SourceBranch)
	if err != nil {
		return err
	}

	_, err = database.GetCommitHash(src.Commit.Id, repo.ID)
	if err != nil && err != sql.ErrNoRows {
		fmt.Println("commit already exists")
		return err
	}

	commit := &Commit{}
	commit.RepoID = repo.ID
	commit.Branch = src.Name
	commit.Hash = src.Commit.Id
	commit.Status = "Pending"
	commit.Created = time.Now().UTC()
	commit.PullRequest = strconv.Itoa(obj.IId)

	commit.Message = src.Commit.Message
	commit.Timestamp = src.Commit.AuthoredDateRaw
	commit.SetAuthor(src.Commit.Author.Email)

	buildscript, err := client.RepoRawFile(strconv.Itoa(obj.SourceProjectId), commit.Hash, ".drone.yml")
	if err != nil {
		msg := "No .drone.yml was found in this repository.  You need to add one.\n"
		if err := saveFailedBuild(commit, msg); err != nil {
			return fmt.Errorf("Failed to save build: %q", err)
		}
		return fmt.Errorf("Error to fetch build script: %q", err)
	}

	// save the commit to the database
	if err := database.SaveCommit(commit); err != nil {
		return fmt.Errorf("Failed to save commit: %q", err)
	}

	// save the build to the database
	build := &Build{}
	build.Slug = "1" // TODO
	build.CommitID = commit.ID
	build.Created = time.Now().UTC()
	build.Status = "Pending"
	build.BuildScript = string(buildscript)
	if err := database.SaveBuild(build); err != nil {
		return fmt.Errorf("Failed to save build: %q", err)
	}

	g.queue.Add(&queue.BuildTask{Repo: repo, Commit: commit, Build: build})

	return nil
}
Example #9
0
func (g *GitlabHandler) Hook(w http.ResponseWriter, r *http.Request) error {
	rID := r.FormValue("id")
	repo, err := database.GetRepoSlug(rID)
	if err != nil {
		return RenderText(w, http.StatusText(http.StatusNotFound), http.StatusNotFound)
	}

	user, err := database.GetUser(repo.UserID)
	if err != nil {
		return RenderText(w, http.StatusText(http.StatusNotFound), http.StatusNotFound)
	}

	payload, _ := ioutil.ReadAll(r.Body)
	parsed, err := gogitlab.ParseHook(payload)
	if err != nil {
		return err
	}
	if parsed.ObjectKind == "merge_request" {
		fmt.Println(string(payload))
		if err := g.PullRequestHook(parsed, repo, user); err != nil {
			return err
		}
		return RenderText(w, http.StatusText(http.StatusOK), http.StatusOK)
	}

	if len(parsed.After) == 0 {
		return RenderText(w, http.StatusText(http.StatusNotFound), http.StatusNotFound)
	}

	_, err = database.GetCommitHash(parsed.After, repo.ID)
	if err != nil && err != sql.ErrNoRows {
		fmt.Println("commit already exists")
		return RenderText(w, http.StatusText(http.StatusBadGateway), http.StatusBadGateway)
	}

	commit := &Commit{}
	commit.RepoID = repo.ID
	commit.Branch = parsed.Branch()
	commit.Hash = parsed.After
	commit.Status = "Pending"
	commit.Created = time.Now().UTC()

	head := parsed.Head()
	commit.Message = head.Message
	commit.Timestamp = head.Timestamp
	if head.Author != nil {
		commit.SetAuthor(head.Author.Email)
	} else {
		commit.SetAuthor(parsed.UserName)
	}

	// get the github settings from the database
	settings := database.SettingsMust()

	// get the drone.yml file from GitHub
	client := gogitlab.NewGitlab(settings.GitlabApiUrl, g.apiPath, user.GitlabToken)

	buildscript, err := client.RepoRawFile(ns(repo.Owner, repo.Name), commit.Hash, ".drone.yml")
	if err != nil {
		msg := "No .drone.yml was found in this repository.  You need to add one.\n"
		if err := saveFailedBuild(commit, msg); err != nil {
			return RenderText(w, http.StatusText(http.StatusInternalServerError), http.StatusInternalServerError)
		}
		return RenderText(w, http.StatusText(http.StatusBadRequest), http.StatusBadRequest)
	}

	// save the commit to the database
	if err := database.SaveCommit(commit); err != nil {
		return RenderText(w, http.StatusText(http.StatusInternalServerError), http.StatusInternalServerError)
	}

	// save the build to the database
	build := &Build{}
	build.Slug = "1" // TODO
	build.CommitID = commit.ID
	build.Created = time.Now().UTC()
	build.Status = "Pending"
	build.BuildScript = string(buildscript)
	if err := database.SaveBuild(build); err != nil {
		return RenderText(w, http.StatusText(http.StatusInternalServerError), http.StatusInternalServerError)
	}

	g.queue.Add(&queue.BuildTask{Repo: repo, Commit: commit, Build: build})

	// OK!
	return RenderText(w, http.StatusText(http.StatusOK), http.StatusOK)

}
Example #10
0
// Processes a generic POST-RECEIVE Gogs hook and
// attempts to trigger a build.
func (h *GogsHandler) Hook(w http.ResponseWriter, r *http.Request) error {

	defer r.Body.Close()
	payloadbytes, err := ioutil.ReadAll(r.Body)
	if err != nil {
		println(err.Error())
		return RenderText(w, http.StatusText(http.StatusBadRequest), http.StatusBadRequest)
	}
	fmt.Printf("body is => %s\n", string(payloadbytes))

	payload, err := ParseHook(payloadbytes)
	if err != nil {
		println(err.Error())
		return RenderText(w, http.StatusText(http.StatusBadRequest), http.StatusBadRequest)
	}
	fmt.Printf("payload parsed\n")

	// Verify that the commit doesn't already exist.
	// We should never build the same commit twice.
	_, err = database.GetCommitHash(payload.Commits[0].Id, payload.Repo.Id)
	if err != nil && err != sql.ErrNoRows {
		return RenderText(w, http.StatusText(http.StatusBadGateway), http.StatusBadGateway)
	}
	fmt.Printf("commit hash checked\n")

	// Save repo to the database if needed
	var urlParts = strings.Split(payload.Repo.Url, "/")

	repo, err := setupRepo(urlParts, payload)
	if err != nil {
		println(err.Error())
		return RenderText(w, http.StatusText(http.StatusBadRequest), http.StatusBadRequest)
	}

	commit := &Commit{}
	commit.RepoID = repo.ID
	commit.Branch = payload.Branch()
	commit.Hash = payload.Commits[0].Id
	commit.Status = "Pending"
	commit.Created = time.Now().UTC()

	commit.Message = payload.Commits[0].Message
	commit.Timestamp = time.Now().UTC().String()
	commit.SetAuthor(payload.Commits[0].Author.Name)
	fmt.Printf("commit struct created\n")

	// save the commit to the database
	if err := database.SaveCommit(commit); err != nil {
		return RenderText(w, http.StatusText(http.StatusInternalServerError), http.StatusInternalServerError)
	}
	fmt.Printf("commit struct saved\n")

	var service_endpoint = urlParts[2]
	if os.Getenv("GOGS_URL") != "" {
		service_endpoint = os.Getenv("GOGS_URL")
	}
	// GET .drone.yml file
	var droneYmlUrl = fmt.Sprintf(droneYmlUrlPattern, service_endpoint, urlParts[3], urlParts[4], commit.Hash)
	println("droneYmlUrl is ", droneYmlUrl)
	ymlGetResponse, err := http.Get(droneYmlUrl)
	var buildYml = ""
	if err != nil {
		println(err.Error())
		return RenderText(w, http.StatusText(http.StatusInternalServerError), http.StatusInternalServerError)
	} else {
		defer ymlGetResponse.Body.Close()
		yml, err := ioutil.ReadAll(ymlGetResponse.Body)
		if err != nil {
			println(err.Error())
			return RenderText(w, http.StatusText(http.StatusInternalServerError), http.StatusInternalServerError)
		}
		buildYml = string(yml)
		println("yml from http get: ", buildYml)
	}

	// parse the build script
	var repoParams = map[string]string{}
	println("parsing yml")
	buildscript, err := script.ParseBuild([]byte(buildYml), repoParams)
	if err != nil {
		msg := "Could not parse your .drone.yml file.  It needs to be a valid drone yaml file.\n\n" + err.Error() + "\n"
		if err := saveFailedBuild(commit, msg); err != nil {
			return RenderText(w, http.StatusText(http.StatusInternalServerError), http.StatusInternalServerError)
		}
		return RenderText(w, http.StatusText(http.StatusBadRequest), http.StatusBadRequest)
	}
	fmt.Printf("build script parsed\n")

	// save the build to the database
	build := &Build{}
	build.Slug = "1" // TODO
	build.CommitID = commit.ID
	build.Created = time.Now().UTC()
	build.Status = "Pending"
	if err := database.SaveBuild(build); err != nil {
		return RenderText(w, http.StatusText(http.StatusInternalServerError), http.StatusInternalServerError)
	}
	println("build saved")

	// send the build to the queue
	h.queue.Add(&queue.BuildTask{Repo: repo, Commit: commit, Build: build, Script: buildscript})
	fmt.Printf("build task added to queue\n")

	// OK!
	return RenderText(w, http.StatusText(http.StatusOK), http.StatusOK)
}
Example #11
0
func Setup() {
	// create an in-memory database
	database.Init("sqlite3", ":memory:")

	// create dummy user data
	user1 := User{
		Password: "******",
		Name:     "Brad Rydzewski",
		Email:    "*****@*****.**",
		Gravatar: "8c58a0be77ee441bb8f8595b7f1b4e87",
		Token:    "123",
		Admin:    true}
	user2 := User{
		Password: "******",
		Name:     "Thomas Burke",
		Email:    "*****@*****.**",
		Gravatar: "c62f7126273f7fa786274274a5dec8ce",
		Token:    "456",
		Admin:    false}
	user3 := User{
		Password: "******",
		Name:     "Carlos Morales",
		Email:    "*****@*****.**",
		Gravatar: "c2180a539620d90d68eaeb848364f1c2",
		Token:    "789",
		Admin:    false}

	database.SaveUser(&user1)
	database.SaveUser(&user2)
	database.SaveUser(&user3)

	// create dummy team data
	team1 := Team{
		Slug:     "drone",
		Name:     "Drone",
		Email:    "*****@*****.**",
		Gravatar: "8c58a0be77ee441bb8f8595b7f1b4e87"}
	team2 := Team{
		Slug:     "github",
		Name:     "Github",
		Email:    "*****@*****.**",
		Gravatar: "61024896f291303615bcd4f7a0dcfb74"}
	team3 := Team{
		Slug:     "golang",
		Name:     "Golang",
		Email:    "*****@*****.**",
		Gravatar: "991695cc770c6b8354b68cd18c280b95"}

	database.SaveTeam(&team1)
	database.SaveTeam(&team2)
	database.SaveTeam(&team3)

	// create team membership data
	database.SaveMember(user1.ID, team1.ID, RoleOwner)
	database.SaveMember(user2.ID, team1.ID, RoleAdmin)
	database.SaveMember(user3.ID, team1.ID, RoleWrite)
	database.SaveMember(user1.ID, team2.ID, RoleOwner)
	database.SaveMember(user2.ID, team2.ID, RoleAdmin)
	database.SaveMember(user3.ID, team2.ID, RoleWrite)
	database.SaveMember(user1.ID, team3.ID, RoleRead)

	// create dummy repo data
	repo1 := Repo{
		Slug:       "github.com/drone/drone",
		Host:       "github.com",
		Owner:      "drone",
		Name:       "drone",
		Private:    true,
		Disabled:   false,
		SCM:        "git",
		URL:        "[email protected]:drone/drone.git",
		Username:   "******",
		Password:   "******",
		PublicKey:  "public key",
		PrivateKey: "private key",
		UserID:     user1.ID,
		TeamID:     team1.ID,
	}
	repo2 := Repo{
		Slug:       "bitbucket.org/drone/test",
		Host:       "bitbucket.org",
		Owner:      "drone",
		Name:       "test",
		Private:    false,
		Disabled:   false,
		SCM:        "hg",
		URL:        "https://bitbucket.org/drone/test",
		Username:   "******",
		Password:   "******",
		PublicKey:  "public key",
		PrivateKey: "private key",
		UserID:     user1.ID,
		TeamID:     team1.ID,
	}
	repo3 := Repo{
		Slug:       "bitbucket.org/brydzewski/test",
		Host:       "bitbucket.org",
		Owner:      "brydzewski",
		Name:       "test",
		Private:    false,
		Disabled:   false,
		SCM:        "hg",
		URL:        "https://bitbucket.org/brydzewski/test",
		Username:   "******",
		Password:   "******",
		PublicKey:  "public key",
		PrivateKey: "private key",
		UserID:     user2.ID,
	}

	database.SaveRepo(&repo1)
	database.SaveRepo(&repo2)
	database.SaveRepo(&repo3)

	commit1 := Commit{
		RepoID:   repo1.ID,
		Status:   "Success",
		Hash:     "4f4c4594be6d6ddbc1c0dd521334f7ecba92b608",
		Branch:   "master",
		Author:   user1.Email,
		Gravatar: user1.Gravatar,
		Message:  "commit message",
	}
	commit2 := Commit{
		RepoID:   repo1.ID,
		Status:   "Failure",
		Hash:     "0eb2fa13e9f4139e803b6ad37831708d4786c74a",
		Branch:   "master",
		Author:   user1.Email,
		Gravatar: user1.Gravatar,
		Message:  "commit message",
	}
	commit3 := Commit{
		RepoID:   repo1.ID,
		Status:   "Failure",
		Hash:     "60a7fe87ccf01d0152e53242528399e05acaf047",
		Branch:   "dev",
		Author:   user1.Email,
		Gravatar: user1.Gravatar,
		Message:  "commit message",
	}
	commit4 := Commit{
		RepoID:   repo2.ID,
		Status:   "Success",
		Hash:     "a4078d1e9a0842cdd214adbf0512578799a4f2ba",
		Branch:   "master",
		Author:   user1.Email,
		Gravatar: user1.Gravatar,
		Message:  "commit message",
	}

	// create dummy commit data
	database.SaveCommit(&commit1)
	database.SaveCommit(&commit2)
	database.SaveCommit(&commit3)
	database.SaveCommit(&commit4)

	// create dummy build data
	database.SaveBuild(&Build{CommitID: commit1.ID, Slug: "node_0.10", Status: "Success", Duration: 60})
	database.SaveBuild(&Build{CommitID: commit1.ID, Slug: "node_0.09", Status: "Success", Duration: 70})
	database.SaveBuild(&Build{CommitID: commit2.ID, Slug: "node_0.10", Status: "Success", Duration: 10})
	database.SaveBuild(&Build{CommitID: commit2.ID, Slug: "node_0.09", Status: "Failure", Duration: 65})
	database.SaveBuild(&Build{CommitID: commit3.ID, Slug: "node_0.10", Status: "Failure", Duration: 50})
	database.SaveBuild(&Build{CommitID: commit3.ID, Slug: "node_0.09", Status: "Failure", Duration: 55})
}