mirror of
https://github.com/go-gitea/gitea.git
synced 2025-07-24 22:36:17 +02:00
view logs
This commit is contained in:
parent
4f2330623e
commit
7ba22b740f
@ -9,7 +9,11 @@
|
|||||||
</div>
|
</div>
|
||||||
<div class="job-brief-list">
|
<div class="job-brief-list">
|
||||||
<a class="job-brief-item" v-for="job in jobGroup.jobs" :key="job.id">
|
<a class="job-brief-item" v-for="job in jobGroup.jobs" :key="job.id">
|
||||||
<SvgIcon name="octicon-check-circle-fill"/> {{ job.name }}
|
<SvgIcon name="octicon-check-circle-fill" class="green" v-if="job.status === 'success'"/>
|
||||||
|
<SvgIcon name="octicon-skip" class="ui text grey" v-else-if="job.status === 'skipped'"/>
|
||||||
|
<SvgIcon name="octicon-clock" class="ui text yellow" v-else-if="job.status === 'waiting'"/>
|
||||||
|
<SvgIcon name="octicon-x-circle-fill" class="red" v-else/>
|
||||||
|
{{ job.name }}
|
||||||
</a>
|
</a>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
@ -24,19 +28,23 @@
|
|||||||
{{ currentJobInfo.detail }}
|
{{ currentJobInfo.detail }}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="job-stage-container">
|
<div class="job-step-container">
|
||||||
<div class="job-stage-section" v-for="(jobStage, i) in currentJobStages" :key="i">
|
<div class="job-step-section" v-for="(jobStep, i) in currentJobSteps" :key="i">
|
||||||
<div class="job-stage-summary" @click.stop="toggleStageLogs(i)">
|
<div class="job-step-summary" @click.stop="toggleStepLogs(i)">
|
||||||
<SvgIcon name="octicon-chevron-down" v-show="currentJobStagesStates[i].expanded"/>
|
<SvgIcon name="octicon-chevron-down" class="mr-3" v-show="currentJobStepsStates[i].expanded"/>
|
||||||
<SvgIcon name="octicon-chevron-right" v-show="!currentJobStagesStates[i].expanded"/>
|
<SvgIcon name="octicon-chevron-right" class="mr-3" v-show="!currentJobStepsStates[i].expanded"/>
|
||||||
|
|
||||||
<SvgIcon name="octicon-check-circle-fill"/>
|
<SvgIcon name="octicon-check-circle-fill" class="green mr-3 " v-if="jobStep.status === 'success'"/>
|
||||||
|
<SvgIcon name="octicon-skip" class="ui text grey mr-3 " v-else-if="jobStep.status === 'skipped'"/>
|
||||||
|
<SvgIcon name="octicon-clock" class="ui text yellow mr-3 " v-else-if="jobStep.status === 'waiting'"/>
|
||||||
|
<SvgIcon name="octicon-x-circle-fill" class="red mr-3 " v-else/>
|
||||||
|
|
||||||
{{ jobStage.summary }}
|
<span class="step-summary-msg">{{ jobStep.summary }}</span>
|
||||||
|
<span class="step-summary-dur">{{ Math.round(jobStep.duration/1000) }}s</span>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<!-- the log elements could be a lot, do not use v-if to destroy/reconstruct the DOM -->
|
<!-- the log elements could be a lot, do not use v-if to destroy/reconstruct the DOM -->
|
||||||
<div class="job-stage-logs" ref="elJobStageLogs" v-show="currentJobStagesStates[i].expanded">
|
<div class="job-step-logs" ref="elJobStepLogs" v-show="currentJobStepsStates[i].expanded">
|
||||||
<!--
|
<!--
|
||||||
<div class="job-log-group">
|
<div class="job-log-group">
|
||||||
<div class="job-log-group-summary"></div>
|
<div class="job-log-group-summary"></div>
|
||||||
@ -70,13 +78,13 @@ const sfc = {
|
|||||||
return {
|
return {
|
||||||
// internal state
|
// internal state
|
||||||
loading: false,
|
loading: false,
|
||||||
currentJobStagesStates: [],
|
currentJobStepsStates: [],
|
||||||
|
|
||||||
// provided by backend
|
// provided by backend
|
||||||
buildInfo: {},
|
buildInfo: {},
|
||||||
allJobGroups: [],
|
allJobGroups: [],
|
||||||
currentJobInfo: {},
|
currentJobInfo: {},
|
||||||
currentJobStages: [],
|
currentJobSteps: [],
|
||||||
};
|
};
|
||||||
},
|
},
|
||||||
|
|
||||||
@ -87,12 +95,12 @@ const sfc = {
|
|||||||
},
|
},
|
||||||
|
|
||||||
methods: {
|
methods: {
|
||||||
stageLogsGetActiveContainer(idx) {
|
stepLogsGetActiveContainer(idx) {
|
||||||
const el = this.$refs.elJobStageLogs[idx];
|
const el = this.$refs.elJobStepLogs[idx];
|
||||||
return el._stageLogsActiveContainer ?? el;
|
return el._stepLogsActiveContainer ?? el;
|
||||||
},
|
},
|
||||||
stageLogsGroupBegin(idx) {
|
stepLogsGroupBegin(idx) {
|
||||||
const el = this.$refs.elJobStageLogs[idx];
|
const el = this.$refs.elJobStepLogs[idx];
|
||||||
|
|
||||||
const elJobLogGroup = document.createElement('div');
|
const elJobLogGroup = document.createElement('div');
|
||||||
elJobLogGroup.classList.add('job-log-group');
|
elJobLogGroup.classList.add('job-log-group');
|
||||||
@ -105,30 +113,42 @@ const sfc = {
|
|||||||
|
|
||||||
elJobLogGroup.appendChild(elJobLogGroupSummary);
|
elJobLogGroup.appendChild(elJobLogGroupSummary);
|
||||||
elJobLogGroup.appendChild(elJobLogList);
|
elJobLogGroup.appendChild(elJobLogList);
|
||||||
el._stageLogsActiveContainer = elJobLogList;
|
el._stepLogsActiveContainer = elJobLogList;
|
||||||
},
|
},
|
||||||
stageLogsGroupEnd(idx) {
|
stepLogsGroupEnd(idx) {
|
||||||
const el = this.$refs.elJobStageLogs[idx];
|
const el = this.$refs.elJobStepLogs[idx];
|
||||||
el._stageLogsActiveContainer = null;
|
el._stepLogsActiveContainer = null;
|
||||||
},
|
},
|
||||||
|
|
||||||
toggleStageLogs(idx) {
|
toggleStepLogs(idx) {
|
||||||
this.currentJobStagesStates[idx].expanded = !this.currentJobStagesStates[idx].expanded;
|
this.currentJobStepsStates[idx].expanded = !this.currentJobStepsStates[idx].expanded;
|
||||||
},
|
},
|
||||||
|
|
||||||
createLogLine(msg, time) {
|
createLogLine(line) {
|
||||||
const el = document.createElement('div');
|
const el = document.createElement('div');
|
||||||
el.classList.add('job-log-line');
|
el.classList.add('job-log-line');
|
||||||
el.innerText = msg;
|
el._jobLogTime = line.t;
|
||||||
el._jobLogTime = time;
|
|
||||||
|
const elLineNum = document.createElement('line-num');
|
||||||
|
elLineNum.innerText = line.ln;
|
||||||
|
el.appendChild(elLineNum);
|
||||||
|
|
||||||
|
const elLogTime = document.createElement('log-time');
|
||||||
|
elLogTime.innerText = new Date(line.t).toUTCString();
|
||||||
|
el.appendChild(elLogTime);
|
||||||
|
|
||||||
|
const elLogMsg = document.createElement('log-msg');
|
||||||
|
elLogMsg.innerText = line.m;
|
||||||
|
el.appendChild(elLogMsg);
|
||||||
|
|
||||||
return el;
|
return el;
|
||||||
},
|
},
|
||||||
|
|
||||||
appendLogs(stageIndex, logLines) {
|
appendLogs(stepIndex, logLines) {
|
||||||
for (const line of logLines) {
|
for (const line of logLines) {
|
||||||
// group: ##[group]GroupTItle , ##[endgroup]
|
// TODO: group support: ##[group]GroupTitle , ##[endgroup]
|
||||||
const el = this.stageLogsGetActiveContainer(stageIndex);
|
const el = this.stepLogsGetActiveContainer(stepIndex);
|
||||||
el.append(this.createLogLine(line.m, line.t));
|
el.append(this.createLogLine(line));
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
@ -136,28 +156,35 @@ const sfc = {
|
|||||||
const stateData = {
|
const stateData = {
|
||||||
buildInfo: {title: 'The Demo Build'},
|
buildInfo: {title: 'The Demo Build'},
|
||||||
allJobGroups: [
|
allJobGroups: [
|
||||||
{summary: 'Job Group Foo', jobs: [{id: 1, name: 'Job A'}, {id: 2, name: 'Job B'}]},
|
{summary: 'Job Group Foo', jobs: [{id: 1, name: 'Job A', status: 'success'}, {id: 2, name: 'Job B', status: 'error'}]},
|
||||||
{summary: 'Job Group Bar', jobs: [{id: 3, name: 'Job X'}, {id: 4, name: 'Job Y'}]},
|
{summary: 'Job Group Bar', jobs: [{id: 3, name: 'Job X', status: 'skipped'}, {id: 4, name: 'Job Y', status: 'waiting'}]},
|
||||||
],
|
],
|
||||||
currentJobInfo: {title: 'the job title', detail: ' succeeded 3 hours ago in 11s'},
|
currentJobInfo: {title: 'the job title', detail: ' succeeded 3 hours ago in 11s'},
|
||||||
currentJobStages: [
|
currentJobSteps: [
|
||||||
{summary: 'Job Stage 1'},
|
{summary: 'Job Step 1', duration: 3000, status: 'success'},
|
||||||
{summary: 'Job Stage 2'},
|
{summary: 'Job Step 2', duration: 3000, status: 'error'},
|
||||||
|
{summary: 'Job Step 3', duration: 3000, status: 'skipped'},
|
||||||
|
{summary: 'Job Step 4', duration: 3000, status: 'waiting'},
|
||||||
],
|
],
|
||||||
};
|
};
|
||||||
const logsData = {streamingLogs: []};
|
const logsData = {streamingLogs: []};
|
||||||
|
|
||||||
for (const reqCursor of reqData.stageLogCursors) {
|
for (const reqCursor of reqData.stepLogCursors) {
|
||||||
if (!reqCursor.expanded) continue;
|
if (!reqCursor.expanded) continue;
|
||||||
// if (reqCursor.cursor > 100) continue;
|
// if (reqCursor.cursor > 100) continue;
|
||||||
const stageIndex = reqCursor.stageIndex;
|
const stepIndex = reqCursor.stepIndex;
|
||||||
let cursor = reqCursor.cursor;
|
let cursor = reqCursor.cursor;
|
||||||
const lines = [];
|
const lines = [];
|
||||||
for (let i = 0; i < 110; i++) {
|
for (let i = 0; i < 110; i++) {
|
||||||
lines.push({m: `hello world ${Date.now()}, cursor: ${cursor}`, t: Date.now()});
|
lines.push({
|
||||||
|
ln: cursor+0, // line number
|
||||||
|
m: `hello world ${Date.now()}, cursor: ${cursor}`,
|
||||||
|
t: Date.now(),
|
||||||
|
d: 3000, // duration
|
||||||
|
});
|
||||||
cursor++;
|
cursor++;
|
||||||
}
|
}
|
||||||
logsData.streamingLogs.push({stageIndex, cursor, lines});
|
logsData.streamingLogs.push({stepIndex, cursor, lines});
|
||||||
}
|
}
|
||||||
return {stateData, logsData};
|
return {stateData, logsData};
|
||||||
},
|
},
|
||||||
@ -172,8 +199,8 @@ const sfc = {
|
|||||||
if (this.loading) return;
|
if (this.loading) return;
|
||||||
this.loading = true;
|
this.loading = true;
|
||||||
|
|
||||||
const stageLogCursors = this.currentJobStagesStates.map((it, idx) => {return {stageIndex: idx, cursor: it.cursor, expanded: it.expanded}});
|
const stepLogCursors = this.currentJobStepsStates.map((it, idx) => {return {stepIndex: idx, cursor: it.cursor, expanded: it.expanded}});
|
||||||
const reqData = {stageLogCursors};
|
const reqData = {stepLogCursors};
|
||||||
|
|
||||||
// const data = await this.fetchJobData();
|
// const data = await this.fetchJobData();
|
||||||
const data = this.fetchMockData(reqData);
|
const data = this.fetchMockData(reqData);
|
||||||
@ -183,14 +210,14 @@ const sfc = {
|
|||||||
for (const [key, value] of Object.entries(data.stateData)) {
|
for (const [key, value] of Object.entries(data.stateData)) {
|
||||||
this[key] = value;
|
this[key] = value;
|
||||||
}
|
}
|
||||||
for (let i = 0; i < this.currentJobStages.length; i++) {
|
for (let i = 0; i < this.currentJobSteps.length; i++) {
|
||||||
if (!this.currentJobStagesStates[i]) {
|
if (!this.currentJobStepsStates[i]) {
|
||||||
this.$set(this.currentJobStagesStates, i, {cursor: null, expanded: false});
|
this.$set(this.currentJobStepsStates, i, {cursor: null, expanded: false});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
for (const [_, logs] of data.logsData.streamingLogs.entries()) {
|
for (const [_, logs] of data.logsData.streamingLogs.entries()) {
|
||||||
this.currentJobStagesStates[logs.stageIndex].cursor = logs.cursor;
|
this.currentJobStepsStates[logs.stepIndex].cursor = logs.cursor;
|
||||||
this.appendLogs(logs.stageIndex, logs.lines);
|
this.appendLogs(logs.stepIndex, logs.lines);
|
||||||
}
|
}
|
||||||
} finally {
|
} finally {
|
||||||
this.loading = false;
|
this.loading = false;
|
||||||
@ -237,15 +264,18 @@ export function initRepositoryBuildView() {
|
|||||||
|
|
||||||
.job-group-section {
|
.job-group-section {
|
||||||
.job-group-summary {
|
.job-group-summary {
|
||||||
|
margin: 5px 0;
|
||||||
|
padding: 10px;
|
||||||
}
|
}
|
||||||
|
|
||||||
.job-brief-list {
|
.job-brief-list {
|
||||||
a.job-brief-item {
|
a.job-brief-item {
|
||||||
display: block;
|
display: block;
|
||||||
margin: 5px 0;
|
margin: 5px 0;
|
||||||
padding: 5px;
|
padding: 10px;
|
||||||
background: #f8f8f8;
|
background: #f8f8f8;
|
||||||
border-radius: 5px;
|
border-radius: 5px;
|
||||||
|
text-decoration: none;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -277,15 +307,23 @@ export function initRepositoryBuildView() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
.job-stage-container {
|
.job-step-container {
|
||||||
max-height: 100%;
|
max-height: 100%;
|
||||||
overflow: auto;
|
overflow: auto;
|
||||||
|
|
||||||
.job-stage-summary {
|
.job-step-summary {
|
||||||
cursor: pointer;
|
cursor: pointer;
|
||||||
padding: 5px 0;
|
padding: 5px 10px;
|
||||||
|
display: flex;
|
||||||
|
|
||||||
|
.step-summary-msg {
|
||||||
|
flex: 1;
|
||||||
|
}
|
||||||
|
.step-summary-dur {
|
||||||
|
margin-left: 16px;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
.job-stage-summary:hover {
|
.job-step-summary:hover {
|
||||||
background-color: #333;
|
background-color: #333;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -293,13 +331,27 @@ export function initRepositoryBuildView() {
|
|||||||
|
|
||||||
<style lang="less">
|
<style lang="less">
|
||||||
// some elements are not managed by vue, so we need to use global style
|
// some elements are not managed by vue, so we need to use global style
|
||||||
.job-stage-section {
|
.job-step-section {
|
||||||
margin: 10px;
|
margin: 10px;
|
||||||
.job-stage-logs {
|
.job-step-logs {
|
||||||
.job-log-line {
|
.job-log-line {
|
||||||
margin-left: 20px;
|
display: flex;
|
||||||
|
line-num {
|
||||||
|
width: 48px;
|
||||||
|
color: #555;
|
||||||
|
text-align: right;
|
||||||
|
}
|
||||||
|
log-time {
|
||||||
|
color: #777;
|
||||||
|
margin-left: 16px;
|
||||||
|
}
|
||||||
|
log-msg {
|
||||||
|
flex: 1;
|
||||||
|
margin-left: 16px;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// TODO: group support
|
||||||
.job-log-group {
|
.job-log-group {
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -25,6 +25,8 @@ import octiconFile from '../../public/img/svg/octicon-file.svg';
|
|||||||
import octiconSidebarExpand from '../../public/img/svg/octicon-sidebar-expand.svg';
|
import octiconSidebarExpand from '../../public/img/svg/octicon-sidebar-expand.svg';
|
||||||
import octiconSidebarCollapse from '../../public/img/svg/octicon-sidebar-collapse.svg';
|
import octiconSidebarCollapse from '../../public/img/svg/octicon-sidebar-collapse.svg';
|
||||||
import octiconCheckCircleFill from '../../public/img/svg/octicon-check-circle-fill.svg';
|
import octiconCheckCircleFill from '../../public/img/svg/octicon-check-circle-fill.svg';
|
||||||
|
import octiconXCircleFill from '../../public/img/svg/octicon-x-circle-fill.svg';
|
||||||
|
import octiconSkip from '../../public/img/svg/octicon-skip.svg';
|
||||||
|
|
||||||
|
|
||||||
export const svgs = {
|
export const svgs = {
|
||||||
@ -55,6 +57,8 @@ export const svgs = {
|
|||||||
'octicon-diff-removed': octiconDiffRemoved,
|
'octicon-diff-removed': octiconDiffRemoved,
|
||||||
'octicon-diff-renamed': octiconDiffRenamed,
|
'octicon-diff-renamed': octiconDiffRenamed,
|
||||||
'octicon-check-circle-fill': octiconCheckCircleFill,
|
'octicon-check-circle-fill': octiconCheckCircleFill,
|
||||||
|
'octicon-x-circle-fill': octiconXCircleFill,
|
||||||
|
'octicon-skip': octiconSkip,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
|
Loading…
x
Reference in New Issue
Block a user