You've already forked woodpecker
							
							
				mirror of
				https://github.com/woodpecker-ci/woodpecker.git
				synced 2025-10-30 23:27:39 +02:00 
			
		
		
		
	add reviewer fields, endpoints
This commit is contained in:
		| @@ -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:"-"` | ||||
| } | ||||
|  | ||||
|   | ||||
| @@ -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 ( | ||||
|   | ||||
| @@ -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"` | ||||
| } | ||||
|   | ||||
| @@ -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 | ||||
| } | ||||
|   | ||||
| @@ -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, | ||||
|   | ||||
| @@ -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() { | ||||
|   | ||||
| @@ -95,7 +95,7 @@ const HookPullRequest = ` | ||||
|     "default_branch": "master" | ||||
|   }, | ||||
|   "sender": { | ||||
|     "login": "baxterthehacker", | ||||
|     "login": "octocat", | ||||
|     "avatar_url": "https://avatars.githubusercontent.com/u/6752317?v=3" | ||||
|   } | ||||
| } | ||||
|   | ||||
| @@ -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 | ||||
| 	} | ||||
|   | ||||
| @@ -129,6 +129,7 @@ var HookPullRequest = `{ | ||||
|   }, | ||||
|   "sender": { | ||||
|       "id": 1, | ||||
|       "login": "gordon", | ||||
|       "username": "gordon", | ||||
|       "full_name": "Gordon the Gopher", | ||||
|       "email": "gordon@golang.org", | ||||
|   | ||||
| @@ -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, | ||||
|   | ||||
| @@ -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"` | ||||
|   | ||||
| @@ -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) | ||||
| 		} | ||||
| 	} | ||||
|   | ||||
| @@ -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 { | ||||
|   | ||||
							
								
								
									
										18
									
								
								store/datastore/ddl/mysql/12.sql
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										18
									
								
								store/datastore/ddl/mysql/12.sql
									
									
									
									
									
										Normal file
									
								
							| @@ -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; | ||||
							
								
								
									
										18
									
								
								store/datastore/ddl/postgres/12.sql
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										18
									
								
								store/datastore/ddl/postgres/12.sql
									
									
									
									
									
										Normal file
									
								
							| @@ -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; | ||||
							
								
								
									
										18
									
								
								store/datastore/ddl/sqlite3/12.sql
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										18
									
								
								store/datastore/ddl/sqlite3/12.sql
									
									
									
									
									
										Normal file
									
								
							| @@ -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; | ||||
		Reference in New Issue
	
	Block a user