Add quick approve button on PR page (#35678)
This PR adds a quick approve button on PR page to allow reviewers to approve all pending checks. Only users with write permission to the Actions unit can approve. --------- Signed-off-by: Zettat123 <zettat123@gmail.com> Co-authored-by: wxiaoguang <wxiaoguang@gmail.com>
This commit is contained in:
@@ -606,33 +606,53 @@ func Cancel(ctx *context_module.Context) {
|
||||
func Approve(ctx *context_module.Context) {
|
||||
runIndex := getRunIndex(ctx)
|
||||
|
||||
current, jobs := getRunJobs(ctx, runIndex, -1)
|
||||
approveRuns(ctx, []int64{runIndex})
|
||||
if ctx.Written() {
|
||||
return
|
||||
}
|
||||
run := current.Run
|
||||
doer := ctx.Doer
|
||||
|
||||
var updatedJobs []*actions_model.ActionRunJob
|
||||
ctx.JSONOK()
|
||||
}
|
||||
|
||||
func approveRuns(ctx *context_module.Context, runIndexes []int64) {
|
||||
doer := ctx.Doer
|
||||
repo := ctx.Repo.Repository
|
||||
|
||||
updatedJobs := make([]*actions_model.ActionRunJob, 0)
|
||||
runMap := make(map[int64]*actions_model.ActionRun, len(runIndexes))
|
||||
runJobs := make(map[int64][]*actions_model.ActionRunJob, len(runIndexes))
|
||||
|
||||
err := db.WithTx(ctx, func(ctx context.Context) (err error) {
|
||||
run.NeedApproval = false
|
||||
run.ApprovedBy = doer.ID
|
||||
if err := actions_model.UpdateRun(ctx, run, "need_approval", "approved_by"); err != nil {
|
||||
return err
|
||||
}
|
||||
for _, job := range jobs {
|
||||
job.Status, err = actions_service.PrepareToStartJobWithConcurrency(ctx, job)
|
||||
for _, runIndex := range runIndexes {
|
||||
run, err := actions_model.GetRunByIndex(ctx, repo.ID, runIndex)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if job.Status == actions_model.StatusWaiting {
|
||||
n, err := actions_model.UpdateRunJob(ctx, job, nil, "status")
|
||||
runMap[run.ID] = run
|
||||
run.Repo = repo
|
||||
run.NeedApproval = false
|
||||
run.ApprovedBy = doer.ID
|
||||
if err := actions_model.UpdateRun(ctx, run, "need_approval", "approved_by"); err != nil {
|
||||
return err
|
||||
}
|
||||
jobs, err := actions_model.GetRunJobsByRunID(ctx, run.ID)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
runJobs[run.ID] = jobs
|
||||
for _, job := range jobs {
|
||||
job.Status, err = actions_service.PrepareToStartJobWithConcurrency(ctx, job)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if n > 0 {
|
||||
updatedJobs = append(updatedJobs, job)
|
||||
if job.Status == actions_model.StatusWaiting {
|
||||
n, err := actions_model.UpdateRunJob(ctx, job, nil, "status")
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if n > 0 {
|
||||
updatedJobs = append(updatedJobs, job)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -643,7 +663,9 @@ func Approve(ctx *context_module.Context) {
|
||||
return
|
||||
}
|
||||
|
||||
actions_service.CreateCommitStatusForRunJobs(ctx, current.Run, jobs...)
|
||||
for runID, run := range runMap {
|
||||
actions_service.CreateCommitStatusForRunJobs(ctx, run, runJobs[runID]...)
|
||||
}
|
||||
|
||||
if len(updatedJobs) > 0 {
|
||||
job := updatedJobs[0]
|
||||
@@ -654,8 +676,6 @@ func Approve(ctx *context_module.Context) {
|
||||
_ = job.LoadAttributes(ctx)
|
||||
notify_service.WorkflowJobStatusUpdate(ctx, job.Run.Repo, job.Run.TriggerUser, job, nil)
|
||||
}
|
||||
|
||||
ctx.JSONOK()
|
||||
}
|
||||
|
||||
func Delete(ctx *context_module.Context) {
|
||||
@@ -818,6 +838,42 @@ func ArtifactsDownloadView(ctx *context_module.Context) {
|
||||
}
|
||||
}
|
||||
|
||||
func ApproveAllChecks(ctx *context_module.Context) {
|
||||
repo := ctx.Repo.Repository
|
||||
commitID := ctx.FormString("commit_id")
|
||||
|
||||
commitStatuses, err := git_model.GetLatestCommitStatus(ctx, repo.ID, commitID, db.ListOptionsAll)
|
||||
if err != nil {
|
||||
ctx.ServerError("GetLatestCommitStatus", err)
|
||||
return
|
||||
}
|
||||
runs, err := actions_service.GetRunsFromCommitStatuses(ctx, commitStatuses)
|
||||
if err != nil {
|
||||
ctx.ServerError("GetRunsFromCommitStatuses", err)
|
||||
return
|
||||
}
|
||||
|
||||
runIndexes := make([]int64, 0, len(runs))
|
||||
for _, run := range runs {
|
||||
if run.NeedApproval {
|
||||
runIndexes = append(runIndexes, run.Index)
|
||||
}
|
||||
}
|
||||
|
||||
if len(runIndexes) == 0 {
|
||||
ctx.JSONOK()
|
||||
return
|
||||
}
|
||||
|
||||
approveRuns(ctx, runIndexes)
|
||||
if ctx.Written() {
|
||||
return
|
||||
}
|
||||
|
||||
ctx.Flash.Success(ctx.Tr("actions.approve_all_success"))
|
||||
ctx.JSONOK()
|
||||
}
|
||||
|
||||
func DisableWorkflowFile(ctx *context_module.Context) {
|
||||
disableOrEnableWorkflowFile(ctx, false)
|
||||
}
|
||||
|
||||
@@ -437,6 +437,9 @@ func ViewIssue(ctx *context.Context) {
|
||||
|
||||
func ViewPullMergeBox(ctx *context.Context) {
|
||||
issue := prepareIssueViewLoad(ctx)
|
||||
if ctx.Written() {
|
||||
return
|
||||
}
|
||||
if !issue.IsPull {
|
||||
ctx.NotFound(nil)
|
||||
return
|
||||
|
||||
@@ -38,6 +38,7 @@ import (
|
||||
"code.gitea.io/gitea/modules/web"
|
||||
"code.gitea.io/gitea/routers/utils"
|
||||
shared_user "code.gitea.io/gitea/routers/web/shared/user"
|
||||
actions_service "code.gitea.io/gitea/services/actions"
|
||||
asymkey_service "code.gitea.io/gitea/services/asymkey"
|
||||
"code.gitea.io/gitea/services/automerge"
|
||||
"code.gitea.io/gitea/services/context"
|
||||
@@ -311,6 +312,14 @@ func prepareMergedViewPullInfo(ctx *context.Context, issue *issues_model.Issue)
|
||||
return compareInfo
|
||||
}
|
||||
|
||||
type pullCommitStatusCheckData struct {
|
||||
MissingRequiredChecks []string // list of missing required checks
|
||||
IsContextRequired func(string) bool // function to check whether a context is required
|
||||
RequireApprovalRunCount int // number of workflow runs that require approval
|
||||
CanApprove bool // whether the user can approve workflow runs
|
||||
ApproveLink string // link to approve all checks
|
||||
}
|
||||
|
||||
// prepareViewPullInfo show meta information for a pull request preview page
|
||||
func prepareViewPullInfo(ctx *context.Context, issue *issues_model.Issue) *pull_service.CompareInfo {
|
||||
ctx.Data["PullRequestWorkInProgressPrefixes"] = setting.Repository.PullRequest.WorkInProgressPrefixes
|
||||
@@ -456,6 +465,11 @@ func prepareViewPullInfo(ctx *context.Context, issue *issues_model.Issue) *pull_
|
||||
return nil
|
||||
}
|
||||
|
||||
statusCheckData := &pullCommitStatusCheckData{
|
||||
ApproveLink: fmt.Sprintf("%s/actions/approve-all-checks?commit_id=%s", repo.Link(), sha),
|
||||
}
|
||||
ctx.Data["StatusCheckData"] = statusCheckData
|
||||
|
||||
commitStatuses, err := git_model.GetLatestCommitStatus(ctx, repo.ID, sha, db.ListOptionsAll)
|
||||
if err != nil {
|
||||
ctx.ServerError("GetLatestCommitStatus", err)
|
||||
@@ -465,6 +479,20 @@ func prepareViewPullInfo(ctx *context.Context, issue *issues_model.Issue) *pull_
|
||||
git_model.CommitStatusesHideActionsURL(ctx, commitStatuses)
|
||||
}
|
||||
|
||||
runs, err := actions_service.GetRunsFromCommitStatuses(ctx, commitStatuses)
|
||||
if err != nil {
|
||||
ctx.ServerError("GetRunsFromCommitStatuses", err)
|
||||
return nil
|
||||
}
|
||||
for _, run := range runs {
|
||||
if run.NeedApproval {
|
||||
statusCheckData.RequireApprovalRunCount++
|
||||
}
|
||||
}
|
||||
if statusCheckData.RequireApprovalRunCount > 0 {
|
||||
statusCheckData.CanApprove = ctx.Repo.CanWrite(unit.TypeActions)
|
||||
}
|
||||
|
||||
if len(commitStatuses) > 0 {
|
||||
ctx.Data["LatestCommitStatuses"] = commitStatuses
|
||||
ctx.Data["LatestCommitStatus"] = git_model.CalcCommitStatus(commitStatuses)
|
||||
@@ -486,9 +514,9 @@ func prepareViewPullInfo(ctx *context.Context, issue *issues_model.Issue) *pull_
|
||||
missingRequiredChecks = append(missingRequiredChecks, requiredContext)
|
||||
}
|
||||
}
|
||||
ctx.Data["MissingRequiredChecks"] = missingRequiredChecks
|
||||
statusCheckData.MissingRequiredChecks = missingRequiredChecks
|
||||
|
||||
ctx.Data["is_context_required"] = func(context string) bool {
|
||||
statusCheckData.IsContextRequired = func(context string) bool {
|
||||
for _, c := range pb.StatusCheckContexts {
|
||||
if c == context {
|
||||
return true
|
||||
|
||||
@@ -1459,6 +1459,7 @@ func registerWebRoutes(m *web.Router) {
|
||||
m.Post("/enable", reqRepoAdmin, actions.EnableWorkflowFile)
|
||||
m.Post("/run", reqRepoActionsWriter, actions.Run)
|
||||
m.Get("/workflow-dispatch-inputs", reqRepoActionsWriter, actions.WorkflowDispatchInputs)
|
||||
m.Post("/approve-all-checks", reqRepoActionsWriter, actions.ApproveAllChecks)
|
||||
|
||||
m.Group("/runs/{run}", func() {
|
||||
m.Combo("").
|
||||
|
||||
Reference in New Issue
Block a user