mirror of
https://github.com/woodpecker-ci/woodpecker.git
synced 2024-12-26 10:20:29 +00:00
Add button to trigger deployments (#1415)
Co-authored-by: 6543 <6543@obermui.de> Co-authored-by: Anbraten <anton@ju60.de> Co-authored-by: Lauris BH <lauris@nix.lv>
This commit is contained in:
parent
5d1ec770c9
commit
cfb288201f
5 changed files with 168 additions and 1 deletions
1
web/components.d.ts
vendored
1
web/components.d.ts
vendored
|
@ -15,6 +15,7 @@ declare module '@vue/runtime-core' {
|
|||
Checkbox: typeof import('./src/components/form/Checkbox.vue')['default']
|
||||
CheckboxesField: typeof import('./src/components/form/CheckboxesField.vue')['default']
|
||||
CronTab: typeof import('./src/components/repo/settings/CronTab.vue')['default']
|
||||
DeployPipelinePopup: typeof import('./src/components/layout/popups/DeployPipelinePopup.vue')['default']
|
||||
DocsLink: typeof import('./src/components/atomic/DocsLink.vue')['default']
|
||||
FluidContainer: typeof import('./src/components/layout/FluidContainer.vue')['default']
|
||||
GeneralTab: typeof import('./src/components/repo/settings/GeneralTab.vue')['default']
|
||||
|
|
|
@ -41,6 +41,18 @@
|
|||
"value": "Variable value"
|
||||
}
|
||||
},
|
||||
"deploy_pipeline": {
|
||||
"title": "Trigger deployment event for current pipeline #{pipelineId}",
|
||||
"enter_target": "Target deployment environment",
|
||||
"trigger": "Deploy",
|
||||
"variables": {
|
||||
"add": "Add variable",
|
||||
"title": "Additional pipeline variables",
|
||||
"desc": "Specify additional variables to use in your pipeline. Variables with the same name will be overwritten.",
|
||||
"name": "Variable name",
|
||||
"value": "Variable value"
|
||||
}
|
||||
},
|
||||
"activity": "Activity",
|
||||
"branches": "Branches",
|
||||
"add": "Add repository",
|
||||
|
@ -218,6 +230,7 @@
|
|||
"restart": "Restart",
|
||||
"canceled": "This step has been canceled.",
|
||||
"cancel_success": "Pipeline canceled",
|
||||
"deploy": "Deploy",
|
||||
"restart_success": "Pipeline restarted",
|
||||
"log_download": "Download",
|
||||
"log_auto_scroll": "Automatically scroll down",
|
||||
|
|
120
web/src/components/layout/popups/DeployPipelinePopup.vue
Normal file
120
web/src/components/layout/popups/DeployPipelinePopup.vue
Normal file
|
@ -0,0 +1,120 @@
|
|||
<template>
|
||||
<Popup :open="open" @close="$emit('close')">
|
||||
<Panel v-if="!loading">
|
||||
<form @submit.prevent="triggerDeployPipeline">
|
||||
<span class="text-xl text-color">{{ $t('repo.deploy_pipeline.title', { pipelineId: pipelineNumber }) }}</span>
|
||||
<InputField :label="$t('repo.deploy_pipeline.enter_target')">
|
||||
<TextField v-model="payload.environment" required />
|
||||
</InputField>
|
||||
<InputField :label="$t('repo.deploy_pipeline.variables.title')">
|
||||
<span class="text-sm text-color-alt mb-2">{{ $t('repo.deploy_pipeline.variables.desc') }}</span>
|
||||
<div class="flex flex-col gap-2">
|
||||
<div v-for="(value, name) in payload.variables" :key="name" class="flex gap-4">
|
||||
<TextField :model-value="name" disabled />
|
||||
<TextField :model-value="value" disabled />
|
||||
<div class="w-34 flex-shrink-0">
|
||||
<Button color="red" class="ml-auto" @click="deleteVar(name)">
|
||||
<i-la-times />
|
||||
</Button>
|
||||
</div>
|
||||
</div>
|
||||
<form class="flex gap-4" @submit.prevent="addPipelineVariable">
|
||||
<TextField
|
||||
v-model="newPipelineVariable.name"
|
||||
:placeholder="$t('repo.deploy_pipeline.variables.name')"
|
||||
required
|
||||
/>
|
||||
<TextField
|
||||
v-model="newPipelineVariable.value"
|
||||
:placeholder="$t('repo.deploy_pipeline.variables.value')"
|
||||
required
|
||||
/>
|
||||
<Button
|
||||
class="w-34 flex-shrink-0"
|
||||
start-icon="plus"
|
||||
type="submit"
|
||||
:text="$t('repo.deploy_pipeline.variables.add')"
|
||||
/>
|
||||
</form>
|
||||
</div>
|
||||
</InputField>
|
||||
<Button type="submit" :text="$t('repo.deploy_pipeline.trigger')" />
|
||||
</form>
|
||||
</Panel>
|
||||
</Popup>
|
||||
</template>
|
||||
|
||||
<script lang="ts" setup>
|
||||
import { onMounted, ref, toRef } from 'vue';
|
||||
import { useRouter } from 'vue-router';
|
||||
|
||||
import Button from '~/components/atomic/Button.vue';
|
||||
import InputField from '~/components/form/InputField.vue';
|
||||
import TextField from '~/components/form/TextField.vue';
|
||||
import Panel from '~/components/layout/Panel.vue';
|
||||
import Popup from '~/components/layout/Popup.vue';
|
||||
import useApiClient from '~/compositions/useApiClient';
|
||||
import { inject } from '~/compositions/useInjectProvide';
|
||||
|
||||
const props = defineProps<{
|
||||
open: boolean;
|
||||
pipelineNumber: number;
|
||||
}>();
|
||||
|
||||
const emit = defineEmits<{
|
||||
(event: 'close'): void;
|
||||
}>();
|
||||
|
||||
const apiClient = useApiClient();
|
||||
|
||||
const repo = inject('repo');
|
||||
|
||||
const router = useRouter();
|
||||
|
||||
const payload = ref<{ id: string; environment: string; variables: Record<string, string> }>({
|
||||
id: '',
|
||||
environment: '',
|
||||
variables: {},
|
||||
});
|
||||
const newPipelineVariable = ref<{ name: string; value: string }>({ name: '', value: '' });
|
||||
|
||||
const loading = ref(true);
|
||||
onMounted(async () => {
|
||||
loading.value = false;
|
||||
});
|
||||
|
||||
function addPipelineVariable() {
|
||||
if (!newPipelineVariable.value.name || !newPipelineVariable.value.value) {
|
||||
return;
|
||||
}
|
||||
payload.value.variables[newPipelineVariable.value.name] = newPipelineVariable.value.value;
|
||||
newPipelineVariable.value.name = '';
|
||||
newPipelineVariable.value.value = '';
|
||||
}
|
||||
|
||||
function deleteVar(key: string) {
|
||||
delete payload.value.variables[key];
|
||||
}
|
||||
|
||||
const pipelineNumber = toRef(props, 'pipelineNumber');
|
||||
async function triggerDeployPipeline() {
|
||||
loading.value = true;
|
||||
const newPipeline = await apiClient.deployPipeline(
|
||||
repo.value.owner,
|
||||
repo.value.name,
|
||||
pipelineNumber.value,
|
||||
payload.value,
|
||||
);
|
||||
|
||||
emit('close');
|
||||
|
||||
await router.push({
|
||||
name: 'repo-pipeline',
|
||||
params: {
|
||||
pipelineId: newPipeline.number,
|
||||
},
|
||||
});
|
||||
|
||||
loading.value = false;
|
||||
}
|
||||
</script>
|
|
@ -23,6 +23,13 @@ type PipelineOptions = {
|
|||
branch: string;
|
||||
variables: Record<string, string>;
|
||||
};
|
||||
|
||||
type DeploymentOptions = {
|
||||
id: string;
|
||||
environment: string;
|
||||
variables: Record<string, string>;
|
||||
};
|
||||
|
||||
export default class WoodpeckerClient extends ApiClient {
|
||||
getRepoList(opts?: RepoListOptions): Promise<Repo[]> {
|
||||
const query = encodeQueryString(opts);
|
||||
|
@ -62,6 +69,18 @@ export default class WoodpeckerClient extends ApiClient {
|
|||
return this._post(`/api/repos/${owner}/${repo}/pipelines`, options) as Promise<Pipeline>;
|
||||
}
|
||||
|
||||
// Deploy triggers a deployment for an existing pipeline using the
|
||||
// specified target environment.
|
||||
deployPipeline(owner: string, repo: string, number: number, options: DeploymentOptions): Promise<Pipeline> {
|
||||
const vars = {
|
||||
...options.variables,
|
||||
event: 'deployment',
|
||||
deploy_to: options.environment,
|
||||
};
|
||||
const query = encodeQueryString(vars);
|
||||
return this._post(`/api/repos/${owner}/${repo}/pipelines/${number}?${query}`) as Promise<Pipeline>;
|
||||
}
|
||||
|
||||
getPipelineList(owner: string, repo: string, opts?: Record<string, string | number | boolean>): Promise<Pipeline[]> {
|
||||
const query = encodeQueryString(opts);
|
||||
return this._get(`/api/repos/${owner}/${repo}/pipelines?${query}`) as Promise<Pipeline[]>;
|
||||
|
|
|
@ -31,6 +31,17 @@
|
|||
:is-loading="isRestartingPipeline"
|
||||
@click="restartPipeline"
|
||||
/>
|
||||
<Button
|
||||
v-if="pipeline.status === 'success'"
|
||||
class="flex-shrink-0"
|
||||
:text="$t('repo.pipeline.actions.deploy')"
|
||||
@click="showDeployPipelinePopup = true"
|
||||
/>
|
||||
<DeployPipelinePopup
|
||||
:pipeline-number="pipelineId"
|
||||
:open="showDeployPipelinePopup"
|
||||
@close="showDeployPipelinePopup = false"
|
||||
/>
|
||||
</template>
|
||||
</template>
|
||||
|
||||
|
@ -66,7 +77,7 @@
|
|||
|
||||
<script lang="ts">
|
||||
import { Tooltip } from 'floating-vue';
|
||||
import { computed, defineComponent, inject, onBeforeUnmount, onMounted, provide, Ref, toRef, watch } from 'vue';
|
||||
import { computed, defineComponent, inject, onBeforeUnmount, onMounted, provide, Ref, ref, toRef, watch } from 'vue';
|
||||
import { useI18n } from 'vue-i18n';
|
||||
import { useRoute, useRouter } from 'vue-router';
|
||||
|
||||
|
@ -135,6 +146,8 @@ export default defineComponent({
|
|||
|
||||
const { message } = usePipeline(pipeline);
|
||||
|
||||
const showDeployPipelinePopup = ref(false);
|
||||
|
||||
async function loadPipeline(): Promise<void> {
|
||||
if (!repo) {
|
||||
throw new Error('Unexpected: Repo is undefined');
|
||||
|
@ -216,6 +229,7 @@ export default defineComponent({
|
|||
message,
|
||||
isCancelingPipeline,
|
||||
isRestartingPipeline,
|
||||
showDeployPipelinePopup,
|
||||
activeTab,
|
||||
since,
|
||||
duration,
|
||||
|
|
Loading…
Reference in a new issue