Files
mdm-ordinis/ordinis-admin-ui/vite.config.ts
T
Zimin A.N. 3223a5ced7 fix(version): channel-based label, no semver fallback for branch builds
Stale package.json создавал ложное впечатление rollback'а: staging
показывал "v1.2.0" хотя последний tag — v1.2.2 (package.json не bump'ился
со времён v1.2.0 release).

Vote A — branch builds НЕ используют package.json как version label.
Vite определяет channel:

  serverTag != none → что прод/staging RUNS
  clientTag         → что build'ил CI tag pipeline (CI_COMMIT_TAG set)
  CI без tag        → 'dev' literal (staging / branch CI build)
  локально          → 'local' literal (pnpm dev)

Detection через новый __APP_CI__ define из process.env.CI (GitLab сетает
CI=true). Local pnpm dev: CI undefined → channel='local'.

package.json.version всё ещё инжектится как __APP_VERSION__ и видна в
tooltip badge'а как 'floor vX.Y.Z' — не как primary label.

Files:
  - vite.config.ts: + APP_CI from process.env.CI
  - src/vite-env.d.ts: + __APP_CI__ declare
  - src/components/version/useAppVersion.ts: + clientCI
  - src/components/version/VersionBadge.tsx: channel logic вместо fallback

Verify all three modes:
  local         → 'local · {commit}'
  CI branch     → 'dev · {commit}'
  CI tag v1.3.1 → 'v1.3.1 · {commit}'

Tests: 116 pass.
2026-05-11 14:45:04 +03:00

137 lines
5.6 KiB
TypeScript
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
/// <reference types="vitest" />
import { defineConfig } from 'vite'
import react from '@vitejs/plugin-react'
import tailwindcss from '@tailwindcss/vite'
import { TanStackRouterVite } from '@tanstack/router-vite-plugin'
import path from 'path'
import { execSync } from 'node:child_process'
import { readFileSync } from 'node:fs'
// Build identity injected at compile time. Used by VersionBadge / UpdateBanner.
//
// 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/, '')
// GitLab CI ставит CI=true. Используем для discriminate "CI branch build"
// (нет tag'а, но есть pipeline) vs "local pnpm dev" (нет ни того ни другого).
// VersionBadge показывает 'dev' для первого, 'local' для второго.
const APP_CI: boolean = process.env.CI === 'true'
const APP_COMMIT: string =
process.env.CI_COMMIT_SHORT_SHA ??
(() => {
try {
return execSync('git rev-parse --short=8 HEAD', { stdio: ['ignore', 'pipe', 'ignore'] })
.toString()
.trim()
} catch {
return 'unknown'
}
})()
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_CI__: JSON.stringify(APP_CI),
__APP_COMMIT__: JSON.stringify(APP_COMMIT),
__APP_BUILT_AT__: JSON.stringify(APP_BUILT_AT),
},
test: {
environment: 'jsdom',
setupFiles: ['./src/test/setup.ts'],
globals: true,
css: false,
},
plugins: [
TanStackRouterVite({ routesDirectory: 'src/routes', generatedRouteTree: 'src/routeTree.gen.ts' }),
react(),
tailwindcss(),
],
resolve: {
alias: {
'@': path.resolve(__dirname, 'src'),
},
},
server: {
host: '0.0.0.0',
port: 5173,
proxy: {
'/api': {
target: process.env.VITE_API_PROXY ?? 'http://localhost:8080',
changeOrigin: true,
// secure: false нужен когда target — HTTPS endpoint с self-signed
// сертификатом (port-forward с локальным CA или внутренний cluster
// service). Для prod/staging Let's Encrypt cert validation не нужно
// отключать но игнор не вредит для dev mode.
secure: false,
// ws: true — на будущее когда Server-Sent Events / WebSocket каналы
// появятся (real-time notifications, audit stream).
ws: true,
},
},
},
build: {
chunkSizeWarningLimit: 600,
rollupOptions: {
output: {
// Группировка по принципу: что меняется редко → отдельный chunk →
// браузер кэширует надолго. Что меняется часто (наш код) — в main.
manualChunks: (id) => {
if (!id.includes('node_modules')) return undefined
// React core — стабильный, основа всего
if (id.match(/node_modules\/(react|react-dom|scheduler)\//)) return 'vendor-react'
// TanStack — Router + Query, крупные но стабильные
if (id.includes('node_modules/@tanstack/')) return 'vendor-tanstack'
// @nstart/ui — самый тяжёлый: brand stickers/animations + tons of UI
// (проверяй динамически: lottie, echarts если есть, motion)
if (id.includes('node_modules/@nstart/ui')) return 'vendor-nstart-ui'
// Иконки — отдельно, ленивая загрузка эффективна
if (id.includes('node_modules/@phosphor-icons/')) return 'vendor-icons'
// Формы + JSON Schema валидация
if (id.match(/node_modules\/(react-hook-form|@hookform|ajv|ajv-formats)\//)) {
return 'vendor-forms'
}
// OIDC / auth
if (id.match(/node_modules\/(oidc-client-ts|react-oidc-context)\//)) {
return 'vendor-auth'
}
// i18next — отдельно
if (id.match(/node_modules\/(i18next|react-i18next|i18next-browser-languagedetector)\//)) {
return 'vendor-i18n'
}
// Axios + парсеры
if (id.match(/node_modules\/(axios|jose|crypto-js)\//)) return 'vendor-net'
// Остальное — в общий vendor (не должно быть много)
return 'vendor-misc'
},
},
},
},
})