From 381c6d0782d8ba39f1fcd11787193b5692616363 Mon Sep 17 00:00:00 2001 From: Kpacha Date: Sun, 16 Aug 2015 18:35:33 +0200 Subject: [PATCH] send build status updates to gitlab as comments on the related commit --- plugin/notify/gitlab.go | 134 ++++++++++++++++++++++++++++++++++ plugin/notify/gitlab_test.go | 107 +++++++++++++++++++++++++++ plugin/notify/notification.go | 9 +++ 3 files changed, 250 insertions(+) create mode 100644 plugin/notify/gitlab.go create mode 100644 plugin/notify/gitlab_test.go diff --git a/plugin/notify/gitlab.go b/plugin/notify/gitlab.go new file mode 100644 index 000000000..92cb25c2c --- /dev/null +++ b/plugin/notify/gitlab.go @@ -0,0 +1,134 @@ +package notify + +import ( + "fmt" + "strconv" + + "github.com/Bugagazavr/go-gitlab-client" + "github.com/drone/drone/plugin/remote/gitlab" + "github.com/drone/drone/shared/model" +) + +type Gitlab struct { + SkipVerify bool `yaml:"skip_verify,omitempty"` + Started bool `yaml:"on_started,omitempty"` + Success bool `yaml:"on_success,omitempty"` + Failure bool `yaml:"on_failure,omitempty"` +} + +type GitlabClient interface { + SendRepoCommitComment(id string, sha string, body string) (*gogitlab.CommitComment, error) +} + +const ( + StatusPending = ":raised_hand:" + StatusSuccess = ":thumbsup:" + StatusFailure = ":thumbsdown:" + StatusError = ":exclamation:" +) + +const ( + DescPending = "this build is pending" + DescSuccess = "the build was successful" + DescFailure = "the build failed" + DescError = "oops, something went wrong" +) + +const ( + PRMasterBranch = "master" + PRBadToMerge = " -> bad to merge" + PRGoodToMerge = " -> good to merge" +) + +// Send uses the Gitlab repository API to comment the commit +func (g *Gitlab) Send(context *model.Request) error { + if !g.isRequested(context) { + return nil + } + + return g.send( + context, + gitlab.NewClient(fmt.Sprintf("http://%s", context.Repo.Host), context.User.Access, g.SkipVerify), + ) +} + +func (g *Gitlab) isRequested(context *model.Request) bool { + if context.Repo.Remote != model.RemoteGitlab { + return false + } + + switch context.Commit.Status { + case model.StatusStarted: + if !g.Started { + return false + } + case model.StatusSuccess: + if !g.Success { + return false + } + case model.StatusFailure, model.StatusError, model.StatusKilled: + if !g.Failure { + return false + } + } + + return true +} + +func (g *Gitlab) send(context *model.Request, client GitlabClient) error { + msg := fmt.Sprintf( + "[%s](%s) %s%s", + getDesc(context.Commit.Status), + getBuildUrl(context), + getStatus(context.Commit.Status), + getMergeRequestComment(context.Commit.Branch, context.Commit.Status), + ) + + _, err := client.SendRepoCommitComment(strconv.FormatInt(context.Repo.ID, 10), context.Commit.Sha, msg) + + return err +} + +// getStatus converts a Drone status to a Gitlab status. +func getStatus(status string) string { + switch status { + case model.StatusEnqueue, model.StatusStarted: + return StatusPending + case model.StatusSuccess: + return StatusSuccess + case model.StatusFailure: + return StatusFailure + case model.StatusError, model.StatusKilled: + return StatusError + default: + return StatusError + } +} + +// getDesc generates a description message for the comment based on the status. +func getDesc(status string) string { + switch status { + case model.StatusEnqueue, model.StatusStarted: + return DescPending + case model.StatusSuccess: + return DescSuccess + case model.StatusFailure: + return DescFailure + case model.StatusError, model.StatusKilled: + return DescError + default: + return DescError + } +} + +func getMergeRequestComment(branch, status string) string { + if branch != PRMasterBranch { + switch status { + case model.StatusSuccess: + return PRGoodToMerge + case model.StatusFailure: + return PRBadToMerge + } + } + return "" +} diff --git a/plugin/notify/gitlab_test.go b/plugin/notify/gitlab_test.go new file mode 100644 index 000000000..2e44add4a --- /dev/null +++ b/plugin/notify/gitlab_test.go @@ -0,0 +1,107 @@ +package notify + +import ( + "fmt" + "testing" + + "github.com/Bugagazavr/go-gitlab-client" + "github.com/drone/drone/shared/model" +) + +type MockGitlabClient struct { + Comment *gogitlab.CommitComment + id string + sha string + body string +} + +func (c *MockGitlabClient) SendRepoCommitComment(id string, sha string, body string) (*gogitlab.CommitComment, error) { + c.id = id + c.sha = sha + c.body = body + return c.Comment, nil +} + +var gitlabClient = &MockGitlabClient{} + +var gitlabSubject = &Gitlab{ + SkipVerify: false, + Started: true, + Success: true, + Failure: true, +} + +var gitlabRequest = &model.Request{ + Host: "http://examplehost.com", + Repo: &model.Repo{ + ID: 123456, + Host: "examplegit.com", + Owner: "owner", + Name: "repo", + Remote: model.RemoteGitlab, + }, + Commit: &model.Commit{ + Sha: "abc", + Branch: "example", + }, + User: &model.User{ + Access: "secret_token", + }, +} + +func Test_GitlabSendStarted(t *testing.T) { + gitlabRequest.Commit.Status = "Started" + + expected := gogitlab.CommitComment{ + Note: fmt.Sprintf("[this build is pending](%s) :raised_hand:", getBuildUrl(gitlabRequest)), + } + + gitlabClient.Comment = &expected + + err := gitlabSubject.send(gitlabRequest, gitlabClient) + if err != nil { + t.Errorf("Unexepected error: %s", err.Error()) + } + + if *gitlabClient.Comment != expected { + t.Errorf("Invalid gitlab payload. Expected: %v, got %v", expected, *gitlabClient.Comment) + } +} + +func Test_GitlabSendSuccess(t *testing.T) { + gitlabRequest.Commit.Status = "Success" + + expected := gogitlab.CommitComment{ + Note: fmt.Sprintf("[the build was successful](%s) :thumbsup: -> good to merge", getBuildUrl(gitlabRequest)), + } + + gitlabClient.Comment = &expected + + err := gitlabSubject.send(gitlabRequest, gitlabClient) + if err != nil { + t.Errorf("Unexepected error: %s", err.Error()) + } + + if *gitlabClient.Comment != expected { + t.Errorf("Invalid gitlab payload. Expected: %v, got %v", expected, *gitlabClient.Comment) + } +} + +func Test_GitlabSendFailure(t *testing.T) { + gitlabRequest.Commit.Status = "Failure" + + expected := gogitlab.CommitComment{ + Note: fmt.Sprintf("[the build failed](%s) :thumbsdown: -> bad to merge", getBuildUrl(gitlabRequest)), + } + + gitlabClient.Comment = &expected + + err := gitlabSubject.send(gitlabRequest, gitlabClient) + if err != nil { + t.Errorf("Unexepected error: %s", err.Error()) + } + + if *gitlabClient.Comment != expected { + t.Errorf("Invalid gitlab payload. Expected: %v, got %v", expected, *gitlabClient.Comment) + } +} diff --git a/plugin/notify/notification.go b/plugin/notify/notification.go index da6701ace..680e2c664 100644 --- a/plugin/notify/notification.go +++ b/plugin/notify/notification.go @@ -31,6 +31,7 @@ type Notification struct { Gitter *Gitter `yaml:"gitter,omitempty"` Flowdock *flowdock.Flowdock `yaml:"flowdock,omitempty"` KatoIM *katoim.KatoIM `yaml:"katoim,omitempty"` + Gitlab *Gitlab `yaml:"gitlab,omitempty"` GitHub github.GitHub `yaml:"--"` } @@ -100,6 +101,14 @@ func (n *Notification) Send(context *model.Request) error { } } + // send Gitlab notifications + if n.Gitlab != nil { + err := n.Gitlab.Send(context) + if err != nil { + log.Println(err) + } + } + // send email notifications // TODO (bradrydzewski) need to improve this code githubStatus := new(github.GitHub)