|
|
|
@ -1,28 +1,30 @@ |
|
|
|
|
<template> |
|
|
|
|
<div class="action-view-container"> |
|
|
|
|
<div class="ui container action-view-container"> |
|
|
|
|
<div class="action-view-header"> |
|
|
|
|
<div class="action-info-summary"> |
|
|
|
|
<ActionRunStatus :locale-status="locale.status[run.status]" :status="run.status" :size="20"/> |
|
|
|
|
<div class="action-title"> |
|
|
|
|
{{ run.title }} |
|
|
|
|
<div class="action-info-summary-title"> |
|
|
|
|
<ActionRunStatus :locale-status="locale.status[run.status]" :status="run.status" :size="20"/> |
|
|
|
|
<h2 class="action-info-summary-title-text"> |
|
|
|
|
{{ run.title }} |
|
|
|
|
</h2> |
|
|
|
|
</div> |
|
|
|
|
<button class="ui basic small compact button primary" @click="approveRun()" v-if="run.canApprove"> |
|
|
|
|
<SvgIcon class="gt-mr-2" name="octicon-play" :size="20"/> {{ locale.approve }} |
|
|
|
|
{{ locale.approve }} |
|
|
|
|
</button> |
|
|
|
|
<button class="ui basic small compact button red" @click="cancelRun()" v-else-if="run.canCancel"> |
|
|
|
|
<SvgIcon class="gt-mr-2" name="octicon-x-circle-fill" :size="20"/> {{ locale.cancel }} |
|
|
|
|
{{ locale.cancel }} |
|
|
|
|
</button> |
|
|
|
|
<button class="ui basic small compact button secondary" @click="rerun()" v-else-if="run.canRerun"> |
|
|
|
|
<SvgIcon class="gt-mr-2" name="octicon-sync" :size="20"/> {{ locale.rerun }} |
|
|
|
|
{{ locale.rerun_all }} |
|
|
|
|
</button> |
|
|
|
|
</div> |
|
|
|
|
<div class="action-commit-summary"> |
|
|
|
|
{{ run.commit.localeCommit }} |
|
|
|
|
<a :href="run.commit.link">{{ run.commit.shortSHA }}</a> |
|
|
|
|
<span class="ui label" v-if="run.commit.shortSHA"> |
|
|
|
|
<span class="ui label" v-if="run.commit.shortSHA"> |
|
|
|
|
<a :href="run.commit.branch.link">{{ run.commit.branch.name }}</a> |
|
|
|
|
</span> |
|
|
|
|
{{ run.commit.localePushedBy }} |
|
|
|
|
{{ run.commit.localePushedBy }} |
|
|
|
|
<a :href="run.commit.pusher.link">{{ run.commit.pusher.displayName }}</a> |
|
|
|
|
</div> |
|
|
|
|
</div> |
|
|
|
@ -30,15 +32,15 @@ |
|
|
|
|
<div class="action-view-left"> |
|
|
|
|
<div class="job-group-section"> |
|
|
|
|
<div class="job-brief-list"> |
|
|
|
|
<div class="job-brief-item" v-for="(job, index) in run.jobs" :key="job.id"> |
|
|
|
|
<div class="job-brief-item" :class="parseInt(jobIndex) === index ? 'selected' : ''" v-for="(job, index) in run.jobs" :key="job.id" @mouseenter="onHoverRerunIndex = job.id" @mouseleave="onHoverRerunIndex = -1"> |
|
|
|
|
<a class="job-brief-link" :href="run.link+'/jobs/'+index"> |
|
|
|
|
<ActionRunStatus :locale-status="locale.status[job.status]" :status="job.status"/> |
|
|
|
|
<span class="ui text gt-mx-3">{{ job.name }}</span> |
|
|
|
|
<span class="job-brief-name gt-mx-3 gt-ellipsis">{{ job.name }}</span> |
|
|
|
|
</a> |
|
|
|
|
<span class="step-summary-duration">{{ job.duration }}</span> |
|
|
|
|
<button :data-tooltip-content="locale.rerun" class="job-brief-rerun" @click="rerunJob(index)" v-if="job.canRerun"> |
|
|
|
|
<SvgIcon name="octicon-sync" class="ui text black"/> |
|
|
|
|
</button> |
|
|
|
|
<span class="job-brief-info"> |
|
|
|
|
<span class="step-summary-duration">{{ job.duration }}</span> |
|
|
|
|
<SvgIcon name="octicon-sync" role="button" :data-tooltip-content="locale.rerun" class="job-brief-rerun gt-mx-3" @click="rerunJob(index)" v-if="job.canRerun && onHoverRerunIndex === job.id"/> |
|
|
|
|
</span> |
|
|
|
|
</div> |
|
|
|
|
</div> |
|
|
|
|
</div> |
|
|
|
@ -58,21 +60,24 @@ |
|
|
|
|
|
|
|
|
|
<div class="action-view-right"> |
|
|
|
|
<div class="job-info-header"> |
|
|
|
|
<div class="job-info-header-title"> |
|
|
|
|
<h3 class="job-info-header-title"> |
|
|
|
|
{{ currentJob.title }} |
|
|
|
|
</div> |
|
|
|
|
<div class="job-info-header-detail"> |
|
|
|
|
</h3> |
|
|
|
|
<p class="job-info-header-detail"> |
|
|
|
|
{{ currentJob.detail }} |
|
|
|
|
</div> |
|
|
|
|
</p> |
|
|
|
|
</div> |
|
|
|
|
<div class="job-step-container"> |
|
|
|
|
<div class="job-step-section" v-for="(jobStep, i) in currentJob.steps" :key="i"> |
|
|
|
|
<div class="job-step-summary" @click.stop="toggleStepLogs(i)"> |
|
|
|
|
<SvgIcon :name="currentJobStepsStates[i].expanded ? 'octicon-chevron-down': 'octicon-chevron-right'" class="gt-mr-3"/> |
|
|
|
|
|
|
|
|
|
<div class="job-step-summary" @click.stop="toggleStepLogs(i)" :class="currentJobStepsStates[i].expanded ? 'selected' : ''"> |
|
|
|
|
<!-- If the job is done and the job step log is loaded for the first time, show the loading icon |
|
|
|
|
currentJobStepsStates[i].cursor === null means the log is loaded for the first time |
|
|
|
|
--> |
|
|
|
|
<SvgIcon v-if="isDone(run.status) && currentJobStepsStates[i].expanded && currentJobStepsStates[i].cursor === null" name="octicon-sync" class="gt-mr-3 job-status-rotate"/> |
|
|
|
|
<SvgIcon v-else :name="currentJobStepsStates[i].expanded ? 'octicon-chevron-down': 'octicon-chevron-right'" class="gt-mr-3"/> |
|
|
|
|
<ActionRunStatus :status="jobStep.status" class="gt-mr-3"/> |
|
|
|
|
|
|
|
|
|
<span class="step-summary-msg">{{ jobStep.summary }}</span> |
|
|
|
|
<span class="step-summary-msg gt-ellipsis">{{ jobStep.summary }}</span> |
|
|
|
|
<span class="step-summary-duration">{{ jobStep.duration }}</span> |
|
|
|
|
</div> |
|
|
|
|
|
|
|
|
@ -115,6 +120,7 @@ const sfc = { |
|
|
|
|
intervalID: null, |
|
|
|
|
currentJobStepsStates: [], |
|
|
|
|
artifacts: [], |
|
|
|
|
onHoverRerunIndex: -1, |
|
|
|
|
|
|
|
|
|
// provided by backend |
|
|
|
|
run: { |
|
|
|
@ -295,6 +301,7 @@ const sfc = { |
|
|
|
|
// sync the currentJobStepsStates to store the job step states |
|
|
|
|
for (let i = 0; i < this.currentJob.steps.length; i++) { |
|
|
|
|
if (!this.currentJobStepsStates[i]) { |
|
|
|
|
// initial states for job steps |
|
|
|
|
this.currentJobStepsStates[i] = {cursor: null, expanded: false}; |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
@ -325,6 +332,10 @@ const sfc = { |
|
|
|
|
body, |
|
|
|
|
}); |
|
|
|
|
}, |
|
|
|
|
|
|
|
|
|
isDone(status) { |
|
|
|
|
return ['success', 'skipped', 'failure', 'cancelled'].includes(status); |
|
|
|
|
} |
|
|
|
|
}, |
|
|
|
|
}; |
|
|
|
|
|
|
|
|
@ -348,6 +359,7 @@ export function initRepositoryActionView() { |
|
|
|
|
cancel: el.getAttribute('data-locale-cancel'), |
|
|
|
|
rerun: el.getAttribute('data-locale-rerun'), |
|
|
|
|
artifactsTitle: el.getAttribute('data-locale-artifacts-title'), |
|
|
|
|
rerun_all: el.getAttribute('data-locale-rerun-all'), |
|
|
|
|
status: { |
|
|
|
|
unknown: el.getAttribute('data-locale-status-unknown'), |
|
|
|
|
waiting: el.getAttribute('data-locale-status-waiting'), |
|
|
|
@ -417,24 +429,30 @@ export function ansiLogToHTML(line) { |
|
|
|
|
/* action view header */ |
|
|
|
|
|
|
|
|
|
.action-view-header { |
|
|
|
|
margin: 0 20px 20px 20px; |
|
|
|
|
margin: 20px 0px; |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
.action-info-summary { |
|
|
|
|
font-size: 150%; |
|
|
|
|
height: 20px; |
|
|
|
|
display: flex; |
|
|
|
|
align-items: center; |
|
|
|
|
margin-top: 1rem; |
|
|
|
|
justify-content: space-between; |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
.action-info-summary-title { |
|
|
|
|
display: flex; |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
.action-info-summary .action-title { |
|
|
|
|
padding: 0 5px; |
|
|
|
|
.action-info-summary-title-text { |
|
|
|
|
font-size: 20px; |
|
|
|
|
margin: 0 0 0 5px; |
|
|
|
|
flex: 1; |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
.action-commit-summary { |
|
|
|
|
padding: 10px 10px; |
|
|
|
|
display: flex; |
|
|
|
|
gap: 5px; |
|
|
|
|
margin: 10px 0px 10px 25px; |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
/* ================ */ |
|
|
|
@ -444,7 +462,6 @@ export function ansiLogToHTML(line) { |
|
|
|
|
width: 30%; |
|
|
|
|
max-width: 400px; |
|
|
|
|
overflow-y: scroll; |
|
|
|
|
margin-left: 10px; |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
.job-group-section .job-group-summary { |
|
|
|
@ -473,42 +490,64 @@ export function ansiLogToHTML(line) { |
|
|
|
|
padding-right: 3px; |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
.job-group-section .job-brief-list .job-brief-item { |
|
|
|
|
.job-brief-item { |
|
|
|
|
margin: 5px 0; |
|
|
|
|
padding: 10px; |
|
|
|
|
background: var(--color-info-bg); |
|
|
|
|
border-radius: 5px; |
|
|
|
|
text-decoration: none; |
|
|
|
|
display: flex; |
|
|
|
|
justify-items: center; |
|
|
|
|
flex-wrap: nowrap; |
|
|
|
|
justify-content: space-between; |
|
|
|
|
align-items: center; |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
.job-brief-item:hover { |
|
|
|
|
background-color: var(--color-secondary); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
.job-brief-item.selected { |
|
|
|
|
font-weight: var(--font-weight-bold); |
|
|
|
|
background-color: var(--color-secondary-dark-1); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
.job-brief-item:first-of-type { |
|
|
|
|
margin-top: 0; |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
.job-group-section .job-brief-list .job-brief-item .job-brief-rerun { |
|
|
|
|
float: right; |
|
|
|
|
border: none; |
|
|
|
|
background-color: transparent; |
|
|
|
|
outline: none; |
|
|
|
|
.job-brief-item .job-brief-rerun { |
|
|
|
|
cursor: pointer; |
|
|
|
|
transition: transform 0.2s; |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
.job-group-section .job-brief-list .job-brief-item .job-brief-rerun:hover { |
|
|
|
|
.job-brief-item .job-brief-rerun:hover { |
|
|
|
|
transform: scale(130%); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
.job-group-section .job-brief-list .job-brief-item .job-brief-link { |
|
|
|
|
flex-grow: 1; |
|
|
|
|
.job-brief-item .job-brief-link { |
|
|
|
|
display: flex; |
|
|
|
|
width: 100%; |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
.job-group-section .job-brief-list .job-brief-item .job-brief-link span { |
|
|
|
|
.job-brief-item .job-brief-link span { |
|
|
|
|
display: flex; |
|
|
|
|
align-items: center; |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
.job-group-section .job-brief-list .job-brief-item:hover { |
|
|
|
|
background-color: var(--color-secondary); |
|
|
|
|
.job-brief-item .job-brief-link .job-brief-name { |
|
|
|
|
display: block; |
|
|
|
|
width: 70%; |
|
|
|
|
color: var(--color-text); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
.job-brief-item .job-brief-link:hover { |
|
|
|
|
text-decoration: none; |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
.job-brief-item .job-brief-info { |
|
|
|
|
display: flex; |
|
|
|
|
align-items: center; |
|
|
|
|
width: 55px; |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
/* ================ */ |
|
|
|
@ -517,21 +556,27 @@ export function ansiLogToHTML(line) { |
|
|
|
|
.action-view-right { |
|
|
|
|
flex: 1; |
|
|
|
|
background-color: var(--color-console-bg); |
|
|
|
|
color: var(--color-console-fg); |
|
|
|
|
color: var(--color-secondary-dark-2); |
|
|
|
|
max-height: 100%; |
|
|
|
|
margin-right: 10px; |
|
|
|
|
width: 70%; |
|
|
|
|
display: flex; |
|
|
|
|
flex-direction: column; |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
.job-info-header .job-info-header-title { |
|
|
|
|
font-size: 150%; |
|
|
|
|
.job-info-header { |
|
|
|
|
padding: 10px; |
|
|
|
|
border-bottom: 1px solid var(--color-grey); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
.job-info-header .job-info-header-title { |
|
|
|
|
color: var(--color-console-fg); |
|
|
|
|
font-size: 16px; |
|
|
|
|
margin: 0; |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
.job-info-header .job-info-header-detail { |
|
|
|
|
padding: 0 10px 10px; |
|
|
|
|
border-bottom: 1px solid var(--color-grey); |
|
|
|
|
color: var(--color-secondary-dark-3); |
|
|
|
|
font-size: 12px; |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
.job-step-container { |
|
|
|
@ -543,6 +588,8 @@ export function ansiLogToHTML(line) { |
|
|
|
|
cursor: pointer; |
|
|
|
|
padding: 5px 10px; |
|
|
|
|
display: flex; |
|
|
|
|
align-items: center; |
|
|
|
|
user-select: none; |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
.job-step-container .job-step-summary .step-summary-msg { |
|
|
|
@ -553,8 +600,25 @@ export function ansiLogToHTML(line) { |
|
|
|
|
margin-left: 16px; |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
.job-step-container .job-step-summary:hover { |
|
|
|
|
.job-step-container .job-step-summary:hover, |
|
|
|
|
.job-step-container .job-step-summary.selected { |
|
|
|
|
color: var(--color-console-fg); |
|
|
|
|
background-color: var(--color-black-light); |
|
|
|
|
border-radius: 5px; |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
@media (max-width: 768px) { |
|
|
|
|
.action-view-body { |
|
|
|
|
flex-direction: column; |
|
|
|
|
} |
|
|
|
|
.action-view-left, .action-view-right { |
|
|
|
|
width: 100%; |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
.action-view-left { |
|
|
|
|
max-width: none; |
|
|
|
|
overflow-y: hidden; |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
</style> |
|
|
|
|
|
|
|
|
@ -576,12 +640,19 @@ export function ansiLogToHTML(line) { |
|
|
|
|
|
|
|
|
|
.job-step-section .job-step-logs { |
|
|
|
|
font-family: monospace, monospace; |
|
|
|
|
margin: 8px 0px; |
|
|
|
|
font-size: 12px; |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
.job-step-section .job-step-logs .job-log-line { |
|
|
|
|
display: flex; |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
.job-step-section .job-step-logs .job-log-line:hover { |
|
|
|
|
color: var(--color-console-fg); |
|
|
|
|
background-color: var(--color-console-hover-bg); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
.job-step-section .job-step-logs .job-log-line .line-num { |
|
|
|
|
width: 48px; |
|
|
|
|
color: var(--color-grey-light); |
|
|
|
|