From 736275f0b1810a43b2b71df5a201bc1f61213c51 Mon Sep 17 00:00:00 2001 From: Jason Song Date: Fri, 18 Nov 2022 18:38:20 +0800 Subject: [PATCH] feat: support cancel button --- models/bots/task.go | 55 +++++++++++++------------ routers/web/repo/builds/view.go | 54 +++++++++++++++++++++--- routers/web/web.go | 2 +- services/bots/clear_tasks.go | 10 +++-- web_src/js/components/RepoBuildView.vue | 18 ++++++-- 5 files changed, 99 insertions(+), 40 deletions(-) diff --git a/models/bots/task.go b/models/bots/task.go index 91b15cc9aa..1178c2857d 100644 --- a/models/bots/task.go +++ b/models/bots/task.go @@ -430,8 +430,10 @@ func UpdateTaskByState(state *runnerv1.TaskState) (*Task, error) { e := db.GetEngine(ctx) task := &Task{} - if _, err := e.ID(state.Id).Get(task); err != nil { + if has, err := e.ID(state.Id).Get(task); err != nil { return nil, err + } else if !has { + return nil, util.ErrNotExist } if state.Result != runnerv1.Result_RESULT_UNSPECIFIED { @@ -483,54 +485,55 @@ func UpdateTaskByState(state *runnerv1.TaskState) (*Task, error) { return task, nil } -func StopTask(ctx context.Context, task *Task, result runnerv1.Result) (*Task, error) { - ctx, commiter, err := db.TxContext(ctx) - if err != nil { - return nil, err +func StopTask(ctx context.Context, taskID int64, status Status) error { + if !status.IsDone() { + return fmt.Errorf("cannot stop task with status %v", status) } - defer commiter.Close() - e := db.GetEngine(ctx) + task := &Task{} + if has, err := e.ID(taskID).Get(task); err != nil { + return err + } else if !has { + return util.ErrNotExist + } + if task.Status.IsDone() { + return nil + } + now := timeutil.TimeStampNow() - if result != runnerv1.Result_RESULT_UNSPECIFIED { - task.Status = Status(result) - task.Stopped = now - if _, err := UpdateRunJob(ctx, &RunJob{ - ID: task.JobID, - Status: task.Status, - Stopped: task.Stopped, - }, nil); err != nil { - return nil, err - } + task.Status = status + task.Stopped = now + if _, err := UpdateRunJob(ctx, &RunJob{ + ID: task.JobID, + Status: task.Status, + Stopped: task.Stopped, + }, nil); err != nil { + return err } if _, err := e.ID(task.ID).Update(task); err != nil { - return nil, err + return err } if err := task.LoadAttributes(ctx); err != nil { - return nil, err + return err } for _, step := range task.Steps { if !step.Status.IsDone() { - step.Status = Status(result) + step.Status = status if step.Started == 0 { step.Started = now } step.Stopped = now } if _, err := e.ID(step.ID).Update(step); err != nil { - return nil, err + return err } } - if err := commiter.Commit(); err != nil { - return nil, err - } - - return task, nil + return nil } func isSubset(set, subset []string) bool { diff --git a/routers/web/repo/builds/view.go b/routers/web/repo/builds/view.go index 527cc70c2e..1ba5e2977c 100644 --- a/routers/web/repo/builds/view.go +++ b/routers/web/repo/builds/view.go @@ -10,6 +10,7 @@ import ( "code.gitea.io/gitea/models/db" "code.gitea.io/gitea/modules/bots" context_module "code.gitea.io/gitea/modules/context" + "code.gitea.io/gitea/modules/timeutil" "code.gitea.io/gitea/modules/web" runnerv1 "gitea.com/gitea/proto-go/runner/v1" @@ -45,8 +46,9 @@ type ViewRequest struct { type ViewResponse struct { StateData struct { BuildInfo struct { - HTMLURL string `json:"htmlurl"` - Title string `json:"title"` + HTMLURL string `json:"htmlurl"` + Title string `json:"title"` + Cancelable bool `json:"cancelable"` } `json:"buildInfo"` AllJobGroups []ViewGroup `json:"allJobGroups"` CurrentJobInfo struct { @@ -103,6 +105,7 @@ func ViewPost(ctx *context_module.Context) { resp := &ViewResponse{} resp.StateData.BuildInfo.Title = run.Title resp.StateData.BuildInfo.HTMLURL = run.HTMLURL() + resp.StateData.BuildInfo.Cancelable = !run.Status.IsDone() respJobs := make([]*ViewJob, len(jobs)) for i, v := range jobs { @@ -115,8 +118,8 @@ func ViewPost(ctx *context_module.Context) { resp.StateData.AllJobGroups = []ViewGroup{ { - Summary: "Only One Group", // TODO: maybe we don't need job group - Jobs: respJobs, + // TODO: maybe we don't need job group + Jobs: respJobs, }, } @@ -221,6 +224,45 @@ func Rerun(ctx *context_module.Context) { ctx.JSON(http.StatusOK, struct{}{}) } +func Cancel(ctx *context_module.Context) { + runIndex := ctx.ParamsInt64("run") + + _, jobs := getRunJobs(ctx, runIndex, -1) + if ctx.Written() { + return + } + + if err := db.WithTx(ctx, func(ctx context.Context) error { + for _, job := range jobs { + status := job.Status + if status.IsDone() { + continue + } + if job.TaskID == 0 { + job.Status = bots_model.StatusCancelled + job.Stopped = timeutil.TimeStampNow() + n, err := bots_model.UpdateRunJob(ctx, job, builder.Eq{"task_id": 0}, "status", "stopped") + if err != nil { + return err + } + if n == 0 { + return fmt.Errorf("job has changed, try again") + } + continue + } + if err := bots_model.StopTask(ctx, job.TaskID, bots_model.StatusCancelled); err != nil { + return err + } + } + return nil + }); err != nil { + ctx.Error(http.StatusInternalServerError, err.Error()) + return + } + + ctx.JSON(http.StatusOK, struct{}{}) +} + func getRunJobs(ctx *context_module.Context, runIndex, jobIndex int64) (current *bots_model.RunJob, jobs []*bots_model.RunJob) { run, err := bots_model.GetRunByIndex(ctx, ctx.Repo.Repository.ID, runIndex) if err != nil { @@ -242,12 +284,12 @@ func getRunJobs(ctx *context_module.Context, runIndex, jobIndex int64) (current v.Run = run } - if jobIndex < 0 || jobIndex >= int64(len(jobs)) { + if jobIndex >= 0 && jobIndex < int64(len(jobs)) { if len(jobs) == 0 { ctx.Error(http.StatusNotFound, fmt.Sprintf("run %v has no job %v", runIndex, jobIndex)) return nil, nil } + current = jobs[jobIndex] } - current = jobs[jobIndex] return } diff --git a/routers/web/web.go b/routers/web/web.go index 07fc7f6332..f656a8d699 100644 --- a/routers/web/web.go +++ b/routers/web/web.go @@ -1225,7 +1225,7 @@ func RegisterRoutes(m *web.Route) { Post(bindIgnErr(builds.ViewRequest{}), builds.ViewPost) m.Post("/rerun", builds.Rerun) }) - + m.Post("/cancel", builds.Cancel) }) }, reqRepoBuildsReader, builds.MustEnableBuilds) diff --git a/services/bots/clear_tasks.go b/services/bots/clear_tasks.go index 4237e5f859..0664af0f6b 100644 --- a/services/bots/clear_tasks.go +++ b/services/bots/clear_tasks.go @@ -12,8 +12,6 @@ import ( "code.gitea.io/gitea/models/db" "code.gitea.io/gitea/modules/log" "code.gitea.io/gitea/modules/timeutil" - - runnerv1 "gitea.com/gitea/proto-go/runner/v1" ) const ( @@ -34,7 +32,9 @@ func StopZombieTasks(ctx context.Context) error { } for _, task := range tasks { - if _, err := bots_model.StopTask(ctx, task, runnerv1.Result_RESULT_FAILURE); err != nil { + if err := db.WithTx(ctx, func(ctx context.Context) error { + return bots_model.StopTask(ctx, task.ID, bots_model.StatusFailure) + }); err != nil { log.Warn("stop zombie task %v: %v", task.ID, err) // go on } @@ -54,7 +54,9 @@ func StopEndlessTasks(ctx context.Context) error { } for _, task := range tasks { - if _, err := bots_model.StopTask(ctx, task, runnerv1.Result_RESULT_FAILURE); err != nil { + if err := db.WithTx(ctx, func(ctx context.Context) error { + return bots_model.StopTask(ctx, task.ID, bots_model.StatusFailure) + }); err != nil { log.Warn("stop endless task %v: %v", task.ID, err) // go on } diff --git a/web_src/js/components/RepoBuildView.vue b/web_src/js/components/RepoBuildView.vue index d5e440b4b5..0b9b077176 100644 --- a/web_src/js/components/RepoBuildView.vue +++ b/web_src/js/components/RepoBuildView.vue @@ -6,9 +6,9 @@
-
- {{ jobGroup.summary }} -
+ + + +
@@ -160,6 +161,17 @@ const sfc = { body: {}, }); }, + // cancel a run + cancelRun() { + fetch(this.buildInfo.htmlurl+'/cancel', { + method: 'POST', + headers: { + 'Content-Type': 'application/json', + 'X-Csrf-Token': csrfToken, + }, + body: {}, + }); + }, formatDuration(d) { d = Math.round(d);