mirror of
https://github.com/woodpecker-ci/woodpecker.git
synced 2025-04-23 20:24:21 +00:00
Add version and update notes (#2722)
This commit is contained in:
parent
5f8e252fba
commit
33f9574dce
7 changed files with 147 additions and 25 deletions
|
@ -1,4 +1,5 @@
|
||||||
when:
|
when:
|
||||||
|
- event: tag
|
||||||
- event: pull_request
|
- event: pull_request
|
||||||
- event: push
|
- event: push
|
||||||
path: &when_path
|
path: &when_path
|
||||||
|
@ -59,7 +60,7 @@ steps:
|
||||||
secrets:
|
secrets:
|
||||||
- BOT_PRIVATE_KEY
|
- BOT_PRIVATE_KEY
|
||||||
commands:
|
commands:
|
||||||
- apk add openssh-client git rsync
|
- apk add openssh-client git rsync jq
|
||||||
- mkdir -p $HOME/.ssh
|
- mkdir -p $HOME/.ssh
|
||||||
- ssh-keyscan -t rsa github.com >> $HOME/.ssh/known_hosts
|
- ssh-keyscan -t rsa github.com >> $HOME/.ssh/known_hosts
|
||||||
- echo "$BOT_PRIVATE_KEY" > $HOME/.ssh/id_rsa
|
- echo "$BOT_PRIVATE_KEY" > $HOME/.ssh/id_rsa
|
||||||
|
@ -67,8 +68,11 @@ steps:
|
||||||
- git config --global user.email "woodpecker-bot@obermui.de"
|
- git config --global user.email "woodpecker-bot@obermui.de"
|
||||||
- git config --global user.name "woodpecker-bot"
|
- git config --global user.name "woodpecker-bot"
|
||||||
- git clone --depth 1 --single-branch git@github.com:woodpecker-ci/woodpecker-ci.github.io.git /repo
|
- git clone --depth 1 --single-branch git@github.com:woodpecker-ci/woodpecker-ci.github.io.git /repo
|
||||||
|
# update latest and next version
|
||||||
|
- if [ "$CI_PIPELINE_EVENT" == "tag" ] ; then jq '.latest = ${CI_COMMIT_TAG}' repo/version.json > repo/version.json.tmp && mv repo/version.json.tmp repo/version.json ; fi
|
||||||
|
- if [ "$CI_PIPELINE_EVENT" == "push" ] ; then jq "next-" '.next = ${CI_COMMIT_SHA:0:10}' repo/version.json > repo/version.json.tmp && mv repo/version.json.tmp repo/version.json ; fi
|
||||||
# copy all docs files and delete all old ones, but leave CNAME and index.yaml untouched
|
# copy all docs files and delete all old ones, but leave CNAME and index.yaml untouched
|
||||||
- rsync -r --exclude .git --exclude CNAME --exclude index.yaml --exclude README.md --delete docs/build/ /repo
|
- rsync -r --exclude .git --exclude CNAME --exclude index.yaml --exclude README.md --exclude version.json --delete docs/build/ /repo
|
||||||
- cd /repo
|
- cd /repo
|
||||||
- git add .
|
- git add .
|
||||||
# exit successfully if nothing changed
|
# exit successfully if nothing changed
|
||||||
|
@ -76,5 +80,6 @@ steps:
|
||||||
- git commit -m "Deploy website - based on ${CI_COMMIT_SHA}"
|
- git commit -m "Deploy website - based on ${CI_COMMIT_SHA}"
|
||||||
- git push
|
- git push
|
||||||
when:
|
when:
|
||||||
event: [push, cron]
|
- event: [push, cron]
|
||||||
path: *when_path
|
path: *when_path
|
||||||
|
- event: tag
|
||||||
|
|
2
web/components.d.ts
vendored
2
web/components.d.ts
vendored
|
@ -10,6 +10,7 @@ declare module 'vue' {
|
||||||
ActionsTab: typeof import('./src/components/repo/settings/ActionsTab.vue')['default']
|
ActionsTab: typeof import('./src/components/repo/settings/ActionsTab.vue')['default']
|
||||||
ActivePipelines: typeof import('./src/components/layout/header/ActivePipelines.vue')['default']
|
ActivePipelines: typeof import('./src/components/layout/header/ActivePipelines.vue')['default']
|
||||||
AdminAgentsTab: typeof import('./src/components/admin/settings/AdminAgentsTab.vue')['default']
|
AdminAgentsTab: typeof import('./src/components/admin/settings/AdminAgentsTab.vue')['default']
|
||||||
|
AdminInfoTab: typeof import('./src/components/admin/settings/AdminInfoTab.vue')['default']
|
||||||
AdminOrgsTab: typeof import('./src/components/admin/settings/AdminOrgsTab.vue')['default']
|
AdminOrgsTab: typeof import('./src/components/admin/settings/AdminOrgsTab.vue')['default']
|
||||||
AdminQueueStats: typeof import('./src/components/admin/settings/queue/AdminQueueStats.vue')['default']
|
AdminQueueStats: typeof import('./src/components/admin/settings/queue/AdminQueueStats.vue')['default']
|
||||||
AdminQueueTab: typeof import('./src/components/admin/settings/AdminQueueTab.vue')['default']
|
AdminQueueTab: typeof import('./src/components/admin/settings/AdminQueueTab.vue')['default']
|
||||||
|
@ -22,6 +23,7 @@ declare module 'vue' {
|
||||||
Checkbox: typeof import('./src/components/form/Checkbox.vue')['default']
|
Checkbox: typeof import('./src/components/form/Checkbox.vue')['default']
|
||||||
CheckboxesField: typeof import('./src/components/form/CheckboxesField.vue')['default']
|
CheckboxesField: typeof import('./src/components/form/CheckboxesField.vue')['default']
|
||||||
Container: typeof import('./src/components/layout/Container.vue')['default']
|
Container: typeof import('./src/components/layout/Container.vue')['default']
|
||||||
|
copy: typeof import('./src/components/admin/settings/AdminAgentsTab copy.vue')['default']
|
||||||
CronTab: typeof import('./src/components/repo/settings/CronTab.vue')['default']
|
CronTab: typeof import('./src/components/repo/settings/CronTab.vue')['default']
|
||||||
DeployPipelinePopup: typeof import('./src/components/layout/popups/DeployPipelinePopup.vue')['default']
|
DeployPipelinePopup: typeof import('./src/components/layout/popups/DeployPipelinePopup.vue')['default']
|
||||||
DocsLink: typeof import('./src/components/atomic/DocsLink.vue')['default']
|
DocsLink: typeof import('./src/components/atomic/DocsLink.vue')['default']
|
||||||
|
|
|
@ -501,5 +501,8 @@
|
||||||
"internal_error": "Some internal error occurred",
|
"internal_error": "Some internal error occurred",
|
||||||
"access_denied": "You are not allowed to login"
|
"access_denied": "You are not allowed to login"
|
||||||
},
|
},
|
||||||
"default": "default"
|
"default": "default",
|
||||||
|
"info": "Info",
|
||||||
|
"running_version": "You are running woodpecker {0}",
|
||||||
|
"update_woodpecker": "Please update your Woodpecker instance to {0}"
|
||||||
}
|
}
|
||||||
|
|
29
web/src/components/admin/settings/AdminInfoTab.vue
Normal file
29
web/src/components/admin/settings/AdminInfoTab.vue
Normal file
|
@ -0,0 +1,29 @@
|
||||||
|
<template>
|
||||||
|
<Settings :title="$t('info')">
|
||||||
|
<div class="flex flex-col items-center gap-4">
|
||||||
|
<WoodpeckerLogo class="w-48 h-48" />
|
||||||
|
|
||||||
|
<i18n-t keypath="running_version" tag="p" class="text-xl">
|
||||||
|
<span class="font-bold">{{ version?.current }}</span>
|
||||||
|
</i18n-t>
|
||||||
|
|
||||||
|
<i18n-t v-if="version?.needsUpdate" keypath="update_woodpecker" tag="span" class="text-int-wp-state-error-100">
|
||||||
|
<a
|
||||||
|
:href="`https://github.com/woodpecker-ci/woodpecker/releases/tag/${version.latest}`"
|
||||||
|
target="_blank"
|
||||||
|
rel="noopener noreferrer"
|
||||||
|
class="underline"
|
||||||
|
>{{ version.latest }}</a
|
||||||
|
>
|
||||||
|
</i18n-t>
|
||||||
|
</div>
|
||||||
|
</Settings>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script lang="ts" setup>
|
||||||
|
import WoodpeckerLogo from '~/assets/logo.svg?component';
|
||||||
|
import Settings from '~/components/layout/Settings.vue';
|
||||||
|
import { useVersion } from '~/compositions/useVersion';
|
||||||
|
|
||||||
|
const version = useVersion();
|
||||||
|
</script>
|
|
@ -1,47 +1,41 @@
|
||||||
<template>
|
<template>
|
||||||
<!-- Navbar -->
|
|
||||||
<nav
|
<nav
|
||||||
class="flex bg-wp-primary-200 dark:bg-wp-primary-300 text-neutral-content p-4 border-b border-wp-background-100 font-bold text-wp-primary-text-100"
|
class="flex bg-wp-primary-200 dark:bg-wp-primary-300 text-neutral-content p-4 border-b border-wp-background-100 font-bold text-wp-primary-text-100"
|
||||||
>
|
>
|
||||||
<!-- Left Links Box -->
|
|
||||||
<div class="flex items-center space-x-2">
|
<div class="flex items-center space-x-2">
|
||||||
<!-- Logo -->
|
|
||||||
<router-link :to="{ name: 'home' }" class="flex flex-col -my-2 px-2">
|
<router-link :to="{ name: 'home' }" class="flex flex-col -my-2 px-2">
|
||||||
<WoodpeckerLogo class="w-8 h-8" />
|
<WoodpeckerLogo class="w-8 h-8" />
|
||||||
<span class="text-xs">{{ version }}</span>
|
<span class="text-xs" :title="version?.current">{{ version?.currentShort }}</span>
|
||||||
</router-link>
|
</router-link>
|
||||||
<!-- Repo Link -->
|
|
||||||
<router-link v-if="user" :to="{ name: 'repos' }" class="navbar-link navbar-clickable">
|
<router-link v-if="user" :to="{ name: 'repos' }" class="navbar-link navbar-clickable">
|
||||||
<span class="flex md:hidden">{{ $t('repos') }}</span>
|
<span class="flex md:hidden">{{ $t('repos') }}</span>
|
||||||
<span class="hidden md:flex">{{ $t('repositories') }}</span>
|
<span class="hidden md:flex">{{ $t('repositories') }}</span>
|
||||||
</router-link>
|
</router-link>
|
||||||
<!-- Docs Link -->
|
|
||||||
<a href="https://woodpecker-ci.org/" target="_blank" class="navbar-link navbar-clickable hidden md:flex">{{
|
<a href="https://woodpecker-ci.org/" target="_blank" class="navbar-link navbar-clickable hidden md:flex">{{
|
||||||
$t('docs')
|
$t('docs')
|
||||||
}}</a>
|
}}</a>
|
||||||
<!-- API Link -->
|
|
||||||
<a v-if="enableSwagger" :href="apiUrl" target="_blank" class="navbar-link navbar-clickable hidden md:flex">{{
|
<a v-if="enableSwagger" :href="apiUrl" target="_blank" class="navbar-link navbar-clickable hidden md:flex">{{
|
||||||
$t('api')
|
$t('api')
|
||||||
}}</a>
|
}}</a>
|
||||||
</div>
|
</div>
|
||||||
<!-- Right Icons Box -->
|
|
||||||
<div class="flex ml-auto -m-1.5 items-center space-x-2">
|
<div class="flex ml-auto -m-1.5 items-center space-x-2">
|
||||||
<!-- Admin Settings -->
|
<div v-if="user?.admin" class="relative">
|
||||||
<IconButton
|
<IconButton
|
||||||
v-if="user?.admin"
|
class="navbar-icon"
|
||||||
class="navbar-icon"
|
:title="$t('admin.settings.settings')"
|
||||||
:title="$t('admin.settings.settings')"
|
:to="{ name: 'admin-settings' }"
|
||||||
:to="{ name: 'admin-settings' }"
|
icon="settings"
|
||||||
icon="settings"
|
/>
|
||||||
/>
|
<div
|
||||||
|
v-if="version?.needsUpdate"
|
||||||
|
class="absolute top-2 right-2 bg-int-wp-state-error-100 rounded-full w-3 h-3"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
|
||||||
<!-- Active Pipelines Indicator -->
|
|
||||||
<ActivePipelines v-if="user" class="navbar-icon" />
|
<ActivePipelines v-if="user" class="navbar-icon" />
|
||||||
<!-- User Avatar -->
|
|
||||||
<IconButton v-if="user" :to="{ name: 'user' }" :title="$t('user.settings.settings')" class="navbar-icon !p-1.5">
|
<IconButton v-if="user" :to="{ name: 'user' }" :title="$t('user.settings.settings')" class="navbar-icon !p-1.5">
|
||||||
<img v-if="user && user.avatar_url" class="rounded-md" :src="`${user.avatar_url}`" />
|
<img v-if="user && user.avatar_url" class="rounded-md" :src="`${user.avatar_url}`" />
|
||||||
</IconButton>
|
</IconButton>
|
||||||
<!-- Login Button -->
|
|
||||||
<Button v-else :text="$t('login')" @click="doLogin" />
|
<Button v-else :text="$t('login')" @click="doLogin" />
|
||||||
</div>
|
</div>
|
||||||
</nav>
|
</nav>
|
||||||
|
@ -55,9 +49,11 @@ import Button from '~/components/atomic/Button.vue';
|
||||||
import IconButton from '~/components/atomic/IconButton.vue';
|
import IconButton from '~/components/atomic/IconButton.vue';
|
||||||
import useAuthentication from '~/compositions/useAuthentication';
|
import useAuthentication from '~/compositions/useAuthentication';
|
||||||
import useConfig from '~/compositions/useConfig';
|
import useConfig from '~/compositions/useConfig';
|
||||||
|
import { useVersion } from '~/compositions/useVersion';
|
||||||
|
|
||||||
import ActivePipelines from './ActivePipelines.vue';
|
import ActivePipelines from './ActivePipelines.vue';
|
||||||
|
|
||||||
|
const version = useVersion();
|
||||||
const config = useConfig();
|
const config = useConfig();
|
||||||
const route = useRoute();
|
const route = useRoute();
|
||||||
const authentication = useAuthentication();
|
const authentication = useAuthentication();
|
||||||
|
@ -68,7 +64,6 @@ function doLogin() {
|
||||||
authentication.authenticate(route.fullPath);
|
authentication.authenticate(route.fullPath);
|
||||||
}
|
}
|
||||||
|
|
||||||
const version = config.version?.startsWith('next') ? 'next' : config.version;
|
|
||||||
const { enableSwagger } = config;
|
const { enableSwagger } = config;
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
|
|
84
web/src/compositions/useVersion.ts
Normal file
84
web/src/compositions/useVersion.ts
Normal file
|
@ -0,0 +1,84 @@
|
||||||
|
import { onMounted, ref } from 'vue';
|
||||||
|
|
||||||
|
import useAuthentication from './useAuthentication';
|
||||||
|
import useConfig from './useConfig';
|
||||||
|
|
||||||
|
type VersionInfo = {
|
||||||
|
latest: string;
|
||||||
|
next: string;
|
||||||
|
};
|
||||||
|
|
||||||
|
const version = ref<{
|
||||||
|
latest: string | undefined;
|
||||||
|
current: string;
|
||||||
|
currentShort: string;
|
||||||
|
needsUpdate: boolean;
|
||||||
|
}>();
|
||||||
|
|
||||||
|
async function fetchVersion(): Promise<VersionInfo | undefined> {
|
||||||
|
try {
|
||||||
|
const resp = await fetch('https://woodpecker-ci.org/version.json');
|
||||||
|
const json = await resp.json();
|
||||||
|
return json;
|
||||||
|
} catch (error) {
|
||||||
|
// eslint-disable-next-line no-console
|
||||||
|
console.error('Failed to fetch version info', error);
|
||||||
|
return undefined;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const isInitialised = ref(false);
|
||||||
|
|
||||||
|
export function useVersion() {
|
||||||
|
if (isInitialised.value) {
|
||||||
|
return version;
|
||||||
|
}
|
||||||
|
isInitialised.value = true;
|
||||||
|
|
||||||
|
const config = useConfig();
|
||||||
|
const current = config.version as string;
|
||||||
|
const usesNext = config.version?.startsWith('next');
|
||||||
|
|
||||||
|
const { user } = useAuthentication();
|
||||||
|
if (!user?.admin) {
|
||||||
|
version.value = {
|
||||||
|
latest: undefined,
|
||||||
|
current,
|
||||||
|
currentShort: usesNext ? 'next' : current,
|
||||||
|
needsUpdate: false,
|
||||||
|
};
|
||||||
|
return version;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (current === 'dev') {
|
||||||
|
version.value = {
|
||||||
|
latest: undefined,
|
||||||
|
current,
|
||||||
|
currentShort: current,
|
||||||
|
needsUpdate: false,
|
||||||
|
};
|
||||||
|
return version;
|
||||||
|
}
|
||||||
|
|
||||||
|
onMounted(async () => {
|
||||||
|
const versionInfo = await fetchVersion();
|
||||||
|
|
||||||
|
let needsUpdate = false;
|
||||||
|
if (versionInfo) {
|
||||||
|
if (usesNext) {
|
||||||
|
needsUpdate = versionInfo.next !== current;
|
||||||
|
} else {
|
||||||
|
needsUpdate = versionInfo.latest !== current;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
version.value = {
|
||||||
|
latest: usesNext ? versionInfo?.next : versionInfo?.latest,
|
||||||
|
current,
|
||||||
|
currentShort: usesNext ? 'next' : current,
|
||||||
|
needsUpdate,
|
||||||
|
};
|
||||||
|
});
|
||||||
|
|
||||||
|
return version;
|
||||||
|
}
|
|
@ -3,6 +3,9 @@
|
||||||
<template #title>
|
<template #title>
|
||||||
{{ $t('repo.settings.settings') }}
|
{{ $t('repo.settings.settings') }}
|
||||||
</template>
|
</template>
|
||||||
|
<Tab id="info" :title="$t('info')">
|
||||||
|
<AdminInfoTab />
|
||||||
|
</Tab>
|
||||||
<Tab id="secrets" :title="$t('admin.settings.secrets.secrets')">
|
<Tab id="secrets" :title="$t('admin.settings.secrets.secrets')">
|
||||||
<AdminSecretsTab />
|
<AdminSecretsTab />
|
||||||
</Tab>
|
</Tab>
|
||||||
|
@ -30,6 +33,7 @@ import { useI18n } from 'vue-i18n';
|
||||||
import { useRouter } from 'vue-router';
|
import { useRouter } from 'vue-router';
|
||||||
|
|
||||||
import AdminAgentsTab from '~/components/admin/settings/AdminAgentsTab.vue';
|
import AdminAgentsTab from '~/components/admin/settings/AdminAgentsTab.vue';
|
||||||
|
import AdminInfoTab from '~/components/admin/settings/AdminInfoTab.vue';
|
||||||
import AdminOrgsTab from '~/components/admin/settings/AdminOrgsTab.vue';
|
import AdminOrgsTab from '~/components/admin/settings/AdminOrgsTab.vue';
|
||||||
import AdminQueueTab from '~/components/admin/settings/AdminQueueTab.vue';
|
import AdminQueueTab from '~/components/admin/settings/AdminQueueTab.vue';
|
||||||
import AdminReposTab from '~/components/admin/settings/AdminReposTab.vue';
|
import AdminReposTab from '~/components/admin/settings/AdminReposTab.vue';
|
||||||
|
|
Loading…
Reference in a new issue