diff --git a/.github/workflows/docker.yml b/.github/workflows/docker.yml
index f8095e8..80dd0a3 100644
--- a/.github/workflows/docker.yml
+++ b/.github/workflows/docker.yml
@@ -3,6 +3,8 @@ name: Docker
on:
push:
branches: [main]
+ # Релиз: тег vX.Y.Z → образ ghcr.io/…:X.Y.Z и :X.Y
+ tags: ['v*']
pull_request:
branches: [main]
@@ -20,7 +22,7 @@ jobs:
uses: docker/setup-buildx-action@v3
- name: Log in to GitHub Container Registry
- if: github.event_name == 'push' && github.ref == 'refs/heads/main'
+ if: github.event_name == 'push'
uses: docker/login-action@v3
with:
registry: ghcr.io
@@ -34,12 +36,14 @@ jobs:
images: ghcr.io/${{ github.repository }}
tags: |
type=raw,value=latest,enable=${{ github.event_name == 'push' && github.ref == 'refs/heads/main' }}
+ type=semver,pattern={{version}}
+ type=semver,pattern={{major}}.{{minor}}
type=sha
- name: Build and push
uses: docker/build-push-action@v6
with:
context: .
- push: ${{ github.event_name == 'push' && github.ref == 'refs/heads/main' }}
+ push: ${{ github.event_name == 'push' }}
tags: ${{ steps.meta.outputs.tags }}
labels: ${{ steps.meta.outputs.labels }}
diff --git a/README.md b/README.md
index 68a2132..15220ab 100644
--- a/README.md
+++ b/README.md
@@ -129,6 +129,8 @@ docker stop vps-tracker && docker rm vps-tracker
С Compose: `git pull && docker compose up -d` (`pull_policy: always` подтянет свежий образ).
+Версии: каждый релиз — git-тег `vX.Y.Z` и образ `ghcr.io/denozordec/vps-tracker:X.Y.Z` (и `:X.Y`); `latest` — текущий `main`. Чтобы не обновляться неожиданно, закрепите версию: `ghcr.io/denozordec/vps-tracker:1.0.0`. Текущая версия видна в меню пользователя и в ответе `GET /health`.
+
Другой порт снаружи: `-p 8080:3001` → `http://localhost:8080`. Сменить порт внутри контейнера: `-e PORT=8080 -p 8080:8080`.
---
diff --git a/apps/api/package.json b/apps/api/package.json
index 17e6434..b014d3b 100644
--- a/apps/api/package.json
+++ b/apps/api/package.json
@@ -1,7 +1,7 @@
{
"name": "@cfdm/api",
"private": true,
- "version": "0.0.0",
+ "version": "1.0.0",
"type": "module",
"scripts": {
"dev": "pnpm --filter @cfdm/shared build && pnpm --filter @cfdm/db build && tsx watch src/index.ts",
diff --git a/apps/api/src/index.ts b/apps/api/src/index.ts
index 5979d6c..463e24c 100644
--- a/apps/api/src/index.ts
+++ b/apps/api/src/index.ts
@@ -3,7 +3,7 @@ import cors from '@fastify/cors'
import sensible from '@fastify/sensible'
import staticPlugin from '@fastify/static'
import rateLimit from '@fastify/rate-limit'
-import { existsSync } from 'node:fs'
+import { existsSync, readFileSync } from 'node:fs'
import { dirname, join } from 'node:path'
import { fileURLToPath } from 'node:url'
@@ -38,6 +38,11 @@ import { portalUsersRoutes } from './routes/portal-users.js'
const __dirname = dirname(fileURLToPath(import.meta.url))
+/** Версия из apps/api/package.json (src/ и dist/ лежат на одном уровне с ним). */
+export const APP_VERSION: string = (
+ JSON.parse(readFileSync(new URL('../package.json', import.meta.url), 'utf8')) as { version: string }
+).version
+
export interface BuildAppOptions {
dbPath?: string
staticDir?: string
@@ -67,7 +72,7 @@ export async function buildApp(opts: BuildAppOptions = {}) {
await app.register(authPlugin)
await app.register(spacePlugin)
- app.get('/health', async () => ({ ok: true }))
+ app.get('/health', async () => ({ ok: true, version: APP_VERSION }))
await app.register(launcherRoutes)
await app.register(spacesRoutes)
diff --git a/apps/api/src/routes/review-guards.test.ts b/apps/api/src/routes/review-guards.test.ts
index f97b50d..fbf3225 100644
--- a/apps/api/src/routes/review-guards.test.ts
+++ b/apps/api/src/routes/review-guards.test.ts
@@ -187,3 +187,17 @@ describe('dashboard stats', () => {
await app.close()
})
})
+
+describe('/health', () => {
+ beforeEach(() => resetTestDb())
+ afterEach(() => closeDb())
+
+ it('returns the release version from package.json', async () => {
+ const { version } = (await import('../../package.json', { with: { type: 'json' } })).default
+ const app = await buildApp()
+ const res = await app.inject({ url: '/health' })
+ expect(res.json()).toEqual({ ok: true, version })
+ expect(version).toMatch(/^\d+\.\d+\.\d+$/)
+ await app.close()
+ })
+})
diff --git a/apps/web/package.json b/apps/web/package.json
index 6d61bff..dd41889 100644
--- a/apps/web/package.json
+++ b/apps/web/package.json
@@ -1,7 +1,7 @@
{
"name": "@cfdm/web",
"private": true,
- "version": "0.0.0",
+ "version": "1.0.0",
"type": "module",
"scripts": {
"dev": "vite",
diff --git a/apps/web/src/components/layout/nav-user.tsx b/apps/web/src/components/layout/nav-user.tsx
index 7a610a0..10dc71b 100644
--- a/apps/web/src/components/layout/nav-user.tsx
+++ b/apps/web/src/components/layout/nav-user.tsx
@@ -221,6 +221,12 @@ export function NavUser() {
>
) : null}
+
+
+
+ VPS Tracker v{__APP_VERSION__}
+
+
diff --git a/apps/web/src/vite-env.d.ts b/apps/web/src/vite-env.d.ts
index 2c41b58..3687953 100644
--- a/apps/web/src/vite-env.d.ts
+++ b/apps/web/src/vite-env.d.ts
@@ -10,3 +10,6 @@ interface ImportMetaEnv {
interface ImportMeta {
readonly env: ImportMetaEnv
}
+
+/** Версия релиза (vite define из корневого package.json). */
+declare const __APP_VERSION__: string
diff --git a/apps/web/vite.config.ts b/apps/web/vite.config.ts
index 71ade29..cbe4b18 100644
--- a/apps/web/vite.config.ts
+++ b/apps/web/vite.config.ts
@@ -3,8 +3,13 @@ import react from '@vitejs/plugin-react'
import tailwindcss from '@tailwindcss/vite'
import { TanStackRouterVite as TanStackRouterPlugin } from '@tanstack/router-plugin/vite'
import path from 'node:path'
+import { readFileSync } from 'node:fs'
+
+// Версия релиза одна на monorepo — из корневого package.json.
+const APP_VERSION = (JSON.parse(readFileSync(path.resolve(__dirname, '../../package.json'), 'utf8')) as { version: string }).version
export default defineConfig({
+ define: { __APP_VERSION__: JSON.stringify(APP_VERSION) },
plugins: [
TanStackRouterPlugin({ target: 'react', autoCodeSplitting: true }),
react(),
diff --git a/package.json b/package.json
index 5b895a3..c02ef70 100644
--- a/package.json
+++ b/package.json
@@ -1,7 +1,7 @@
{
"name": "vps-tracker",
"private": true,
- "version": "0.0.0",
+ "version": "1.0.0",
"type": "module",
"scripts": {
"dev": "pnpm --filter web dev",
diff --git a/packages/db/package.json b/packages/db/package.json
index 3b66924..476091e 100644
--- a/packages/db/package.json
+++ b/packages/db/package.json
@@ -1,6 +1,6 @@
{
"name": "@cfdm/db",
- "version": "0.0.0",
+ "version": "1.0.0",
"private": true,
"type": "module",
"main": "./dist/index.js",
diff --git a/packages/shared/package.json b/packages/shared/package.json
index e10d57e..c5323b4 100644
--- a/packages/shared/package.json
+++ b/packages/shared/package.json
@@ -1,6 +1,6 @@
{
"name": "@cfdm/shared",
- "version": "0.0.0",
+ "version": "1.0.0",
"private": true,
"type": "module",
"main": "./dist/index.js",
diff --git a/packages/ui/package.json b/packages/ui/package.json
index 1271bd1..2c29f1e 100644
--- a/packages/ui/package.json
+++ b/packages/ui/package.json
@@ -1,6 +1,6 @@
{
"name": "@cfdm/ui",
- "version": "0.0.0",
+ "version": "1.0.0",
"private": true,
"type": "module",
"exports": {