mirror of
https://github.com/go-gitea/gitea.git
synced 2025-04-08 17:05:45 +02:00
feat: support cancel button
This commit is contained in:
parent
f3a9a25682
commit
736275f0b1
@ -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 {
|
||||
|
@ -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
|
||||
}
|
||||
|
@ -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)
|
||||
|
||||
|
@ -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
|
||||
}
|
||||
|
@ -6,9 +6,9 @@
|
||||
</div>
|
||||
|
||||
<div class="job-group-section" v-for="(jobGroup, i) in allJobGroups" :key="i">
|
||||
<div class="job-group-summary">
|
||||
{{ jobGroup.summary }}
|
||||
</div>
|
||||
<!-- <div class="job-group-summary">-->
|
||||
<!-- {{ jobGroup.summary }}-->
|
||||
<!-- </div>-->
|
||||
<div class="job-brief-list">
|
||||
<a class="job-brief-item" v-for="(job, index) in jobGroup.jobs" :key="job.id" v-bind:href="buildInfo.htmlurl+'/jobs/'+index">
|
||||
<SvgIcon name="octicon-check-circle-fill" class="green" v-if="job.status === 'success'"/>
|
||||
@ -23,6 +23,7 @@
|
||||
</button>
|
||||
</a>
|
||||
</div>
|
||||
<button class="ui fluid tiny basic red button" @click="cancelRun()" v-if="buildInfo.cancelable">Cancel</button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@ -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);
|
||||
|
Loading…
x
Reference in New Issue
Block a user