mirror of
https://github.com/woodpecker-ci/woodpecker.git
synced 2025-02-04 18:21:06 +02:00
parent
5e595978fa
commit
37c82b905c
@ -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
|
// DeleteBuild cancels a build
|
||||||
func DeleteBuild(c *gin.Context) {
|
func DeleteBuild(c *gin.Context) {
|
||||||
_store := store.FromContext(c)
|
_store := store.FromContext(c)
|
||||||
|
@ -61,6 +61,7 @@ func apiRoutes(e *gin.Engine) {
|
|||||||
|
|
||||||
repo.GET("/builds", api.GetBuilds)
|
repo.GET("/builds", api.GetBuilds)
|
||||||
repo.GET("/builds/:number", api.GetBuild)
|
repo.GET("/builds/:number", api.GetBuild)
|
||||||
|
repo.GET("/builds/:number/config", api.GetBuildConfig)
|
||||||
|
|
||||||
// requires push permissions
|
// requires push permissions
|
||||||
repo.POST("/builds/:number", session.MustPush, api.PostBuild)
|
repo.POST("/builds/:number", session.MustPush, api.PostBuild)
|
||||||
|
@ -1,5 +1,16 @@
|
|||||||
import ApiClient, { encodeQueryString } from './client';
|
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 = {
|
type RepoListOptions = {
|
||||||
all?: boolean;
|
all?: boolean;
|
||||||
@ -49,6 +60,10 @@ export default class WoodpeckerClient extends ApiClient {
|
|||||||
return this._get(`/api/repos/${owner}/${repo}/builds/${number}`) as Promise<Build>;
|
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[]> {
|
getBuildFeed(opts?: Record<string, string | number | boolean>): Promise<BuildFeed[]> {
|
||||||
const query = encodeQueryString(opts);
|
const query = encodeQueryString(opts);
|
||||||
return this._get(`/api/user/feed?${query}`) as Promise<BuildFeed[]>;
|
return this._get(`/api/user/feed?${query}`) as Promise<BuildFeed[]>;
|
||||||
|
6
web/src/lib/api/types/buildConfig.ts
Normal file
6
web/src/lib/api/types/buildConfig.ts
Normal file
@ -0,0 +1,6 @@
|
|||||||
|
// A config for a build.
|
||||||
|
export type BuildConfig = {
|
||||||
|
hash: string;
|
||||||
|
name: string;
|
||||||
|
data: string;
|
||||||
|
};
|
@ -1,4 +1,5 @@
|
|||||||
export * from './build';
|
export * from './build';
|
||||||
|
export * from './buildConfig';
|
||||||
export * from './registry';
|
export * from './registry';
|
||||||
export * from './repo';
|
export * from './repo';
|
||||||
export * from './secret';
|
export * from './secret';
|
||||||
|
@ -58,6 +58,12 @@ const routes: RouteRecordRaw[] = [
|
|||||||
component: (): Component => import('~/views/repo/build/Build.vue'),
|
component: (): Component => import('~/views/repo/build/Build.vue'),
|
||||||
props: true,
|
props: true,
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
path: 'config',
|
||||||
|
name: 'repo-build-config',
|
||||||
|
component: (): Component => import('~/views/repo/build/BuildConfig.vue'),
|
||||||
|
props: true,
|
||||||
|
},
|
||||||
],
|
],
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
|
58
web/src/views/repo/build/BuildConfig.vue
Normal file
58
web/src/views/repo/build/BuildConfig.vue
Normal 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>
|
@ -31,6 +31,7 @@
|
|||||||
<div class="flex flex-wrap gap-y-2 items-center justify-between">
|
<div class="flex flex-wrap gap-y-2 items-center justify-between">
|
||||||
<Tabs v-model="activeTab" disable-hash-mode>
|
<Tabs v-model="activeTab" disable-hash-mode>
|
||||||
<Tab id="tasks" title="Tasks" />
|
<Tab id="tasks" title="Tasks" />
|
||||||
|
<Tab id="config" title="Config" />
|
||||||
</Tabs>
|
</Tabs>
|
||||||
|
|
||||||
<div class="flex justify-between gap-x-4 text-gray-500 flex-shrink-0 ml-auto">
|
<div class="flex justify-between gap-x-4 text-gray-500 flex-shrink-0 ml-auto">
|
||||||
@ -63,7 +64,7 @@ import {
|
|||||||
toRef,
|
toRef,
|
||||||
watch,
|
watch,
|
||||||
} from 'vue';
|
} from 'vue';
|
||||||
import { useRouter } from 'vue-router';
|
import { useRoute, useRouter } from 'vue-router';
|
||||||
|
|
||||||
import Button from '~/components/atomic/Button.vue';
|
import Button from '~/components/atomic/Button.vue';
|
||||||
import IconButton from '~/components/atomic/IconButton.vue';
|
import IconButton from '~/components/atomic/IconButton.vue';
|
||||||
@ -122,6 +123,7 @@ export default defineComponent({
|
|||||||
|
|
||||||
setup(props) {
|
setup(props) {
|
||||||
const apiClient = useApiClient();
|
const apiClient = useApiClient();
|
||||||
|
const route = useRoute();
|
||||||
const router = useRouter();
|
const router = useRouter();
|
||||||
const notifications = useNotifications();
|
const notifications = useNotifications();
|
||||||
const favicon = useFavicon();
|
const favicon = useFavicon();
|
||||||
@ -191,12 +193,20 @@ export default defineComponent({
|
|||||||
|
|
||||||
const activeTab = computed({
|
const activeTab = computed({
|
||||||
get() {
|
get() {
|
||||||
|
if (route.name === 'repo-build-config') {
|
||||||
|
return 'config';
|
||||||
|
}
|
||||||
|
|
||||||
return 'tasks';
|
return 'tasks';
|
||||||
},
|
},
|
||||||
set(tab: string) {
|
set(tab: string) {
|
||||||
if (tab === 'tasks') {
|
if (tab === 'tasks') {
|
||||||
router.replace({ name: 'repo-build' });
|
router.replace({ name: 'repo-build' });
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (tab === 'config') {
|
||||||
|
router.replace({ name: 'repo-build-config' });
|
||||||
|
}
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
|
Loading…
x
Reference in New Issue
Block a user