From e62633d903c063540d521c4b063795aade1b6a14 Mon Sep 17 00:00:00 2001 From: "Zimin A.N." Date: Mon, 11 May 2026 14:19:50 +0300 Subject: [PATCH] fix(version): read CI_COMMIT_TAG as source of truth, package.json as fallback MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Previously: vite read package.json.version only, requiring manual bump before each tag pipeline (manual sync ломалось — MR !44 пример костыля). Now: vite reads CI_COMMIT_TAG (passed via Dockerfile ARG GIT_TAG from .gitlab-ci.yml .docker_base) → strips 'v' prefix → exposes как __APP_TAG__. 'none' sentinel filtered → empty string → fallback к package.json. VersionBadge priority chain: 1. serverTag (что прод RUNS, через /api/v1/version) 2. clientTag (что build'илось на tag pipeline) 3. v${clientVersion} (package.json fallback floor для local / branch) Dockerfile: add ARG GIT_TAG=none, ENV CI_COMMIT_TAG=$GIT_TAG (mirrors существующий pattern с GIT_COMMIT → CI_COMMIT_SHORT_SHA). Revert package.json 1.3.1 → 1.2.0 (manual bump из MR !44). Версия теперь follows tag, не наоборот. --- ordinis-admin-ui/Dockerfile | 17 ++++++++------ ordinis-admin-ui/package.json | 2 +- .../src/components/version/VersionBadge.tsx | 17 ++++++++++---- .../src/components/version/useAppVersion.ts | 4 ++++ ordinis-admin-ui/src/vite-env.d.ts | 9 ++++++++ ordinis-admin-ui/vite.config.ts | 23 +++++++++++++++---- 6 files changed, 55 insertions(+), 17 deletions(-) diff --git a/ordinis-admin-ui/Dockerfile b/ordinis-admin-ui/Dockerfile index ae46f68..2a07960 100644 --- a/ordinis-admin-ui/Dockerfile +++ b/ordinis-admin-ui/Dockerfile @@ -1,13 +1,16 @@ FROM repo.nstart.cloud/library/node:22-alpine AS build -# Git commit для VersionBadge / UpdateBanner (vite.config.ts читает -# process.env.CI_COMMIT_SHORT_SHA на build time). Внутри Docker build -# context нет .git/ (BUILD_CONTEXT=ordinis-admin-ui/), vite -# execSync('git rev-parse') падает → fallback "unknown". -# CI передаёт реальный SHA через --build-arg GIT_COMMIT (см. .gitlab-ci.yml -# publish_script). ENV exposes его vite-процессу через CI_COMMIT_SHORT_SHA -# (тот же ключ что vite.config.ts проверяет первым). +# Build identity для VersionBadge / UpdateBanner. Внутри Docker build context +# нет .git/ (BUILD_CONTEXT=ordinis-admin-ui/), vite execSync падает → +# fallback "unknown" / package.json. CI передаёт реальные значения через +# --build-arg (см. .gitlab-ci.yml publish_script .docker_base). +# +# GIT_TAG — "none" sentinel когда CI_COMMIT_TAG не set (branch pipeline). +# Фильтруем "none" → пустая строка чтобы vite.config.ts видел falsy и +# fallback'ил к package.json. ARG GIT_COMMIT=unknown +ARG GIT_TAG=none ENV CI_COMMIT_SHORT_SHA=$GIT_COMMIT +ENV CI_COMMIT_TAG=$GIT_TAG WORKDIR /app # .npmrc нужен ДО pnpm install, иначе @nstart/* резолвятся в public npm и 404. # pnpm-lock.yaml — для воспроизводимости (frozen lockfile в CI). diff --git a/ordinis-admin-ui/package.json b/ordinis-admin-ui/package.json index 5cb254f..281f70c 100644 --- a/ordinis-admin-ui/package.json +++ b/ordinis-admin-ui/package.json @@ -1,7 +1,7 @@ { "name": "ordinis-admin-ui", "private": true, - "version": "1.3.1", + "version": "1.2.0", "type": "module", "scripts": { "dev": "vite", diff --git a/ordinis-admin-ui/src/components/version/VersionBadge.tsx b/ordinis-admin-ui/src/components/version/VersionBadge.tsx index 762ac03..017c8a4 100644 --- a/ordinis-admin-ui/src/components/version/VersionBadge.tsx +++ b/ordinis-admin-ui/src/components/version/VersionBadge.tsx @@ -13,6 +13,7 @@ import { useAppVersion } from './useAppVersion' export function VersionBadge() { const { clientVersion, + clientTag, clientCommit, clientBuiltAt, serverVersion, @@ -21,8 +22,18 @@ export function VersionBadge() { updateAvailable, } = useAppVersion() + // Priority: serverTag (что прод RUNS) → clientTag (что build'илось на tag + // pipeline) → v${clientVersion} (package.json fallback для local / branch). + // 'none' — Spring Boot build-info sentinel когда tag отсутствует. + const displayVersion = + serverTag && serverTag !== 'none' + ? serverTag + : clientTag + ? `v${clientTag}` + : `v${clientVersion}` + const tooltip = [ - `client: ${clientVersion} (${clientCommit})`, + `client: ${clientTag ? `v${clientTag}` : `v${clientVersion}`} (${clientCommit})`, `server: ${serverTag || serverVersion} (${serverCommit ?? '...'})`, `built: ${clientBuiltAt}`, ].join('\n') @@ -38,9 +49,7 @@ export function VersionBadge() { className="inline-block w-1.5 h-1.5 rounded-full bg-accent animate-pulse" /> )} - {/* Prefer server tag (v1.2.0 etc.) над package.json clientVersion (0.1.0). - serverTag только при tag build'е — на branch builds показываем clientVersion. */} - {serverTag && serverTag !== 'none' ? serverTag : `v${clientVersion}`} + {displayVersion} · {(serverCommit || clientCommit).substring(0, 8)} diff --git a/ordinis-admin-ui/src/components/version/useAppVersion.ts b/ordinis-admin-ui/src/components/version/useAppVersion.ts index 19681fe..7203643 100644 --- a/ordinis-admin-ui/src/components/version/useAppVersion.ts +++ b/ordinis-admin-ui/src/components/version/useAppVersion.ts @@ -20,6 +20,9 @@ export function useAppVersion() { const clientCommit = __APP_COMMIT__ const clientVersion = __APP_VERSION__ + // Empty string when build wasn't на tag pipeline. VersionBadge / UpdateBanner + // решают priority: serverTag → clientTag → fallback v${clientVersion}. + const clientTag = __APP_TAG__ const clientBuiltAt = __APP_BUILT_AT__ const knownClient = clientCommit !== 'unknown' && clientCommit !== '' @@ -29,6 +32,7 @@ export function useAppVersion() { return { clientVersion, + clientTag, clientCommit, clientBuiltAt, serverVersion: server?.version, diff --git a/ordinis-admin-ui/src/vite-env.d.ts b/ordinis-admin-ui/src/vite-env.d.ts index e28b598..29dea28 100644 --- a/ordinis-admin-ui/src/vite-env.d.ts +++ b/ordinis-admin-ui/src/vite-env.d.ts @@ -13,7 +13,16 @@ interface ImportMeta { * Build identity injected via Vite {@code define} (см. vite.config.ts). * Используются {@code useAppVersion} hook + {@code UpdateBanner} component * для server-vs-client comparison. + * + * */ declare const __APP_VERSION__: string +declare const __APP_TAG__: string declare const __APP_COMMIT__: string declare const __APP_BUILT_AT__: string diff --git a/ordinis-admin-ui/vite.config.ts b/ordinis-admin-ui/vite.config.ts index 652240d..a09486f 100644 --- a/ordinis-admin-ui/vite.config.ts +++ b/ordinis-admin-ui/vite.config.ts @@ -7,15 +7,27 @@ import path from 'path' import { execSync } from 'node:child_process' import { readFileSync } from 'node:fs' -// Build identity injected at compile time. Used by /update banner — client -// сравнивает свой commit с server's /api/v1/version и показывает "Доступна -// новая версия" если различаются. +// Build identity injected at compile time. Used by VersionBadge / UpdateBanner. // -// Fallbacks для CI / sandboxed builds где git unavailable: env var override, -// затем "unknown". +// Priority chain (most authoritative first): +// 1. CI_COMMIT_TAG → set ONLY on tag pipelines (e.g. "v1.3.1"). Это source +// of truth для release builds. Strip leading 'v' → "1.3.1". +// 2. package.json.version → fallback floor для local dev и branch builds. +// НЕ обновляется вручную — release flow добавит автоматический bump +// позже (release script tag + npm version в одном commit'е). +// +// __APP_TAG__ — пустая строка если не tag build. VersionBadge решает что +// показать (приоритет: serverTag → clientTag → v${clientVersion}). +// +// __APP_COMMIT__ — 8-char short SHA. CI передаёт через CI_COMMIT_SHORT_SHA +// (GitLab default 8 chars), local через `git rev-parse --short=8`. const APP_VERSION: string = JSON.parse( readFileSync(path.resolve(__dirname, 'package.json'), 'utf-8'), ).version +// 'none' — Dockerfile default ARG value (см. .gitlab-ci.yml passes +// --build-arg GIT_TAG="${CI_COMMIT_TAG:-none}"). Filter → пустая строка. +const RAW_TAG = process.env.CI_COMMIT_TAG ?? '' +const APP_TAG: string = RAW_TAG === 'none' ? '' : RAW_TAG.replace(/^v/, '') const APP_COMMIT: string = process.env.CI_COMMIT_SHORT_SHA ?? (() => { @@ -32,6 +44,7 @@ const APP_BUILT_AT: string = new Date().toISOString() export default defineConfig({ define: { __APP_VERSION__: JSON.stringify(APP_VERSION), + __APP_TAG__: JSON.stringify(APP_TAG), __APP_COMMIT__: JSON.stringify(APP_COMMIT), __APP_BUILT_AT__: JSON.stringify(APP_BUILT_AT), },