diff --git a/.github/workflows/docker.yaml b/.github/workflows/docker.yaml index ab9283fa02..1802e8f336 100644 --- a/.github/workflows/docker.yaml +++ b/.github/workflows/docker.yaml @@ -136,6 +136,7 @@ jobs: sbom: true build-args: | BUILD_HASH=${{ github.sha }} + BUILD_CHANNEL=${{ github.ref == 'refs/heads/dev' && 'dev' || (github.ref == 'refs/heads/main' || startsWith(github.ref, 'refs/tags/v')) && 'main' || 'unknown' }} ${{ matrix.variant.build_args }} - name: Export digest diff --git a/.github/workflows/release-pypi.yml b/.github/workflows/release-pypi.yml index 9995ccedae..40cebc5028 100644 --- a/.github/workflows/release-pypi.yml +++ b/.github/workflows/release-pypi.yml @@ -28,6 +28,9 @@ jobs: with: python-version: 3.11 - name: Build + env: + APP_BUILD_CHANNEL: main + APP_BUILD_HASH: ${{ github.sha }} run: | python -m pip install --upgrade pip pip install build diff --git a/Dockerfile b/Dockerfile index 08d7a71eec..4bf9cfc57c 100644 --- a/Dockerfile +++ b/Dockerfile @@ -19,6 +19,7 @@ ARG USE_AUXILIARY_EMBEDDING_MODEL=TaylorAI/bge-micro-v2 ARG USE_TIKTOKEN_ENCODING_NAME="cl100k_base" ARG BUILD_HASH=dev-build +ARG BUILD_CHANNEL=unknown # Override at your own risk - non-root configurations are untested ARG UID=0 ARG GID=0 @@ -26,6 +27,7 @@ ARG GID=0 ######## WebUI frontend ######## FROM --platform=$BUILDPLATFORM node:22-alpine3.20 AS build ARG BUILD_HASH +ARG BUILD_CHANNEL ARG USE_SLIM ARG UID ARG GID @@ -43,6 +45,7 @@ RUN npm ci --force COPY . . ENV APP_BUILD_HASH=${BUILD_HASH} +ENV APP_BUILD_CHANNEL=${BUILD_CHANNEL} RUN npm run build && \ if [ "$USE_SLIM" = "true" ]; then find build -type f -name '*.map' -delete; fi @@ -225,7 +228,7 @@ RUN if [ "$USE_PERMISSION_HARDENING" = "true" ]; then \ USER $UID:$GID ARG BUILD_HASH -ENV WEBUI_BUILD_VERSION=${BUILD_HASH} +ENV WEBUI_BUILD_HASH=${BUILD_HASH} ENV DOCKER=true CMD [ "bash", "start.sh"] diff --git a/hatch_build.py b/hatch_build.py index bf47545680..623b368b78 100644 --- a/hatch_build.py +++ b/hatch_build.py @@ -17,7 +17,6 @@ class CustomBuildHook(BuildHookInterface): stderr.write('### npm install\n') subprocess.run([npm, 'install', '--force'], check=True) # noqa: S603 stderr.write('\n### npm run build\n') - os.environ['APP_BUILD_HASH'] = version node_options = os.environ.get('NODE_OPTIONS', '') if '--max-old-space-size' not in node_options: os.environ['NODE_OPTIONS'] = f'{node_options} --max-old-space-size=8192'.strip() diff --git a/scripts/build-info.js b/scripts/build-info.js new file mode 100644 index 0000000000..c8b8816d9e --- /dev/null +++ b/scripts/build-info.js @@ -0,0 +1,43 @@ +import { execFileSync } from 'node:child_process'; +import { readFileSync } from 'node:fs'; +import { join } from 'node:path'; +import { fileURLToPath } from 'node:url'; + +export function resolveBuildInfo({ + env = process.env, + cwd = fileURLToPath(new URL('../', import.meta.url)) +} = {}) { + const git = (...args) => { + try { + return execFileSync('git', args, { + cwd, + encoding: 'utf8', + stdio: ['ignore', 'pipe', 'ignore'] + }).trim(); + } catch { + return ''; + } + }; + + const suppliedHash = env.APP_BUILD_HASH ?? ''; + const hash = /^(?:[a-f\d]{7,40}|[a-f\d]{64})$/i.test(suppliedHash) + ? suppliedHash + : git('rev-parse', 'HEAD'); + let channel = env.APP_BUILD_CHANNEL; + if (!['main', 'dev', 'unknown'].includes(channel)) { + const branch = git('branch', '--show-current'); + channel = branch === 'main' || branch === 'dev' ? branch : 'unknown'; + if (!branch && hash) { + try { + const { version } = JSON.parse(readFileSync(join(cwd, 'package.json'), 'utf8')); + if (git('tag', '--points-at', 'HEAD').split('\n').includes(`v${version}`)) { + channel = 'main'; + } + } catch { + // Source archives may have neither Git metadata nor a package version. + } + } + } + + return { channel, hash }; +} diff --git a/src/app.d.ts b/src/app.d.ts index f59b884c51..e3b97f2232 100644 --- a/src/app.d.ts +++ b/src/app.d.ts @@ -1,6 +1,10 @@ // See https://kit.svelte.dev/docs/types#app // for information about these interfaces declare global { + const APP_VERSION: string; + const APP_BUILD_HASH: string; + const APP_BUILD_CHANNEL: 'main' | 'dev' | 'unknown'; + namespace App { // interface Error {} // interface Locals {} diff --git a/src/lib/components/admin/Settings/General.svelte b/src/lib/components/admin/Settings/General.svelte index 88b4412db7..65f91be375 100644 --- a/src/lib/components/admin/Settings/General.svelte +++ b/src/lib/components/admin/Settings/General.svelte @@ -9,8 +9,8 @@ import SettingsSelect from '$lib/components/common/SettingsSelect.svelte'; import LanguageModeSelect from '$lib/components/common/LanguageModeSelect.svelte'; import Switch from '$lib/components/common/Switch.svelte'; - import Tooltip from '$lib/components/common/Tooltip.svelte'; - import { WEBUI_BUILD_HASH, WEBUI_VERSION } from '$lib/constants'; + import BuildVersion from '$lib/components/common/BuildVersion.svelte'; + import { WEBUI_BUILD_CHANNEL, WEBUI_VERSION } from '$lib/constants'; import { banners as _banners, config, showChangelog } from '$lib/stores'; import type { Banner } from '$lib/types'; import { compareVersion } from '$lib/utils'; @@ -112,7 +112,7 @@ banners = [...$_banners]; - if ($config?.features?.enable_version_update_check) { + if (WEBUI_BUILD_CHANNEL === 'main' && $config?.features?.enable_version_update_check) { checkForVersionUpdates(); } }); @@ -137,9 +137,9 @@ {$i18n.t('settings.admin.general.version.label')}
- v{WEBUI_VERSION} + - {#if $config?.features?.enable_version_update_check} + {#if WEBUI_BUILD_CHANNEL === 'main' && $config?.features?.enable_version_update_check} {#if version.latest === null} {$i18n.t('Could not check for updates')}
- {#if $config?.features?.enable_version_update_check} + {#if WEBUI_BUILD_CHANNEL === 'main' && $config?.features?.enable_version_update_check} - {#if $config?.features?.enable_version_update_check} + {#if WEBUI_BUILD_CHANNEL === 'main' && $config?.features?.enable_version_update_check}