From e319aaff155aa87e015c8cf06420042e8759fedd Mon Sep 17 00:00:00 2001 From: Brad Rydzewski Date: Sat, 18 Mar 2017 16:49:27 +0800 Subject: [PATCH] add reviewer fields, endpoints --- model/build.go | 7 +++- model/const.go | 16 ++++---- model/repo.go | 1 + remote/bitbucket/convert.go | 30 +++++++++----- remote/github/convert.go | 23 +++++++---- remote/github/convert_test.go | 2 + remote/github/fixtures/hooks.go | 2 +- remote/gitlab/gitlab.go | 12 ++++-- remote/gogs/fixtures/hooks.go | 1 + remote/gogs/helper.go | 15 +++++++ remote/gogs/types.go | 1 + router/router.go | 2 + server/build.go | 63 +++++++++++++++++++++++++++++ store/datastore/ddl/mysql/12.sql | 18 +++++++++ store/datastore/ddl/postgres/12.sql | 18 +++++++++ store/datastore/ddl/sqlite3/12.sql | 18 +++++++++ 16 files changed, 198 insertions(+), 31 deletions(-) create mode 100644 store/datastore/ddl/mysql/12.sql create mode 100644 store/datastore/ddl/postgres/12.sql create mode 100644 store/datastore/ddl/sqlite3/12.sql diff --git a/model/build.go b/model/build.go index 3f7e5952..56c2f69a 100644 --- a/model/build.go +++ b/model/build.go @@ -22,12 +22,15 @@ type Build struct { Title string `json:"title" meddler:"build_title"` Message string `json:"message" meddler:"build_message"` Timestamp int64 `json:"timestamp" meddler:"build_timestamp"` + Sender string `json:"sender" meddler:"build_sender"` Author string `json:"author" meddler:"build_author"` Avatar string `json:"author_avatar" meddler:"build_avatar"` Email string `json:"author_email" meddler:"build_email"` Link string `json:"link_url" meddler:"build_link"` - Signed bool `json:"signed" meddler:"build_signed"` - Verified bool `json:"verified" meddler:"build_verified"` + Signed bool `json:"signed" meddler:"build_signed"` // deprecate + Verified bool `json:"verified" meddler:"build_verified"` // deprecate + Reviewer string `json:"reviewed_by" meddler:"build_reviewer"` + Reviewed int64 `json:"reviewed_at" meddler:"build_reviewed"` Jobs []*Job `json:"jobs,omitempty" meddler:"-"` } diff --git a/model/const.go b/model/const.go index efac6b3b..dfc2f308 100644 --- a/model/const.go +++ b/model/const.go @@ -8,13 +8,15 @@ const ( ) const ( - StatusSkipped = "skipped" - StatusPending = "pending" - StatusRunning = "running" - StatusSuccess = "success" - StatusFailure = "failure" - StatusKilled = "killed" - StatusError = "error" + StatusSkipped = "skipped" + StatusPending = "pending" + StatusRunning = "running" + StatusSuccess = "success" + StatusFailure = "failure" + StatusKilled = "killed" + StatusError = "error" + StatusBlocked = "blocked" + StatusDeclined = "declined" ) const ( diff --git a/model/repo.go b/model/repo.go index 6baaf81f..7e881b96 100644 --- a/model/repo.go +++ b/model/repo.go @@ -29,5 +29,6 @@ type Repo struct { AllowPush bool `json:"allow_push" meddler:"repo_allow_push"` AllowDeploy bool `json:"allow_deploys" meddler:"repo_allow_deploys"` AllowTag bool `json:"allow_tags" meddler:"repo_allow_tags"` + Config string `json:"config_path" meddler:"repo_config_path"` Hash string `json:"-" meddler:"repo_hash"` } diff --git a/remote/bitbucket/convert.go b/remote/bitbucket/convert.go index bc6d9171..b5272b40 100644 --- a/remote/bitbucket/convert.go +++ b/remote/bitbucket/convert.go @@ -2,8 +2,8 @@ package bitbucket import ( "fmt" - "regexp" "net/url" + "regexp" "strings" "github.com/drone/drone/model" @@ -19,17 +19,19 @@ const ( ) const ( - descPending = "this build is pending" - descSuccess = "the build was successful" - descFailure = "the build failed" - descError = "oops, something went wrong" + descPending = "this build is pending" + descSuccess = "the build was successful" + descFailure = "the build failed" + descBlocked = "the build requires approval" + descDeclined = "the build was rejected" + descError = "oops, something went wrong" ) // convertStatus is a helper function used to convert a Drone status to a // Bitbucket commit status. func convertStatus(status string) string { switch status { - case model.StatusPending, model.StatusRunning: + case model.StatusPending, model.StatusRunning, model.StatusBlocked: return statusPending case model.StatusSuccess: return statusSuccess @@ -48,6 +50,10 @@ func convertDesc(status string) string { return descSuccess case model.StatusFailure: return descFailure + case model.StatusBlocked: + return descBlocked + case model.StatusDeclined: + return descDeclined default: return descError } @@ -163,6 +169,7 @@ func convertPullHook(from *internal.PullRequestHook) *model.Build { Message: from.PullRequest.Desc, Avatar: from.Actor.Links.Avatar.Href, Author: from.Actor.Login, + Sender: from.Actor.Login, Timestamp: from.PullRequest.Updated.UTC().Unix(), } } @@ -177,6 +184,7 @@ func convertPushHook(hook *internal.PushHook, change *internal.Change) *model.Bu Message: change.New.Target.Message, Avatar: hook.Actor.Links.Avatar.Href, Author: hook.Actor.Login, + Sender: hook.Actor.Login, Timestamp: change.New.Target.Date.UTC().Unix(), } switch change.New.Type { @@ -198,9 +206,9 @@ var reGitMail = regexp.MustCompile("<(.*)>") // extracts the email from a git commit author string func extractEmail(gitauthor string) (author string) { - matches := reGitMail.FindAllStringSubmatch(gitauthor,-1) - if len(matches) == 1 { - author = matches[0][1] - } - return + matches := reGitMail.FindAllStringSubmatch(gitauthor, -1) + if len(matches) == 1 { + author = matches[0][1] + } + return } diff --git a/remote/github/convert.go b/remote/github/convert.go index 84ec72e1..ade90038 100644 --- a/remote/github/convert.go +++ b/remote/github/convert.go @@ -19,10 +19,12 @@ const ( ) const ( - descPending = "this build is pending" - descSuccess = "the build was successful" - descFailure = "the build failed" - descError = "oops, something went wrong" + descPending = "this build is pending" + descSuccess = "the build was successful" + descFailure = "the build failed" + descBlocked = "the build requires approval" + descDeclined = "the build was rejected" + descError = "oops, something went wrong" ) const ( @@ -35,12 +37,12 @@ const ( // GitHub commit status. func convertStatus(status string) string { switch status { - case model.StatusPending, model.StatusRunning: + case model.StatusPending, model.StatusRunning, model.StatusBlocked: return statusPending + case model.StatusFailure, model.StatusDeclined: + return statusFailure case model.StatusSuccess: return statusSuccess - case model.StatusFailure: - return statusFailure default: return statusError } @@ -56,6 +58,10 @@ func convertDesc(status string) string { return descSuccess case model.StatusFailure: return descFailure + case model.StatusBlocked: + return descBlocked + case model.StatusDeclined: + return descDeclined default: return descError } @@ -185,6 +191,7 @@ func convertPushHook(from *webhook) *model.Build { Avatar: from.Sender.Avatar, Author: from.Sender.Login, Remote: from.Repo.CloneURL, + Sender: from.Sender.Login, } if len(build.Author) == 0 { build.Author = from.Head.Author.Username @@ -213,6 +220,7 @@ func convertDeployHook(from *webhook) *model.Build { Ref: from.Deployment.Ref, Branch: from.Deployment.Ref, Deploy: from.Deployment.Env, + Sender: from.Sender.Login, } // if the ref is a sha or short sha we need to manuallyconstruct the ref. if strings.HasPrefix(build.Commit, build.Ref) || build.Commit == build.Ref { @@ -242,6 +250,7 @@ func convertPullHook(from *webhook, merge bool) *model.Build { Author: from.PullRequest.User.Login, Avatar: from.PullRequest.User.Avatar, Title: from.PullRequest.Title, + Sender: from.Sender.Login, Remote: from.PullRequest.Head.Repo.CloneURL, Refspec: fmt.Sprintf(refspec, from.PullRequest.Head.Ref, diff --git a/remote/github/convert_test.go b/remote/github/convert_test.go index dd8ea80f..3fc8b8e4 100644 --- a/remote/github/convert_test.go +++ b/remote/github/convert_test.go @@ -181,6 +181,7 @@ func Test_helper(t *testing.T) { from.PullRequest.Title = "Updated README.md" from.PullRequest.User.Login = "octocat" from.PullRequest.User.Avatar = "https://avatars1.githubusercontent.com/u/583231" + from.Sender.Login = "octocat" build := convertPullHook(from, true) g.Assert(build.Event).Equal(model.EventPull) @@ -193,6 +194,7 @@ func Test_helper(t *testing.T) { g.Assert(build.Title).Equal(from.PullRequest.Title) g.Assert(build.Author).Equal(from.PullRequest.User.Login) g.Assert(build.Avatar).Equal(from.PullRequest.User.Avatar) + g.Assert(build.Sender).Equal(from.Sender.Login) }) g.It("should convert a deployment from webhook", func() { diff --git a/remote/github/fixtures/hooks.go b/remote/github/fixtures/hooks.go index f8dbf317..da9baac6 100644 --- a/remote/github/fixtures/hooks.go +++ b/remote/github/fixtures/hooks.go @@ -95,7 +95,7 @@ const HookPullRequest = ` "default_branch": "master" }, "sender": { - "login": "baxterthehacker", + "login": "octocat", "avatar_url": "https://avatars.githubusercontent.com/u/6752317?v=3" } } diff --git a/remote/gitlab/gitlab.go b/remote/gitlab/gitlab.go index f1da201a..728d8d8e 100644 --- a/remote/gitlab/gitlab.go +++ b/remote/gitlab/gitlab.go @@ -629,18 +629,20 @@ const ( ) const ( - DescPending = "this build is pending" - DescRunning = "this buils is running" + DescPending = "the build is pending" + DescRunning = "the buils is running" DescSuccess = "the build was successful" DescFailure = "the build failed" DescCanceled = "the build canceled" + DescBlocked = "the build is pending approval" + DescDeclined = "the build was rejected" ) // getStatus is a helper functin that converts a Drone // status to a GitHub status. func getStatus(status string) string { switch status { - case model.StatusPending: + case model.StatusPending, model.StatusBlocked: return StatusPending case model.StatusRunning: return StatusRunning @@ -669,6 +671,10 @@ func getDesc(status string) string { return DescFailure case model.StatusKilled: return DescCanceled + case model.StatusBlocked: + return DescBlocked + case model.StatusDeclined: + return DescDeclined default: return DescFailure } diff --git a/remote/gogs/fixtures/hooks.go b/remote/gogs/fixtures/hooks.go index 0fd5eb19..2ed55601 100644 --- a/remote/gogs/fixtures/hooks.go +++ b/remote/gogs/fixtures/hooks.go @@ -129,6 +129,7 @@ var HookPullRequest = `{ }, "sender": { "id": 1, + "login": "gordon", "username": "gordon", "full_name": "Gordon the Gopher", "email": "gordon@golang.org", diff --git a/remote/gogs/helper.go b/remote/gogs/helper.go index e4f3e138..7e0ef1db 100644 --- a/remote/gogs/helper.go +++ b/remote/gogs/helper.go @@ -74,6 +74,10 @@ func buildFromPush(hook *pushHook) *model.Build { if author == "" { author = hook.Sender.Username } + sender := hook.Sender.Username + if sender == "" { + sender = hook.Sender.Login + } return &model.Build{ Event: model.EventPush, @@ -85,6 +89,7 @@ func buildFromPush(hook *pushHook) *model.Build { Avatar: avatar, Author: author, Timestamp: time.Now().UTC().Unix(), + Sender: sender, } } @@ -98,6 +103,10 @@ func buildFromTag(hook *pushHook) *model.Build { if author == "" { author = hook.Sender.Username } + sender := hook.Sender.Username + if sender == "" { + sender = hook.Sender.Login + } return &model.Build{ Event: model.EventTag, @@ -108,6 +117,7 @@ func buildFromTag(hook *pushHook) *model.Build { Message: fmt.Sprintf("created tag %s", hook.Ref), Avatar: avatar, Author: author, + Sender: sender, Timestamp: time.Now().UTC().Unix(), } } @@ -118,6 +128,10 @@ func buildFromPullRequest(hook *pullRequestHook) *model.Build { hook.Repo.URL, fixMalformedAvatar(hook.PullRequest.User.Avatar), ) + sender := hook.Sender.Username + if sender == "" { + sender = hook.Sender.Login + } build := &model.Build{ Event: model.EventPull, Commit: hook.PullRequest.Head.Sha, @@ -127,6 +141,7 @@ func buildFromPullRequest(hook *pullRequestHook) *model.Build { Message: hook.PullRequest.Title, Author: hook.PullRequest.User.Username, Avatar: avatar, + Sender: sender, Title: hook.PullRequest.Title, Refspec: fmt.Sprintf("%s:%s", hook.PullRequest.Head.Ref, diff --git a/remote/gogs/types.go b/remote/gogs/types.go index 0b0949d4..1bf33c62 100644 --- a/remote/gogs/types.go +++ b/remote/gogs/types.go @@ -116,6 +116,7 @@ type pullRequestHook struct { } `json:"repository"` Sender struct { ID int64 `json:"id"` + Login string `json:"login"` Username string `json:"username"` Name string `json:"full_name"` Email string `json:"email"` diff --git a/router/router.go b/router/router.go index 55ecb84e..48123891 100644 --- a/router/router.go +++ b/router/router.go @@ -108,6 +108,8 @@ func Load(middleware ...gin.HandlerFunc) http.Handler { repo.POST("/chown", session.MustRepoAdmin(), server.ChownRepo) repo.POST("/builds/:number", session.MustPush, server.PostBuild) + repo.POST("/builds/:number/approve", session.MustPush, server.PostApproval) + repo.POST("/builds/:number/decline", session.MustPush, server.PostDecline) repo.DELETE("/builds/:number/:job", session.MustPush, server.DeleteBuild) } } diff --git a/server/build.go b/server/build.go index 755c8f17..946d8add 100644 --- a/server/build.go +++ b/server/build.go @@ -156,6 +156,69 @@ func DeleteBuild(c *gin.Context) { c.String(204, "") } +func PostApproval(c *gin.Context) { + var ( + repo = session.Repo(c) + user = session.User(c) + num, _ = strconv.Atoi( + c.Params.ByName("number"), + ) + ) + + build, err := store.GetBuildNumber(c, repo, num) + if err != nil { + c.AbortWithError(404, err) + return + } + if build.Status != model.StatusBlocked { + c.String(500, "cannot decline a build with status %s", build.Status) + return + } + build.Status = model.StatusPending + build.Reviewed = time.Now().Unix() + build.Reviewer = user.Login + + if err := store.UpdateBuild(c, build); err != nil { + c.String(500, "error updating build. %s", err) + return + } + + // + // TODO start build + // + + c.JSON(200, build) +} + +func PostDecline(c *gin.Context) { + var ( + repo = session.Repo(c) + user = session.User(c) + num, _ = strconv.Atoi( + c.Params.ByName("number"), + ) + ) + + build, err := store.GetBuildNumber(c, repo, num) + if err != nil { + c.AbortWithError(404, err) + return + } + if build.Status != model.StatusBlocked { + c.String(500, "cannot decline a build with status %s", build.Status) + return + } + build.Status = model.StatusDeclined + build.Reviewed = time.Now().Unix() + build.Reviewer = user.Login + + if err := store.UpdateBuild(c, build); err != nil { + c.String(500, "error updating build. %s", err) + return + } + c.JSON(200, build) +} + func GetBuildQueue(c *gin.Context) { out, err := store.GetBuildQueue(c) if err != nil { diff --git a/store/datastore/ddl/mysql/12.sql b/store/datastore/ddl/mysql/12.sql new file mode 100644 index 00000000..39db9500 --- /dev/null +++ b/store/datastore/ddl/mysql/12.sql @@ -0,0 +1,18 @@ +-- +migrate Up + +ALTER TABLE repos ADD COLUMN repo_config_path VARCHAR(255); +ALTER TABLE builds ADD COLUMN build_sender VARCHAR(255); +ALTER TABLE builds ADD COLUMN build_reviewer VARCHAR(255); +ALTER TABLE builds ADD COLUMN build_reviewed INTEGER; + +UPDATE repos SET repo_config_path = ''; +UPDATE builds SET build_reviewer = ''; +UPDATE builds SET build_reviewed = 0; +UPDATE builds SET build_sender = ''; + +-- +migrate Down + +ALTER TABLE repos DROP COLUMN repo_config_path; +ALTER TABLE builds DROP COLUMN build_sender; +ALTER TABLE builds DROP COLUMN build_reviewer; +ALTER TABLE builds DROP COLUMN build_reviewed; diff --git a/store/datastore/ddl/postgres/12.sql b/store/datastore/ddl/postgres/12.sql new file mode 100644 index 00000000..d55f2ae5 --- /dev/null +++ b/store/datastore/ddl/postgres/12.sql @@ -0,0 +1,18 @@ +-- +migrate Up + +ALTER TABLE repos ADD COLUMN repo_config_path VARCHAR(255); +ALTER TABLE builds ADD COLUMN build_reviewer VARCHAR(255); +ALTER TABLE builds ADD COLUMN build_reviewed INTEGER; +ALTER TABLE builds ADD COLUMN build_sender VARCHAR(255); + +UPDATE repos SET repo_config_path = ''; +UPDATE builds SET build_reviewer = ''; +UPDATE builds SET build_reviewed = 0; +UPDATE builds SET build_sender = ''; + +-- +migrate Down + +ALTER TABLE repos DROP COLUMN repo_config_path; +ALTER TABLE builds DROP COLUMN build_reviewer; +ALTER TABLE builds DROP COLUMN build_reviewed; +ALTER TABLE builds DROP COLUMN build_sender; diff --git a/store/datastore/ddl/sqlite3/12.sql b/store/datastore/ddl/sqlite3/12.sql new file mode 100644 index 00000000..4392c850 --- /dev/null +++ b/store/datastore/ddl/sqlite3/12.sql @@ -0,0 +1,18 @@ +-- +migrate Up + +ALTER TABLE repos ADD COLUMN repo_config_path TEXT; +ALTER TABLE builds ADD COLUMN build_reviewer TEXT; +ALTER TABLE builds ADD COLUMN build_reviewed INTEGER; +ALTER TABLE builds ADD COLUMN build_sender TEXT; + +UPDATE repos SET repo_config_path = ''; +UPDATE builds SET build_reviewer = ''; +UPDATE builds SET build_reviewed = 0; +UPDATE builds SET build_sender = ''; + +-- +migrate Down + +ALTER TABLE repos DROP COLUMN repo_config_path; +ALTER TABLE builds DROP COLUMN build_reviewer; +ALTER TABLE builds DROP COLUMN build_reviewed; +ALTER TABLE builds DROP COLUMN build_sender;