Skip to content

Commit

Permalink
Merge branch 'master' into minim-golang-1.18
Browse files Browse the repository at this point in the history
  • Loading branch information
6543 authored Jun 16, 2022
2 parents 9075bd2 + 3c4d451 commit 7d69b32
Show file tree
Hide file tree
Showing 9 changed files with 220 additions and 95 deletions.
5 changes: 4 additions & 1 deletion web/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,10 @@
"pinia": "2.0.0",
"vue": "v3.2.20",
"vue-i18n": "9",
"vue-router": "4.0.10"
"vue-router": "4.0.10",
"xterm": "4.17.0",
"xterm-addon-fit": "0.5.0",
"xterm-addon-web-links": "0.5.1"
},
"devDependencies": {
"@iconify/json": "1.1.421",
Expand Down
6 changes: 4 additions & 2 deletions web/src/assets/locales/en.json
Original file line number Diff line number Diff line change
Expand Up @@ -165,12 +165,14 @@
"created": "Created",
"tasks": "Tasks",
"config": "Config",
"files": "Changed files ({0})",
"files": "Changed files ({files})",
"no_files": "No files have been changed.",
"execution_error": "Execution error",
"no_pipelines": "No pipelines have been started yet.",
"step_not_started": "This step hasn't started yet.",
"pipelines_for": "Pipelines for branch \"{0}\"",
"pipelines_for": "Pipelines for branch \"{branch}\"",
"exit_code": "exit code {exitCode}",
"loading": "Loading ...",

"actions": {
"cancel": "Cancel",
Expand Down
185 changes: 154 additions & 31 deletions web/src/components/repo/build/BuildLog.vue
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
<template>
<div v-if="build" class="font-mono bg-gray-700 pt-14 md:pt-4 dark:bg-dark-gray-700 p-4 overflow-y-scroll">
<div v-if="build" class="flex flex-col pt-10 md:pt-0">
<div
class="fixed top-0 left-0 w-full md:hidden flex px-4 py-2 bg-gray-600 dark:bg-dark-gray-800 text-gray-50"
@click="$emit('update:proc-id', null)"
Expand All @@ -8,36 +8,54 @@
<Icon name="close" class="ml-auto" />
</div>

<template v-if="!proc?.error">
<div v-for="logLine in logLines" :key="logLine.pos" class="flex items-center">
<div class="text-gray-500 text-sm w-4">{{ (logLine.pos || 0) + 1 }}</div>
<!-- eslint-disable-next-line vue/no-v-html -->
<div class="mx-4 text-gray-200 dark:text-gray-400" v-html="logLine.out" />
<div class="ml-auto text-gray-500 text-sm">{{ logLine.time || 0 }}s</div>
<div class="flex flex-grow flex-col bg-gray-300 dark:bg-dark-gray-700 md:m-2 md:mt-0 md:rounded-md overflow-hidden">
<div v-show="loadedLogs" class="w-full flex-grow p-2">
<div id="terminal" class="w-full h-full" />
</div>
<div v-if="proc?.end_time !== undefined" class="text-gray-500 text-sm mt-4 ml-8">
exit code {{ proc.exit_code }}

<div class="m-auto text-xl text-gray-500 dark:text-gray-500">
<span v-if="proc?.error" class="text-red-400">{{ proc.error }}</span>
<span v-else-if="proc?.state === 'skipped'" class="text-red-400">{{ $t('repo.build.actions.canceled') }}</span>
<span v-else-if="!proc?.start_time">{{ $t('repo.build.step_not_started') }}</span>
<div v-else-if="!loadedLogs">{{ $t('repo.build.loading') }}</div>
</div>
</template>

<div class="text-gray-300 mx-auto">
<span v-if="proc?.error" class="text-red-500">{{ proc.error }}</span>
<span v-else-if="proc?.state === 'skipped'" class="text-orange-300 dark:text-orange-800">
>{{ $t('repo.build.actions.canceled') }}</span
<div
v-if="proc?.end_time !== undefined"
:class="proc.exit_code == 0 ? 'dark:text-lime-400 text-lime-600' : 'dark:text-red-400 text-red-600'"
class="w-full bg-gray-400 dark:bg-dark-gray-800 text-md p-4"
>
<span v-else-if="!proc?.start_time" class="dark:text-gray-500">{{ $t('repo.build.step_not_started') }}</span>
{{ $t('repo.build.exit_code', { exitCode: proc.exit_code }) }}
</div>
</div>
</div>
</template>

<script lang="ts">
import AnsiConvert from 'ansi-to-html';
import { computed, defineComponent, inject, onBeforeUnmount, onMounted, PropType, Ref, toRef, watch } from 'vue';
import 'xterm/css/xterm.css';

import {
computed,
defineComponent,
inject,
nextTick,
onBeforeUnmount,
onMounted,
PropType,
Ref,
ref,
toRef,
watch,
} from 'vue';
import { Terminal } from 'xterm';
import { FitAddon } from 'xterm-addon-fit';
import { WebLinksAddon } from 'xterm-addon-web-links';

import Icon from '~/components/atomic/Icon.vue';
import useBuildProc from '~/compositions/useBuildProc';
import useApiClient from '~/compositions/useApiClient';
import { useDarkMode } from '~/compositions/useDarkMode';
import { Build, Repo } from '~/lib/api/types';
import { findProc } from '~/utils/helpers';
import { findProc, isProcFinished, isProcRunning } from '~/utils/helpers';

export default defineComponent({
name: 'BuildLog',
Expand Down Expand Up @@ -67,37 +85,142 @@ export default defineComponent({
const build = toRef(props, 'build');
const procId = toRef(props, 'procId');
const repo = inject<Ref<Repo>>('repo');
const buildProc = useBuildProc();
const apiClient = useApiClient();

const ansiConvert = new AnsiConvert({ escapeXML: true });
const logLines = computed(() => buildProc.logs.value?.map((l) => ({ ...l, out: ansiConvert.toHtml(l.out) })));
const loadedProcSlug = ref<string>();
const procSlug = computed(() => `${repo?.value.owner} - ${repo?.value.name} - ${build.value.id} - ${procId.value}`);
const proc = computed(() => build.value && findProc(build.value.procs || [], procId.value));
const stream = ref<EventSource>();
const term = ref(
new Terminal({
convertEol: true,
disableStdin: true,
theme: {
cursor: 'transparent',
},
}),
);
const fitAddon = ref(new FitAddon());
const loadedLogs = ref(true);
const autoScroll = ref(true); // TODO
async function loadLogs() {
if (loadedProcSlug.value === procSlug.value) {
return;
}
loadedProcSlug.value = procSlug.value;
loadedLogs.value = false;
term.value.reset();
term.value.write('\x1b[?25l');
function loadBuildProc() {
if (!repo) {
throw new Error('Unexpected: "repo" should be provided at this place');
}
if (!repo.value || !build.value || !proc.value) {
if (stream.value) {
stream.value.close();
}
// we do not have logs for skipped jobs
if (
!repo.value ||
!build.value ||
!proc.value ||
proc.value.state === 'skipped' ||
proc.value.state === 'killed'
) {
return;
}
buildProc.load(repo.value.owner, repo.value.name, build.value.number, proc.value);
if (isProcFinished(proc.value)) {
const logs = await apiClient.getLogs(repo.value.owner, repo.value.name, build.value.number, proc.value.pid);
term.value.write(
logs
.slice(Math.max(logs.length, 0) - 300, logs.length) // TODO: think about way to support lazy-loading more than last 300 logs (#776)
.map((line) => `${(line.pos || 0).toString().padEnd(logs.length.toString().length)} ${line.out}`)
.join(''),
);
loadedLogs.value = true;
}
if (isProcRunning(proc.value)) {
// load stream of parent process (which receives all child processes logs)
// TODO: change stream to only send data of single child process
stream.value = apiClient.streamLogs(
repo.value.owner,
repo.value.name,
build.value.number,
proc.value.ppid,
(l) => {
loadedLogs.value = true;
term.value.write(l.out, () => {
if (autoScroll.value) {
term.value.scrollToBottom();
}
});
},
);
}
}
function resize() {
fitAddon.value.fit();
}
onMounted(() => {
loadBuildProc();
onMounted(async () => {
term.value.loadAddon(fitAddon.value);
term.value.loadAddon(new WebLinksAddon());
await nextTick(() => {
const element = document.getElementById('terminal');
if (element === null) {
throw new Error('Unexpected: "terminal" should be provided at this place');
}
term.value.open(element);
fitAddon.value.fit();
window.addEventListener('resize', resize);
});
loadLogs();
});
watch([repo, build, procId], () => {
loadBuildProc();
watch(procSlug, () => {
loadLogs();
});
const { darkMode } = useDarkMode();
watch(
darkMode,
() => {
if (darkMode.value) {
term.value.options = {
theme: {
background: '#303440', // dark-gray-700
foreground: '#d3d3d3', // gray-...
},
};
} else {
term.value.options = {
theme: {
background: 'rgb(209,213,219)', // gray-300
foreground: '#000',
selection: '#000',
},
};
}
},
{ immediate: true },
);
onBeforeUnmount(() => {
buildProc.unload();
if (stream.value) {
stream.value.close();
}
window.removeEventListener('resize', resize);
});
return { logLines, proc };
return { proc, loadedLogs };
},
});
</script>
38 changes: 32 additions & 6 deletions web/src/components/repo/build/BuildProcList.vue
Original file line number Diff line number Diff line change
@@ -1,6 +1,19 @@
<template>
<div class="flex flex-col w-full md:w-3/12 text-gray-200 dark:text-gray-400 bg-gray-600 dark:bg-dark-gray-800">
<div class="flex py-4 px-2 mx-2 space-x-1 justify-between flex-shrink-0 border-b-1 dark:border-dark-gray-600">
<div class="flex flex-col w-full md:w-3/12 text-gray-600 dark:text-gray-400">
<div
class="
flex
md:ml-2
p-4
space-x-1
justify-between
flex-shrink-0
border-b-1
md:rounded-md
bg-gray-300
dark:border-b-dark-gray-600 dark:bg-dark-gray-700
"
>
<div class="flex space-x-1 items-center flex-shrink-0">
<div class="flex items-center"><img class="w-6" :src="build.author_avatar" /></div>
<span>{{ build.author }}</span>
Expand All @@ -25,7 +38,7 @@
<Icon name="commit" />
<span>{{ build.commit.slice(0, 10) }}</span>
</template>
<a v-else class="text-link flex items-center" :href="build.link_url" target="_blank">
<a v-else class="text-blue-700 dark:text-link flex items-center" :href="build.link_url" target="_blank">
<Icon name="commit" />
<span>{{ build.commit.slice(0, 10) }}</span>
</a>
Expand All @@ -40,7 +53,9 @@
<div class="md:absolute top-0 left-0 w-full">
<div v-for="proc in build.procs" :key="proc.id">
<div class="p-4 pb-1 flex flex-wrap items-center justify-between">
<span>{{ proc.name }}</span>
<div class="flex items-center">
<span class="ml-2">{{ proc.name }}</span>
</div>
<div v-if="proc.environ" class="text-xs">
<div v-for="(value, key) in proc.environ" :key="key">
<span
Expand All @@ -49,6 +64,7 @@
pr-1
py-0.5
bg-gray-800
text-gray-200
dark:bg-gray-600
border-2 border-gray-800
dark:border-gray-600
Expand All @@ -65,8 +81,18 @@
<div
v-for="job in proc.children"
:key="job.pid"
class="flex p-2 pl-6 cursor-pointer items-center hover:bg-gray-700 hover:dark:bg-dark-gray-900"
:class="{ 'bg-gray-700 !dark:bg-dark-gray-600': selectedProcId && selectedProcId === job.pid }"
class="
flex
mx-2
mb-1
p-2
pl-6
cursor-pointer
rounded-md
items-center
hover:bg-gray-300 hover:dark:bg-dark-gray-700
"
:class="{ 'bg-gray-300 !dark:bg-dark-gray-700': selectedProcId && selectedProcId === job.pid }"
@click="$emit('update:selected-proc-id', job.pid)"
>
<div v-if="['success'].includes(job.state)" class="w-2 h-2 bg-lime-400 rounded-full" />
Expand Down
49 changes: 0 additions & 49 deletions web/src/compositions/useBuildProc.ts

This file was deleted.

2 changes: 1 addition & 1 deletion web/src/views/repo/RepoBranch.vue
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
<template>
<div class="flex w-full mb-4 justify-center">
<span class="text-gray-600 dark:text-gray-500 text-xl">{{ $t('repo.build.pipelines_for', [branch]) }}</span>
<span class="text-gray-600 dark:text-gray-500 text-xl">{{ $t('repo.build.pipelines_for', { branch }) }}</span>
</div>
<BuildList :builds="builds" :repo="repo" />
</template>
Expand Down
Loading

0 comments on commit 7d69b32

Please sign in to comment.