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:
parent
18fc4f5289
commit
5987f00523
|
@ -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) {
|
||||||
|
|
|
@ -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)
|
||||||
|
|
||||||
|
|
|
@ -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%);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -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,
|
||||||
|
|
Loading…
Reference in New Issue