feat: Spec product version, build bake, Admin UI label.
Resolve VERSION from EventHubSpec; VITE_APP_BUILD from run_number; show Admin UI / API as version.build (sha). Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
@@ -50,12 +50,18 @@ jobs:
|
|||||||
|
|
||||||
- name: Bake build identity
|
- name: Bake build identity
|
||||||
id: build_meta
|
id: build_meta
|
||||||
|
env:
|
||||||
|
REGISTRY_PASSWORD: ${{ secrets.REGISTRY_PASSWORD }}
|
||||||
|
GITEA_TOKEN: ${{ secrets.REGISTRY_PASSWORD }}
|
||||||
run: |
|
run: |
|
||||||
set -euo pipefail
|
set -euo pipefail
|
||||||
APP_VERSION="$(tr -d '[:space:]' < VERSION 2>/dev/null || echo 0.0)"
|
APP_VERSION="$(bash scripts/resolve-product-version.sh)"
|
||||||
|
APP_BUILD="${GITHUB_RUN_NUMBER:-0}"
|
||||||
GIT_SHA="${GITHUB_SHA:0:12}"
|
GIT_SHA="${GITHUB_SHA:0:12}"
|
||||||
BUILT_AT="$(date -u +%Y-%m-%dT%H:%M:%SZ)"
|
BUILT_AT="$(date -u +%Y-%m-%dT%H:%M:%SZ)"
|
||||||
|
echo "Bake: version=${APP_VERSION} build=${APP_BUILD} git_sha=${GIT_SHA}"
|
||||||
echo "VITE_APP_VERSION=${APP_VERSION}" >> "$GITHUB_ENV"
|
echo "VITE_APP_VERSION=${APP_VERSION}" >> "$GITHUB_ENV"
|
||||||
|
echo "VITE_APP_BUILD=${APP_BUILD}" >> "$GITHUB_ENV"
|
||||||
echo "VITE_GIT_SHA=${GIT_SHA}" >> "$GITHUB_ENV"
|
echo "VITE_GIT_SHA=${GIT_SHA}" >> "$GITHUB_ENV"
|
||||||
echo "VITE_BUILT_AT=${BUILT_AT}" >> "$GITHUB_ENV"
|
echo "VITE_BUILT_AT=${BUILT_AT}" >> "$GITHUB_ENV"
|
||||||
|
|
||||||
|
|||||||
@@ -84,6 +84,7 @@ export async function installAdminApiMocks(page: Page, options: MockOptions = {}
|
|||||||
status: 'ok',
|
status: 'ok',
|
||||||
service: 'eventhub',
|
service: 'eventhub',
|
||||||
version: '0.1',
|
version: '0.1',
|
||||||
|
build: 1,
|
||||||
git_sha: 'e2etestsha000',
|
git_sha: 'e2etestsha000',
|
||||||
built_at: '2026-07-14T00:00:00Z',
|
built_at: '2026-07-14T00:00:00Z',
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -0,0 +1,40 @@
|
|||||||
|
#!/usr/bin/env bash
|
||||||
|
# Product version (MAJOR.MINOR) из EventHubSpec/VERSION.
|
||||||
|
# Fallback: локальный VERSION (офлайн / без токена).
|
||||||
|
set -euo pipefail
|
||||||
|
|
||||||
|
BASE="${GITEA_BASE_URL:-https://git.sabilin.com}"
|
||||||
|
TOKEN="${GITEA_TOKEN:-${REGISTRY_PASSWORD:-}}"
|
||||||
|
REF="${PRODUCT_VERSION_REF:-master}"
|
||||||
|
FALLBACK_FILE="${PRODUCT_VERSION_FILE:-VERSION}"
|
||||||
|
|
||||||
|
fetch_spec() {
|
||||||
|
local url="${BASE}/api/v1/repos/EventHub/EventHubSpec/raw/VERSION?ref=${REF}"
|
||||||
|
if [[ -n "${TOKEN}" ]]; then
|
||||||
|
curl -fsSL -H "Authorization: token ${TOKEN}" "${url}"
|
||||||
|
else
|
||||||
|
curl -fsSL "${url}"
|
||||||
|
fi
|
||||||
|
}
|
||||||
|
|
||||||
|
ver=""
|
||||||
|
if raw="$(fetch_spec 2>/dev/null)"; then
|
||||||
|
ver="$(printf '%s' "${raw}" | grep -Eo '[0-9]+\.[0-9]+' | head -1 || true)"
|
||||||
|
fi
|
||||||
|
|
||||||
|
if [[ -z "${ver}" && -f "${FALLBACK_FILE}" ]]; then
|
||||||
|
echo "WARN: EventHubSpec/VERSION недоступен — fallback ${FALLBACK_FILE}" >&2
|
||||||
|
ver="$(grep -Eo '[0-9]+\.[0-9]+' "${FALLBACK_FILE}" | head -1 || true)"
|
||||||
|
fi
|
||||||
|
|
||||||
|
if [[ -z "${ver}" ]]; then
|
||||||
|
echo "ERROR: не удалось получить product version (Spec + local VERSION)" >&2
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
if ! [[ "${ver}" =~ ^[0-9]+\.[0-9]+$ ]]; then
|
||||||
|
echo "ERROR: некорректный VERSION '${ver}' (ожидается MAJOR.MINOR)" >&2
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
printf '%s\n' "${ver}"
|
||||||
@@ -4,6 +4,7 @@ export type AdminHealthInfo = {
|
|||||||
status: string;
|
status: string;
|
||||||
service?: string;
|
service?: string;
|
||||||
version?: string;
|
version?: string;
|
||||||
|
build?: number;
|
||||||
git_sha?: string;
|
git_sha?: string;
|
||||||
built_at?: string;
|
built_at?: string;
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -57,9 +57,13 @@ import { useCommandPaletteHotkey } from '@/hooks/useCommandPaletteHotkey';
|
|||||||
import { HeaderNodeMetrics } from '@/components/HeaderNodeMetrics';
|
import { HeaderNodeMetrics } from '@/components/HeaderNodeMetrics';
|
||||||
import { healthApi } from '@/api/healthApi';
|
import { healthApi } from '@/api/healthApi';
|
||||||
|
|
||||||
function formatBuildLabel(version: string, sha: string): string {
|
function formatBuildLabel(version: string, build: number | string | undefined, sha: string): string {
|
||||||
const shortSha = sha && sha !== 'unknown' && sha !== 'dev' ? sha.slice(0, 12) : sha;
|
const shortSha = sha && sha !== 'unknown' && sha !== 'dev' ? sha.slice(0, 12) : sha;
|
||||||
return `${version} (${shortSha})`;
|
const buildPart =
|
||||||
|
build === undefined || build === null || build === '' || Number(build) === 0
|
||||||
|
? version
|
||||||
|
: `${version}.${build}`;
|
||||||
|
return `${buildPart} (${shortSha})`;
|
||||||
}
|
}
|
||||||
|
|
||||||
const navIconByKey: Record<string, React.ComponentType<{ className?: string }>> = {
|
const navIconByKey: Record<string, React.ComponentType<{ className?: string }>> = {
|
||||||
@@ -125,6 +129,7 @@ const ControlCenterLayout: React.FC = () => {
|
|||||||
|
|
||||||
const adminVersionLabel = formatBuildLabel(
|
const adminVersionLabel = formatBuildLabel(
|
||||||
import.meta.env.VITE_APP_VERSION || '0.0',
|
import.meta.env.VITE_APP_VERSION || '0.0',
|
||||||
|
import.meta.env.VITE_APP_BUILD || '0',
|
||||||
import.meta.env.VITE_GIT_SHA || 'dev'
|
import.meta.env.VITE_GIT_SHA || 'dev'
|
||||||
);
|
);
|
||||||
|
|
||||||
@@ -135,7 +140,11 @@ const ControlCenterLayout: React.FC = () => {
|
|||||||
.then((info) => {
|
.then((info) => {
|
||||||
if (cancelled) return;
|
if (cancelled) return;
|
||||||
setApiVersionLabel(
|
setApiVersionLabel(
|
||||||
formatBuildLabel(info.version || '0.0', info.git_sha || 'unknown')
|
formatBuildLabel(
|
||||||
|
info.version || '0.0',
|
||||||
|
info.build,
|
||||||
|
info.git_sha || 'unknown'
|
||||||
|
)
|
||||||
);
|
);
|
||||||
})
|
})
|
||||||
.catch(() => {
|
.catch(() => {
|
||||||
|
|||||||
@@ -32,6 +32,7 @@ function buildContext(extra?: Record<string, unknown>): Record<string, unknown>
|
|||||||
route: typeof window !== 'undefined' ? window.location.pathname : undefined,
|
route: typeof window !== 'undefined' ? window.location.pathname : undefined,
|
||||||
user_agent: typeof navigator !== 'undefined' ? navigator.userAgent : undefined,
|
user_agent: typeof navigator !== 'undefined' ? navigator.userAgent : undefined,
|
||||||
build: import.meta.env.VITE_APP_VERSION || '0.0',
|
build: import.meta.env.VITE_APP_VERSION || '0.0',
|
||||||
|
build_number: import.meta.env.VITE_APP_BUILD || '0',
|
||||||
git_sha: import.meta.env.VITE_GIT_SHA || 'dev',
|
git_sha: import.meta.env.VITE_GIT_SHA || 'dev',
|
||||||
...extra,
|
...extra,
|
||||||
};
|
};
|
||||||
|
|||||||
+1
-1
@@ -195,7 +195,7 @@
|
|||||||
"nodeMemory": "Memory: {{used}} / {{total}} MB ({{percent}}%)",
|
"nodeMemory": "Memory: {{used}} / {{total}} MB ({{percent}}%)",
|
||||||
"nodeWas": "was {{value}}%",
|
"nodeWas": "was {{value}}%",
|
||||||
"nodeAria": "{{node}}: CPU {{cpu}}%, memory {{memory}}%",
|
"nodeAria": "{{node}}: CPU {{cpu}}%, memory {{memory}}%",
|
||||||
"adminVersion": "Admin {{version}}",
|
"adminVersion": "Admin UI {{version}}",
|
||||||
"apiVersion": "API {{version}}",
|
"apiVersion": "API {{version}}",
|
||||||
"apiVersionUnavailable": "API — unavailable"
|
"apiVersionUnavailable": "API — unavailable"
|
||||||
},
|
},
|
||||||
|
|||||||
+1
-1
@@ -195,7 +195,7 @@
|
|||||||
"nodeMemory": "Память: {{used}} / {{total}} МБ ({{percent}}%)",
|
"nodeMemory": "Память: {{used}} / {{total}} МБ ({{percent}}%)",
|
||||||
"nodeWas": "было {{value}}%",
|
"nodeWas": "было {{value}}%",
|
||||||
"nodeAria": "{{node}}: CPU {{cpu}}%, память {{memory}}%",
|
"nodeAria": "{{node}}: CPU {{cpu}}%, память {{memory}}%",
|
||||||
"adminVersion": "Admin {{version}}",
|
"adminVersion": "Admin UI {{version}}",
|
||||||
"apiVersion": "API {{version}}",
|
"apiVersion": "API {{version}}",
|
||||||
"apiVersionUnavailable": "API — нет данных"
|
"apiVersionUnavailable": "API — нет данных"
|
||||||
},
|
},
|
||||||
|
|||||||
@@ -7,6 +7,7 @@ const apiBaseUrl = process.env.VITE_API_BASE_URL ?? 'https://admin-api.dev.event
|
|||||||
const wsUrl = process.env.VITE_WS_URL ?? 'wss://admin-ws.dev.eventhub.local';
|
const wsUrl = process.env.VITE_WS_URL ?? 'wss://admin-ws.dev.eventhub.local';
|
||||||
|
|
||||||
const appVersion = process.env.VITE_APP_VERSION ?? '0.0';
|
const appVersion = process.env.VITE_APP_VERSION ?? '0.0';
|
||||||
|
const appBuild = process.env.VITE_APP_BUILD ?? '0';
|
||||||
const gitSha = process.env.VITE_GIT_SHA ?? 'dev';
|
const gitSha = process.env.VITE_GIT_SHA ?? 'dev';
|
||||||
const builtAt = process.env.VITE_BUILT_AT ?? '';
|
const builtAt = process.env.VITE_BUILT_AT ?? '';
|
||||||
|
|
||||||
@@ -14,6 +15,7 @@ export default defineConfig({
|
|||||||
plugins: [react(), tailwindcss()],
|
plugins: [react(), tailwindcss()],
|
||||||
define: {
|
define: {
|
||||||
'import.meta.env.VITE_APP_VERSION': JSON.stringify(appVersion),
|
'import.meta.env.VITE_APP_VERSION': JSON.stringify(appVersion),
|
||||||
|
'import.meta.env.VITE_APP_BUILD': JSON.stringify(appBuild),
|
||||||
'import.meta.env.VITE_GIT_SHA': JSON.stringify(gitSha),
|
'import.meta.env.VITE_GIT_SHA': JSON.stringify(gitSha),
|
||||||
'import.meta.env.VITE_BUILT_AT': JSON.stringify(builtAt),
|
'import.meta.env.VITE_BUILT_AT': JSON.stringify(builtAt),
|
||||||
},
|
},
|
||||||
|
|||||||
Reference in New Issue
Block a user