Show yml config of pipeline in UI (#649)

closes #89
This commit is contained in:
Anbraten 2022-01-09 03:59:45 +01:00 committed by GitHub
parent 5e595978fa
commit 37c82b905c
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
8 changed files with 123 additions and 2 deletions

View file

@ -180,6 +180,30 @@ func GetProcLogs(c *gin.Context) {
}
}
func GetBuildConfig(c *gin.Context) {
_store := store.FromContext(c)
repo := session.Repo(c)
num, err := strconv.ParseInt(c.Param("number"), 10, 64)
if err != nil {
_ = c.AbortWithError(http.StatusBadRequest, err)
return
}
build, err := _store.GetBuildNumber(repo, num)
if err != nil {
_ = c.AbortWithError(http.StatusInternalServerError, err)
return
}
configs, err := _store.ConfigsForBuild(build.ID)
if err != nil {
c.String(http.StatusInternalServerError, err.Error())
return
}
c.JSON(http.StatusOK, configs)
}
// DeleteBuild cancels a build
func DeleteBuild(c *gin.Context) {
_store := store.FromContext(c)

View file

@ -61,6 +61,7 @@ func apiRoutes(e *gin.Engine) {
repo.GET("/builds", api.GetBuilds)
repo.GET("/builds/:number", api.GetBuild)
repo.GET("/builds/:number/config", api.GetBuildConfig)
// requires push permissions
repo.POST("/builds/:number", session.MustPush, api.PostBuild)

View file

@ -1,5 +1,16 @@
import ApiClient, { encodeQueryString } from './client';
import { Build, BuildFeed, BuildLog, BuildProc, Registry, Repo, RepoPermissions, RepoSettings, Secret } from './types';
import {
Build,
BuildConfig,
BuildFeed,
BuildLog,
BuildProc,
Registry,
Repo,
RepoPermissions,
RepoSettings,
Secret,
} from './types';
type RepoListOptions = {
all?: boolean;
@ -49,6 +60,10 @@ export default class WoodpeckerClient extends ApiClient {
return this._get(`/api/repos/${owner}/${repo}/builds/${number}`) as Promise<Build>;
}
getBuildConfig(owner: string, repo: string, number: number): Promise<BuildConfig[]> {
return this._get(`/api/repos/${owner}/${repo}/builds/${number}/config`) as Promise<BuildConfig[]>;
}
getBuildFeed(opts?: Record<string, string | number | boolean>): Promise<BuildFeed[]> {
const query = encodeQueryString(opts);
return this._get(`/api/user/feed?${query}`) as Promise<BuildFeed[]>;

View file

@ -0,0 +1,6 @@
// A config for a build.
export type BuildConfig = {
hash: string;
name: string;
data: string;
};

View file

@ -1,4 +1,5 @@
export * from './build';
export * from './buildConfig';
export * from './registry';
export * from './repo';
export * from './secret';

View file

@ -58,6 +58,12 @@ const routes: RouteRecordRaw[] = [
component: (): Component => import('~/views/repo/build/Build.vue'),
props: true,
},
{
path: 'config',
name: 'repo-build-config',
component: (): Component => import('~/views/repo/build/BuildConfig.vue'),
props: true,
},
],
},
{

View file

@ -0,0 +1,58 @@
<template>
<FluidContainer v-if="buildConfigs" class="flex flex-col gap-y-6 text-gray-500 justify-between !py-0">
<Panel v-for="buildConfig in buildConfigs" :key="buildConfig.hash" :title="buildConfig.name">
<span class="font-mono whitespace-pre">{{ buildConfig.data }}</span>
</Panel>
</FluidContainer>
</template>
<script lang="ts">
import { defineComponent, inject, onMounted, Ref, ref, watch } from 'vue';
import FluidContainer from '~/components/layout/FluidContainer.vue';
import Panel from '~/components/layout/Panel.vue';
import useApiClient from '~/compositions/useApiClient';
import { Build, BuildConfig, Repo } from '~/lib/api/types';
export default defineComponent({
name: 'BuildConfig',
components: {
FluidContainer,
Panel,
},
setup() {
const build = inject<Ref<Build>>('build');
const apiClient = useApiClient();
const repo = inject<Ref<Repo>>('repo');
if (!repo || !build) {
throw new Error('Unexpected: "repo" & "build" should be provided at this place');
}
const buildConfigs = ref<BuildConfig[]>();
async function loadBuildConfig() {
if (!repo || !build) {
throw new Error('Unexpected: "repo" & "build" should be provided at this place');
}
buildConfigs.value = (await apiClient.getBuildConfig(repo.value.owner, repo.value.name, build.value.number)).map(
(i) => ({
...i,
data: atob(i.data),
}),
);
}
onMounted(() => {
loadBuildConfig();
});
watch(build, () => {
loadBuildConfig();
});
return { buildConfigs };
},
});
</script>

View file

@ -31,6 +31,7 @@
<div class="flex flex-wrap gap-y-2 items-center justify-between">
<Tabs v-model="activeTab" disable-hash-mode>
<Tab id="tasks" title="Tasks" />
<Tab id="config" title="Config" />
</Tabs>
<div class="flex justify-between gap-x-4 text-gray-500 flex-shrink-0 ml-auto">
@ -63,7 +64,7 @@ import {
toRef,
watch,
} from 'vue';
import { useRouter } from 'vue-router';
import { useRoute, useRouter } from 'vue-router';
import Button from '~/components/atomic/Button.vue';
import IconButton from '~/components/atomic/IconButton.vue';
@ -122,6 +123,7 @@ export default defineComponent({
setup(props) {
const apiClient = useApiClient();
const route = useRoute();
const router = useRouter();
const notifications = useNotifications();
const favicon = useFavicon();
@ -191,12 +193,20 @@ export default defineComponent({
const activeTab = computed({
get() {
if (route.name === 'repo-build-config') {
return 'config';
}
return 'tasks';
},
set(tab: string) {
if (tab === 'tasks') {
router.replace({ name: 'repo-build' });
}
if (tab === 'config') {
router.replace({ name: 'repo-build-config' });
}
},
});