mirror of
https://github.com/woodpecker-ci/woodpecker.git
synced 2025-01-11 01:55:27 +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:
|
||||
- event: tag
|
||||
- event: pull_request
|
||||
- event: push
|
||||
path: &when_path
|
||||
|
@ -59,7 +60,7 @@ steps:
|
|||
secrets:
|
||||
- BOT_PRIVATE_KEY
|
||||
commands:
|
||||
- apk add openssh-client git rsync
|
||||
- apk add openssh-client git rsync jq
|
||||
- mkdir -p $HOME/.ssh
|
||||
- ssh-keyscan -t rsa github.com >> $HOME/.ssh/known_hosts
|
||||
- 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.name "woodpecker-bot"
|
||||
- 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
|
||||
- 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
|
||||
- git add .
|
||||
# exit successfully if nothing changed
|
||||
|
@ -76,5 +80,6 @@ steps:
|
|||
- git commit -m "Deploy website - based on ${CI_COMMIT_SHA}"
|
||||
- git push
|
||||
when:
|
||||
event: [push, cron]
|
||||
path: *when_path
|
||||
- event: [push, cron]
|
||||
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']
|
||||
ActivePipelines: typeof import('./src/components/layout/header/ActivePipelines.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']
|
||||
AdminQueueStats: typeof import('./src/components/admin/settings/queue/AdminQueueStats.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']
|
||||
CheckboxesField: typeof import('./src/components/form/CheckboxesField.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']
|
||||
DeployPipelinePopup: typeof import('./src/components/layout/popups/DeployPipelinePopup.vue')['default']
|
||||
DocsLink: typeof import('./src/components/atomic/DocsLink.vue')['default']
|
||||
|
|
|
@ -501,5 +501,8 @@
|
|||
"internal_error": "Some internal error occurred",
|
||||
"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>
|
||||
<!-- Navbar -->
|
||||
<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"
|
||||
>
|
||||
<!-- Left Links Box -->
|
||||
<div class="flex items-center space-x-2">
|
||||
<!-- Logo -->
|
||||
<router-link :to="{ name: 'home' }" class="flex flex-col -my-2 px-2">
|
||||
<WoodpeckerLogo class="w-8 h-8" />
|
||||
<span class="text-xs">{{ version }}</span>
|
||||
<span class="text-xs" :title="version?.current">{{ version?.currentShort }}</span>
|
||||
</router-link>
|
||||
<!-- Repo Link -->
|
||||
<router-link v-if="user" :to="{ name: 'repos' }" class="navbar-link navbar-clickable">
|
||||
<span class="flex md:hidden">{{ $t('repos') }}</span>
|
||||
<span class="hidden md:flex">{{ $t('repositories') }}</span>
|
||||
</router-link>
|
||||
<!-- Docs Link -->
|
||||
<a href="https://woodpecker-ci.org/" target="_blank" class="navbar-link navbar-clickable hidden md:flex">{{
|
||||
$t('docs')
|
||||
}}</a>
|
||||
<!-- API Link -->
|
||||
<a v-if="enableSwagger" :href="apiUrl" target="_blank" class="navbar-link navbar-clickable hidden md:flex">{{
|
||||
$t('api')
|
||||
}}</a>
|
||||
</div>
|
||||
<!-- Right Icons Box -->
|
||||
<div class="flex ml-auto -m-1.5 items-center space-x-2">
|
||||
<!-- Admin Settings -->
|
||||
<IconButton
|
||||
v-if="user?.admin"
|
||||
class="navbar-icon"
|
||||
:title="$t('admin.settings.settings')"
|
||||
:to="{ name: 'admin-settings' }"
|
||||
icon="settings"
|
||||
/>
|
||||
<div v-if="user?.admin" class="relative">
|
||||
<IconButton
|
||||
class="navbar-icon"
|
||||
:title="$t('admin.settings.settings')"
|
||||
:to="{ name: 'admin-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" />
|
||||
<!-- User Avatar -->
|
||||
<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}`" />
|
||||
</IconButton>
|
||||
<!-- Login Button -->
|
||||
<Button v-else :text="$t('login')" @click="doLogin" />
|
||||
</div>
|
||||
</nav>
|
||||
|
@ -55,9 +49,11 @@ import Button from '~/components/atomic/Button.vue';
|
|||
import IconButton from '~/components/atomic/IconButton.vue';
|
||||
import useAuthentication from '~/compositions/useAuthentication';
|
||||
import useConfig from '~/compositions/useConfig';
|
||||
import { useVersion } from '~/compositions/useVersion';
|
||||
|
||||
import ActivePipelines from './ActivePipelines.vue';
|
||||
|
||||
const version = useVersion();
|
||||
const config = useConfig();
|
||||
const route = useRoute();
|
||||
const authentication = useAuthentication();
|
||||
|
@ -68,7 +64,6 @@ function doLogin() {
|
|||
authentication.authenticate(route.fullPath);
|
||||
}
|
||||
|
||||
const version = config.version?.startsWith('next') ? 'next' : config.version;
|
||||
const { enableSwagger } = config;
|
||||
</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>
|
||||
{{ $t('repo.settings.settings') }}
|
||||
</template>
|
||||
<Tab id="info" :title="$t('info')">
|
||||
<AdminInfoTab />
|
||||
</Tab>
|
||||
<Tab id="secrets" :title="$t('admin.settings.secrets.secrets')">
|
||||
<AdminSecretsTab />
|
||||
</Tab>
|
||||
|
@ -30,6 +33,7 @@ import { useI18n } from 'vue-i18n';
|
|||
import { useRouter } from 'vue-router';
|
||||
|
||||
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 AdminQueueTab from '~/components/admin/settings/AdminQueueTab.vue';
|
||||
import AdminReposTab from '~/components/admin/settings/AdminReposTab.vue';
|
||||
|
|
Loading…
Reference in a new issue