Changes: - Add rerun workflow button. Then users can rerun the whole workflow by only one-click. - Refactor to use SVG octicons in RepoActionView.vue ![image](https://user-images.githubusercontent.com/18380374/234736083-dea9b333-ec11-4095-a113-763f3716fba7.png) ![image](https://user-images.githubusercontent.com/18380374/234736107-d657d19c-f70a-42f4-985f-156a8c7efb7a.png) ![image](https://user-images.githubusercontent.com/18380374/234736160-9ad372df-7089-4d18-9bab-48bca3f01878.png) --------- Co-authored-by: silverwind <me@silverwind.io> Co-authored-by: wxiaoguang <wxiaoguang@gmail.com>tags/v1.20.0-rc0
@@ -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) { |
@@ -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) | |||
@@ -6,11 +6,14 @@ | |||
<div class="action-title"> | |||
{{ run.title }} | |||
</div> | |||
<button class="run_approve" @click="approveRun()" v-if="run.canApprove"> | |||
<i class="play circle outline icon"/> | |||
<button class="action-control-button text green" @click="approveRun()" v-if="run.canApprove"> | |||
<SvgIcon name="octicon-play" :size="20"/> | |||
</button> | |||
<button class="run_cancel" @click="cancelRun()" v-else-if="run.canCancel"> | |||
<i class="stop circle outline icon"/> | |||
<button class="action-control-button text red" @click="cancelRun()" v-else-if="run.canCancel"> | |||
<SvgIcon name="octicon-x-circle-fill" :size="20"/> | |||
</button> | |||
<button class="action-control-button text green" @click="rerun()" v-else-if="run.canRerun"> | |||
<SvgIcon name="octicon-sync" :size="20"/> | |||
</button> | |||
</div> | |||
<div class="action-commit-summary"> | |||
@@ -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 { | |||
border: none; | |||
color: var(--color-red); | |||
background-color: transparent; | |||
outline: none; | |||
cursor: pointer; | |||
transition: transform 0.2s; | |||
} | |||
.action-view-header .run_approve { | |||
.action-view-header .action-control-button { | |||
border: none; | |||
color: var(--color-green); | |||
background-color: transparent; | |||
outline: none; | |||
cursor: pointer; | |||
transition: transform 0.2s; | |||
display: flex; | |||
} | |||
.action-view-header .run_cancel:hover, | |||
.action-view-header .run_approve:hover { | |||
.action-view-header .action-control-button:hover { | |||
transform: scale(130%); | |||
} | |||
@@ -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, |