Add rerun workflow button and refactor to use SVG octicons (#24350)

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>
This commit is contained in:
yp05327 2023-05-01 23:14:20 +09:00 committed by GitHub
parent 18fc4f5289
commit 5987f00523
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
4 changed files with 54 additions and 25 deletions

View file

@ -55,6 +55,7 @@ type ViewResponse struct {
Status string `json:"status"` Status string `json:"status"`
CanCancel bool `json:"canCancel"` CanCancel bool `json:"canCancel"`
CanApprove bool `json:"canApprove"` // the run needs an approval and the doer has permission to approve CanApprove bool `json:"canApprove"` // the run needs an approval and the doer has permission to approve
CanRerun bool `json:"canRerun"`
Done bool `json:"done"` Done bool `json:"done"`
Jobs []*ViewJob `json:"jobs"` Jobs []*ViewJob `json:"jobs"`
Commit ViewCommit `json:"commit"` Commit ViewCommit `json:"commit"`
@ -136,6 +137,7 @@ func ViewPost(ctx *context_module.Context) {
resp.State.Run.Link = run.Link() resp.State.Run.Link = run.Link()
resp.State.Run.CanCancel = !run.Status.IsDone() && ctx.Repo.CanWrite(unit.TypeActions) 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.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.Done = run.Status.IsDone()
resp.State.Run.Jobs = make([]*ViewJob, 0, len(jobs)) // marshal to '[]' instead fo 'null' in json resp.State.Run.Jobs = make([]*ViewJob, 0, len(jobs)) // marshal to '[]' instead fo 'null' in json
resp.State.Run.Status = run.Status.String() resp.State.Run.Status = run.Status.String()
@ -238,7 +240,7 @@ func ViewPost(ctx *context_module.Context) {
ctx.JSON(http.StatusOK, resp) ctx.JSON(http.StatusOK, resp)
} }
func Rerun(ctx *context_module.Context) { func RerunOne(ctx *context_module.Context) {
runIndex := ctx.ParamsInt64("run") runIndex := ctx.ParamsInt64("run")
jobIndex := ctx.ParamsInt64("job") jobIndex := ctx.ParamsInt64("job")
@ -246,10 +248,37 @@ func Rerun(ctx *context_module.Context) {
if ctx.Written() { if ctx.Written() {
return 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 status := job.Status
if !status.IsDone() { if !status.IsDone() {
ctx.JSON(http.StatusOK, struct{}{}) return nil
return
} }
job.TaskID = 0 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") _, err := actions_model.UpdateRunJob(ctx, job, builder.Eq{"status": status}, "task_id", "status", "started", "stopped")
return err return err
}); err != nil { }); err != nil {
ctx.Error(http.StatusInternalServerError, err.Error()) return err
return
} }
actions_service.CreateCommitStatus(ctx, job) actions_service.CreateCommitStatus(ctx, job)
return nil
ctx.JSON(http.StatusOK, struct{}{})
} }
func Cancel(ctx *context_module.Context) { func Cancel(ctx *context_module.Context) {

View file

@ -1186,10 +1186,11 @@ func registerRoutes(m *web.Route) {
m.Combo(""). m.Combo("").
Get(actions.View). Get(actions.View).
Post(web.Bind(actions.ViewRequest{}), actions.ViewPost) 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("/cancel", reqRepoActionsWriter, actions.Cancel)
m.Post("/approve", reqRepoActionsWriter, actions.Approve) m.Post("/approve", reqRepoActionsWriter, actions.Approve)
m.Post("/rerun", reqRepoActionsWriter, actions.RerunAll)
}) })
}, reqRepoActionsReader, actions.MustEnableActions) }, reqRepoActionsReader, actions.MustEnableActions)

View file

@ -6,11 +6,14 @@
<div class="action-title"> <div class="action-title">
{{ run.title }} {{ run.title }}
</div> </div>
<button class="run_approve" @click="approveRun()" v-if="run.canApprove"> <button class="action-control-button text green" @click="approveRun()" v-if="run.canApprove">
<i class="play circle outline icon"/> <SvgIcon name="octicon-play" :size="20"/>
</button> </button>
<button class="run_cancel" @click="cancelRun()" v-else-if="run.canCancel"> <button class="action-control-button text red" @click="cancelRun()" v-else-if="run.canCancel">
<i class="stop circle outline icon"/> <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> </button>
</div> </div>
<div class="action-commit-summary"> <div class="action-commit-summary">
@ -106,6 +109,7 @@ const sfc = {
status: '', status: '',
canCancel: false, canCancel: false,
canApprove: false, canApprove: false,
canRerun: false,
done: false, done: false,
jobs: [ jobs: [
// { // {
@ -193,6 +197,11 @@ const sfc = {
await this.fetchPost(`${jobLink}/rerun`); await this.fetchPost(`${jobLink}/rerun`);
window.location.href = jobLink; window.location.href = jobLink;
}, },
// rerun workflow
async rerun() {
await this.fetchPost(`${this.run.link}/rerun`);
window.location.href = this.run.link;
},
// cancel a run // cancel a run
cancelRun() { cancelRun() {
this.fetchPost(`${this.run.link}/cancel`); this.fetchPost(`${this.run.link}/cancel`);
@ -366,26 +375,16 @@ export function ansiLogToHTML(line) {
margin: 0 20px 20px 20px; margin: 0 20px 20px 20px;
} }
.action-view-header .run_cancel { .action-view-header .action-control-button {
border: none; border: none;
color: var(--color-red);
background-color: transparent; background-color: transparent;
outline: none; outline: none;
cursor: pointer; cursor: pointer;
transition: transform 0.2s; transition: transform 0.2s;
display: flex;
} }
.action-view-header .run_approve { .action-view-header .action-control-button:hover {
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 {
transform: scale(130%); transform: scale(130%);
} }

View file

@ -18,6 +18,7 @@ import octiconLink from '../../public/img/svg/octicon-link.svg';
import octiconLock from '../../public/img/svg/octicon-lock.svg'; import octiconLock from '../../public/img/svg/octicon-lock.svg';
import octiconMilestone from '../../public/img/svg/octicon-milestone.svg'; import octiconMilestone from '../../public/img/svg/octicon-milestone.svg';
import octiconMirror from '../../public/img/svg/octicon-mirror.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 octiconProject from '../../public/img/svg/octicon-project.svg';
import octiconRepo from '../../public/img/svg/octicon-repo.svg'; import octiconRepo from '../../public/img/svg/octicon-repo.svg';
import octiconRepoForked from '../../public/img/svg/octicon-repo-forked.svg'; import octiconRepoForked from '../../public/img/svg/octicon-repo-forked.svg';
@ -79,6 +80,7 @@ const svgs = {
'octicon-milestone': octiconMilestone, 'octicon-milestone': octiconMilestone,
'octicon-mirror': octiconMirror, 'octicon-mirror': octiconMirror,
'octicon-organization': octiconOrganization, 'octicon-organization': octiconOrganization,
'octicon-play': octiconPlay,
'octicon-plus': octiconPlus, 'octicon-plus': octiconPlus,
'octicon-project': octiconProject, 'octicon-project': octiconProject,
'octicon-repo': octiconRepo, 'octicon-repo': octiconRepo,