diff --git a/routers/web/repo/actions/view.go b/routers/web/repo/actions/view.go index c553aef9ae..f96cd2acf8 100644 --- a/routers/web/repo/actions/view.go +++ b/routers/web/repo/actions/view.go @@ -55,6 +55,7 @@ type ViewResponse struct { Status string `json:"status"` CanCancel bool `json:"canCancel"` CanApprove bool `json:"canApprove"` // the run needs an approval and the doer has permission to approve + CanRerun bool `json:"canRerun"` Done bool `json:"done"` Jobs []*ViewJob `json:"jobs"` Commit ViewCommit `json:"commit"` @@ -136,6 +137,7 @@ func ViewPost(ctx *context_module.Context) { resp.State.Run.Link = run.Link() resp.State.Run.CanCancel = !run.Status.IsDone() && ctx.Repo.CanWrite(unit.TypeActions) resp.State.Run.CanApprove = run.NeedApproval && ctx.Repo.CanWrite(unit.TypeActions) + resp.State.Run.CanRerun = run.Status.IsDone() && ctx.Repo.CanWrite(unit.TypeActions) resp.State.Run.Done = run.Status.IsDone() resp.State.Run.Jobs = make([]*ViewJob, 0, len(jobs)) // marshal to '[]' instead fo 'null' in json resp.State.Run.Status = run.Status.String() @@ -238,7 +240,7 @@ func ViewPost(ctx *context_module.Context) { ctx.JSON(http.StatusOK, resp) } -func Rerun(ctx *context_module.Context) { +func RerunOne(ctx *context_module.Context) { runIndex := ctx.ParamsInt64("run") jobIndex := ctx.ParamsInt64("job") @@ -246,10 +248,37 @@ func Rerun(ctx *context_module.Context) { if ctx.Written() { return } + + if err := rerunJob(ctx, job); err != nil { + ctx.Error(http.StatusInternalServerError, err.Error()) + return + } + + ctx.JSON(http.StatusOK, struct{}{}) +} + +func RerunAll(ctx *context_module.Context) { + runIndex := ctx.ParamsInt64("run") + + _, jobs := getRunJobs(ctx, runIndex, 0) + if ctx.Written() { + return + } + + for _, j := range jobs { + if err := rerunJob(ctx, j); err != nil { + ctx.Error(http.StatusInternalServerError, err.Error()) + return + } + } + + ctx.JSON(http.StatusOK, struct{}{}) +} + +func rerunJob(ctx *context_module.Context, job *actions_model.ActionRunJob) error { status := job.Status if !status.IsDone() { - ctx.JSON(http.StatusOK, struct{}{}) - return + return nil } job.TaskID = 0 @@ -261,13 +290,11 @@ func Rerun(ctx *context_module.Context) { _, err := actions_model.UpdateRunJob(ctx, job, builder.Eq{"status": status}, "task_id", "status", "started", "stopped") return err }); err != nil { - ctx.Error(http.StatusInternalServerError, err.Error()) - return + return err } actions_service.CreateCommitStatus(ctx, job) - - ctx.JSON(http.StatusOK, struct{}{}) + return nil } func Cancel(ctx *context_module.Context) { diff --git a/routers/web/web.go b/routers/web/web.go index e63add51f3..5357c55500 100644 --- a/routers/web/web.go +++ b/routers/web/web.go @@ -1186,10 +1186,11 @@ func registerRoutes(m *web.Route) { m.Combo(""). Get(actions.View). Post(web.Bind(actions.ViewRequest{}), actions.ViewPost) - m.Post("/rerun", reqRepoActionsWriter, actions.Rerun) + m.Post("/rerun", reqRepoActionsWriter, actions.RerunOne) }) m.Post("/cancel", reqRepoActionsWriter, actions.Cancel) m.Post("/approve", reqRepoActionsWriter, actions.Approve) + m.Post("/rerun", reqRepoActionsWriter, actions.RerunAll) }) }, reqRepoActionsReader, actions.MustEnableActions) diff --git a/web_src/js/components/RepoActionView.vue b/web_src/js/components/RepoActionView.vue index 6fcd6ce880..00411654a3 100644 --- a/web_src/js/components/RepoActionView.vue +++ b/web_src/js/components/RepoActionView.vue @@ -6,11 +6,14 @@
{{ run.title }}
- - +
@@ -106,6 +109,7 @@ const sfc = { status: '', canCancel: false, canApprove: false, + canRerun: false, done: false, jobs: [ // { @@ -193,6 +197,11 @@ const sfc = { await this.fetchPost(`${jobLink}/rerun`); window.location.href = jobLink; }, + // rerun workflow + async rerun() { + await this.fetchPost(`${this.run.link}/rerun`); + window.location.href = this.run.link; + }, // cancel a run cancelRun() { this.fetchPost(`${this.run.link}/cancel`); @@ -366,26 +375,16 @@ export function ansiLogToHTML(line) { margin: 0 20px 20px 20px; } -.action-view-header .run_cancel { +.action-view-header .action-control-button { border: none; - color: var(--color-red); background-color: transparent; outline: none; cursor: pointer; transition: transform 0.2s; + display: flex; } -.action-view-header .run_approve { - border: none; - color: var(--color-green); - background-color: transparent; - outline: none; - cursor: pointer; - transition: transform 0.2s; -} - -.action-view-header .run_cancel:hover, -.action-view-header .run_approve:hover { +.action-view-header .action-control-button:hover { transform: scale(130%); } diff --git a/web_src/js/svg.js b/web_src/js/svg.js index 821ed9fd43..024b1fbc16 100644 --- a/web_src/js/svg.js +++ b/web_src/js/svg.js @@ -18,6 +18,7 @@ import octiconLink from '../../public/img/svg/octicon-link.svg'; import octiconLock from '../../public/img/svg/octicon-lock.svg'; import octiconMilestone from '../../public/img/svg/octicon-milestone.svg'; import octiconMirror from '../../public/img/svg/octicon-mirror.svg'; +import octiconPlay from '../../public/img/svg/octicon-play.svg'; import octiconProject from '../../public/img/svg/octicon-project.svg'; import octiconRepo from '../../public/img/svg/octicon-repo.svg'; import octiconRepoForked from '../../public/img/svg/octicon-repo-forked.svg'; @@ -79,6 +80,7 @@ const svgs = { 'octicon-milestone': octiconMilestone, 'octicon-mirror': octiconMirror, 'octicon-organization': octiconOrganization, + 'octicon-play': octiconPlay, 'octicon-plus': octiconPlus, 'octicon-project': octiconProject, 'octicon-repo': octiconRepo,