mirror of
https://github.com/woodpecker-ci/woodpecker.git
synced 2024-11-29 05:11:04 +00:00
parent
28cd815c42
commit
cff393d644
6 changed files with 116 additions and 60 deletions
|
@ -220,6 +220,7 @@
|
||||||
"push": "Push",
|
"push": "Push",
|
||||||
"tag": "Tag",
|
"tag": "Tag",
|
||||||
"pr": "Pull Request",
|
"pr": "Pull Request",
|
||||||
|
"pr_closed": "Pull Request merged / closed",
|
||||||
"deploy": "Deploy",
|
"deploy": "Deploy",
|
||||||
"cron": "Cron",
|
"cron": "Cron",
|
||||||
"manual": "Manual",
|
"manual": "Manual",
|
||||||
|
@ -466,5 +467,15 @@
|
||||||
"internal_error": "Some internal error occurred",
|
"internal_error": "Some internal error occurred",
|
||||||
"registration_closed": "The registration is closed",
|
"registration_closed": "The registration is closed",
|
||||||
"access_denied": "You are not allowed to access this instance",
|
"access_denied": "You are not allowed to access this instance",
|
||||||
"invalid_state": "The OAuth state is invalid"
|
"invalid_state": "The OAuth state is invalid",
|
||||||
|
"by_user": "by {user}",
|
||||||
|
"pushed_to": "pushed to",
|
||||||
|
"closed": "closed",
|
||||||
|
"deployed_to": "deployed to",
|
||||||
|
"created": "created",
|
||||||
|
"triggered": "triggered",
|
||||||
|
"pipeline_duration": "Pipeline duration",
|
||||||
|
"pipeline_since": "Pipeline was created {created} minutes ago",
|
||||||
|
"pipeline_has_warnings": "The pipeline has warnings",
|
||||||
|
"pipeline_has_errors": "The pipeline has errors"
|
||||||
}
|
}
|
||||||
|
|
|
@ -12,7 +12,7 @@
|
||||||
<!-- eslint-disable-next-line @intlify/vue-i18n/no-raw-text -->
|
<!-- eslint-disable-next-line @intlify/vue-i18n/no-raw-text -->
|
||||||
{{ repo?.owner }} / {{ repo?.name }}
|
{{ repo?.owner }} / {{ repo?.name }}
|
||||||
</router-link>
|
</router-link>
|
||||||
<span class="whitespace-nowrap overflow-hidden overflow-ellipsis" :title="message">{{ title }}</span>
|
<span class="whitespace-nowrap overflow-hidden overflow-ellipsis" :title="message">{{ shortMessage }}</span>
|
||||||
<div class="flex flex-col mt-2">
|
<div class="flex flex-col mt-2">
|
||||||
<div class="flex space-x-2 items-center" :title="created">
|
<div class="flex space-x-2 items-center" :title="created">
|
||||||
<Icon name="since" />
|
<Icon name="since" />
|
||||||
|
@ -45,5 +45,5 @@ const repoStore = useRepoStore();
|
||||||
const pipeline = toRef(props, 'pipeline');
|
const pipeline = toRef(props, 'pipeline');
|
||||||
const repo = repoStore.getRepo(computed(() => pipeline.value.repo_id));
|
const repo = repoStore.getRepo(computed(() => pipeline.value.repo_id));
|
||||||
|
|
||||||
const { since, duration, message, title, created } = usePipeline(pipeline);
|
const { since, duration, shortMessage, message, created } = usePipeline(pipeline);
|
||||||
</script>
|
</script>
|
||||||
|
|
|
@ -1,8 +1,8 @@
|
||||||
<template>
|
<template>
|
||||||
<ListItem v-if="pipeline" class="p-0 w-full">
|
<ListItem v-if="pipeline" class="p-0 w-full">
|
||||||
<div class="flex w-11 items-center md:mr-4">
|
<div class="flex w-6 items-center md:mr-4">
|
||||||
<div
|
<div
|
||||||
class="h-full w-3"
|
class="h-full w-2 flex-shrink-0"
|
||||||
:class="{
|
:class="{
|
||||||
'bg-wp-state-warn-100': pipeline.status === 'pending',
|
'bg-wp-state-warn-100': pipeline.status === 'pending',
|
||||||
'bg-wp-state-error-100': pipelineStatusColors[pipeline.status] === 'red',
|
'bg-wp-state-error-100': pipelineStatusColors[pipeline.status] === 'red',
|
||||||
|
@ -11,35 +11,20 @@
|
||||||
'bg-wp-state-info-100': pipelineStatusColors[pipeline.status] === 'blue',
|
'bg-wp-state-info-100': pipelineStatusColors[pipeline.status] === 'blue',
|
||||||
}"
|
}"
|
||||||
/>
|
/>
|
||||||
<div class="w-8 flex flex-wrap justify-between items-center h-full">
|
<div class="w-6 flex flex-wrap justify-between items-center h-full">
|
||||||
<PipelineRunningIcon v-if="pipeline.status === 'started' || pipeline.status === 'running'" />
|
<PipelineRunningIcon v-if="pipeline.status === 'started' || pipeline.status === 'running'" />
|
||||||
<PipelineStatusIcon v-else class="mx-2 md:mx-3" :status="pipeline.status" />
|
<PipelineStatusIcon v-else class="mx-2 md:mx-3" :status="pipeline.status" />
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="flex py-2 px-4 flex-grow min-w-0 <md:flex-wrap">
|
<div class="flex py-2 px-4 flex-grow min-w-0 <md:flex-wrap gap-2">
|
||||||
<div class="<md:hidden flex items-center flex-shrink-0">
|
<div class="flex flex-col min-w-0 justify-center gap-2">
|
||||||
<Icon v-if="pipeline.event === 'cron'" name="stopwatch" class="text-wp-text-100" />
|
<span class="text-wp-text-100 text-lg whitespace-nowrap overflow-hidden overflow-ellipsis" :title="message">
|
||||||
<img v-else class="rounded-md w-8" :src="pipeline.author_avatar" />
|
{{ shortMessage }}
|
||||||
</div>
|
|
||||||
|
|
||||||
<div class="w-full md:w-auto md:mx-4 flex items-center min-w-0">
|
|
||||||
<!-- eslint-disable-next-line @intlify/vue-i18n/no-raw-text -->
|
|
||||||
<span class="text-wp-text-alt-100 <md:hidden">#{{ pipeline.number }}</span>
|
|
||||||
<!-- eslint-disable-next-line @intlify/vue-i18n/no-raw-text -->
|
|
||||||
<span class="text-wp-text-alt-100 <md:hidden mx-2">-</span>
|
|
||||||
<span
|
|
||||||
class="text-wp-text-100 <md:underline whitespace-nowrap overflow-hidden overflow-ellipsis"
|
|
||||||
:title="message"
|
|
||||||
>
|
|
||||||
{{ title }}
|
|
||||||
</span>
|
</span>
|
||||||
</div>
|
|
||||||
|
|
||||||
<div
|
<div class="flex <md:flex-wrap gap-1 text-wp-text-alt-100">
|
||||||
class="grid grid-rows-2 grid-flow-col w-full md:ml-auto md:w-96 py-2 gap-x-4 gap-y-2 flex-shrink-0 text-wp-text-100"
|
<div class="flex items-center" :title="pipelineEventTitle">
|
||||||
>
|
|
||||||
<div class="flex space-x-2 items-center min-w-0">
|
|
||||||
<Icon v-if="pipeline.event === 'pull_request'" name="pull-request" />
|
<Icon v-if="pipeline.event === 'pull_request'" name="pull-request" />
|
||||||
<Icon v-else-if="pipeline.event === 'pull_request_closed'" name="pull-request-closed" />
|
<Icon v-else-if="pipeline.event === 'pull_request_closed'" name="pull-request-closed" />
|
||||||
<Icon v-else-if="pipeline.event === 'deployment'" name="deployment" />
|
<Icon v-else-if="pipeline.event === 'deployment'" name="deployment" />
|
||||||
|
@ -47,31 +32,57 @@
|
||||||
<Icon v-else-if="pipeline.event === 'cron'" name="push" />
|
<Icon v-else-if="pipeline.event === 'cron'" name="push" />
|
||||||
<Icon v-else-if="pipeline.event === 'manual'" name="manual-pipeline" />
|
<Icon v-else-if="pipeline.event === 'manual'" name="manual-pipeline" />
|
||||||
<Icon v-else name="push" />
|
<Icon v-else name="push" />
|
||||||
<span class="truncate">{{ prettyRef }}</span>
|
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="flex space-x-2 items-center min-w-0">
|
<a :href="pipeline.forge_url" target="_blank" class="underline" :title="pipeline.commit">
|
||||||
<Icon name="commit" />
|
<Badge :label="pipeline.commit.slice(0, 7)" />
|
||||||
<span class="truncate">{{ pipeline.commit.slice(0, 10) }}</span>
|
</a>
|
||||||
|
|
||||||
|
<span v-if="pipeline.event === 'pull_request' || pipeline.event === 'push'">{{ $t('pushed_to') }}</span>
|
||||||
|
<span v-if="pipeline.event === 'pull_request_closed'">{{ $t('closed') }}</span>
|
||||||
|
<span v-if="pipeline.event === 'deployment'">{{ $t('deployed_to') }}</span>
|
||||||
|
<span v-if="pipeline.event === 'tag' || pipeline.event === 'release'">{{ $t('created') }}</span>
|
||||||
|
<span v-if="pipeline.event === 'cron' || pipeline.event === 'manual'">{{ $t('triggered') }}</span>
|
||||||
|
<span v-else>{{ $t('triggered') }}</span>
|
||||||
|
<Badge
|
||||||
|
v-if="prettyRef"
|
||||||
|
:title="prTitleWithDescription"
|
||||||
|
:label="prTitle ? `${prettyRef} (${truncate(prTitle, 30)})` : prettyRef"
|
||||||
|
/>
|
||||||
|
<span class="truncate">{{ $t('by_user', { user: pipeline.author }) }}</span>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="flex space-x-2 items-center min-w-0">
|
<div class="flex min-w-0 <md:w-full gap-2 justify-between items-center md:ml-auto relative">
|
||||||
|
<div class="flex flex-col gap-2 text-wp-text-alt-100">
|
||||||
|
<div class="flex gap-2 items-center min-w-0" :title="$t('pipeline_duration')">
|
||||||
<Icon name="duration" />
|
<Icon name="duration" />
|
||||||
<span class="truncate">{{ duration }}</span>
|
<span class="truncate">{{ duration }}</span>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="flex space-x-2 items-center min-w-0" :title="created">
|
<div class="flex gap-2 items-center min-w-0" :title="$t('pipeline_since', { created })">
|
||||||
<Icon name="since" />
|
<Icon name="since" />
|
||||||
<span>{{ since }}</span>
|
<span>{{ since }}</span>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
<Icon v-if="pipeline.event === 'cron'" name="stopwatch" class="text-wp-text-100" />
|
||||||
|
<img v-else class="rounded-md w-8 flex-shrink-0" :src="pipeline.author_avatar" :title="pipeline.author" />
|
||||||
|
|
||||||
|
<div v-if="pipeline.errors" class="flex items-center absolute -top-1 -right-2">
|
||||||
|
<Icon v-if="hasErrors" name="attention" class="text-wp-state-error-100" :title="$t('pipeline_has_errors')" />
|
||||||
|
<Icon v-else name="warning" class="text-wp-state-warn-100" :title="$t('pipeline_has_warnings')" />
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</ListItem>
|
</ListItem>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script lang="ts" setup>
|
<script lang="ts" setup>
|
||||||
import { toRef } from 'vue';
|
import { computed, toRef } from 'vue';
|
||||||
|
import { useI18n } from 'vue-i18n';
|
||||||
|
|
||||||
|
import Badge from '~/components/atomic/Badge.vue';
|
||||||
import Icon from '~/components/atomic/Icon.vue';
|
import Icon from '~/components/atomic/Icon.vue';
|
||||||
import ListItem from '~/components/atomic/ListItem.vue';
|
import ListItem from '~/components/atomic/ListItem.vue';
|
||||||
import { pipelineStatusColors } from '~/components/repo/pipeline/pipeline-status';
|
import { pipelineStatusColors } from '~/components/repo/pipeline/pipeline-status';
|
||||||
|
@ -79,11 +90,38 @@ import PipelineRunningIcon from '~/components/repo/pipeline/PipelineRunningIcon.
|
||||||
import PipelineStatusIcon from '~/components/repo/pipeline/PipelineStatusIcon.vue';
|
import PipelineStatusIcon from '~/components/repo/pipeline/PipelineStatusIcon.vue';
|
||||||
import usePipeline from '~/compositions/usePipeline';
|
import usePipeline from '~/compositions/usePipeline';
|
||||||
import type { Pipeline } from '~/lib/api/types';
|
import type { Pipeline } from '~/lib/api/types';
|
||||||
|
import { truncate } from '~/utils/locale';
|
||||||
|
|
||||||
const props = defineProps<{
|
const props = defineProps<{
|
||||||
pipeline: Pipeline;
|
pipeline: Pipeline;
|
||||||
}>();
|
}>();
|
||||||
|
|
||||||
|
const { t } = useI18n();
|
||||||
|
|
||||||
const pipeline = toRef(props, 'pipeline');
|
const pipeline = toRef(props, 'pipeline');
|
||||||
const { since, duration, message, title, prettyRef, created } = usePipeline(pipeline);
|
const { since, duration, message, shortMessage, prTitle, prTitleWithDescription, prettyRef, created } =
|
||||||
|
usePipeline(pipeline);
|
||||||
|
|
||||||
|
const hasErrors = computed(() => pipeline.value.errors?.some((e) => !e.is_warning));
|
||||||
|
|
||||||
|
const pipelineEventTitle = computed(() => {
|
||||||
|
switch (pipeline.value.event) {
|
||||||
|
case 'pull_request':
|
||||||
|
return t('repo.pipeline.event.pr');
|
||||||
|
case 'pull_request_closed':
|
||||||
|
return t('repo.pipeline.event.pr_closed');
|
||||||
|
case 'deployment':
|
||||||
|
return t('repo.pipeline.event.deploy');
|
||||||
|
case 'tag':
|
||||||
|
return t('repo.pipeline.event.tag');
|
||||||
|
case 'release':
|
||||||
|
return t('repo.pipeline.event.release');
|
||||||
|
case 'cron':
|
||||||
|
return t('repo.pipeline.event.cron');
|
||||||
|
case 'manual':
|
||||||
|
return t('repo.pipeline.event.manual');
|
||||||
|
default:
|
||||||
|
return t('repo.pipeline.event.push');
|
||||||
|
}
|
||||||
|
});
|
||||||
</script>
|
</script>
|
||||||
|
|
|
@ -27,7 +27,8 @@ export default (pipeline: Ref<Pipeline | undefined>) => {
|
||||||
const i18n = useI18n();
|
const i18n = useI18n();
|
||||||
const since = computed(() => {
|
const since = computed(() => {
|
||||||
if (sinceRaw.value === 0) {
|
if (sinceRaw.value === 0) {
|
||||||
return i18n.t('time.not_started');
|
// return i18n.t('time.not_started');
|
||||||
|
return '-';
|
||||||
}
|
}
|
||||||
|
|
||||||
if (sinceElapsed.value === undefined) {
|
if (sinceElapsed.value === undefined) {
|
||||||
|
@ -73,15 +74,11 @@ export default (pipeline: Ref<Pipeline | undefined>) => {
|
||||||
return prettyDuration(durationElapsed.value);
|
return prettyDuration(durationElapsed.value);
|
||||||
});
|
});
|
||||||
|
|
||||||
const message = computed(() => {
|
const message = computed(() => convertEmojis(pipeline.value?.message ?? ''));
|
||||||
if (!pipeline.value) {
|
const shortMessage = computed(() => message.value.split('\n')[0]);
|
||||||
return '';
|
|
||||||
}
|
|
||||||
|
|
||||||
return convertEmojis(pipeline.value.message);
|
const prTitleWithDescription = computed(() => convertEmojis(pipeline.value?.title ?? ''));
|
||||||
});
|
const prTitle = computed(() => prTitleWithDescription.value.split('\n')[0]);
|
||||||
|
|
||||||
const title = computed(() => message.value.split('\n')[0]);
|
|
||||||
|
|
||||||
const prettyRef = computed(() => {
|
const prettyRef = computed(() => {
|
||||||
if (pipeline.value?.event === 'push' || pipeline.value?.event === 'deployment') {
|
if (pipeline.value?.event === 'push' || pipeline.value?.event === 'deployment') {
|
||||||
|
@ -117,5 +114,5 @@ export default (pipeline: Ref<Pipeline | undefined>) => {
|
||||||
return toLocaleString(new Date(start * 1000));
|
return toLocaleString(new Date(start * 1000));
|
||||||
});
|
});
|
||||||
|
|
||||||
return { since, duration, message, title, prettyRef, created };
|
return { since, duration, message, shortMessage, prTitle, prTitleWithDescription, prettyRef, created };
|
||||||
};
|
};
|
||||||
|
|
|
@ -6,3 +6,11 @@ export function getUserLanguage(): string {
|
||||||
|
|
||||||
return selectedLocale;
|
return selectedLocale;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export function truncate(text: string, length: number): string {
|
||||||
|
if (text.length <= length) {
|
||||||
|
return text;
|
||||||
|
}
|
||||||
|
|
||||||
|
return `${text.slice(0, length)}...`;
|
||||||
|
}
|
||||||
|
|
|
@ -26,7 +26,9 @@
|
||||||
<span class="flex-shrink-0 text-center">{{ $t('repo.pipeline.pipeline', { pipelineId }) }}</span>
|
<span class="flex-shrink-0 text-center">{{ $t('repo.pipeline.pipeline', { pipelineId }) }}</span>
|
||||||
<!-- eslint-disable-next-line @intlify/vue-i18n/no-raw-text -->
|
<!-- eslint-disable-next-line @intlify/vue-i18n/no-raw-text -->
|
||||||
<span class="hidden md:inline-block">-</span>
|
<span class="hidden md:inline-block">-</span>
|
||||||
<span class="min-w-0 whitespace-nowrap overflow-hidden overflow-ellipsis" :title="message">{{ title }}</span>
|
<span class="min-w-0 whitespace-nowrap overflow-hidden overflow-ellipsis" :title="message">{{
|
||||||
|
shortMessage
|
||||||
|
}}</span>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<template v-if="repoPermissions!.push && pipeline.status !== 'declined' && pipeline.status !== 'blocked'">
|
<template v-if="repoPermissions!.push && pipeline.status !== 'declined' && pipeline.status !== 'blocked'">
|
||||||
|
@ -138,7 +140,7 @@ if (!repo || !repoPermissions) {
|
||||||
}
|
}
|
||||||
|
|
||||||
const pipeline = pipelineStore.getPipeline(repositoryId, pipelineId);
|
const pipeline = pipelineStore.getPipeline(repositoryId, pipelineId);
|
||||||
const { since, duration, created, message, title } = usePipeline(pipeline);
|
const { since, duration, created, message, shortMessage } = usePipeline(pipeline);
|
||||||
provide('pipeline', pipeline);
|
provide('pipeline', pipeline);
|
||||||
|
|
||||||
const pipelineConfigs = ref<PipelineConfig[]>();
|
const pipelineConfigs = ref<PipelineConfig[]>();
|
||||||
|
|
Loading…
Reference in a new issue