Compare commits
6
Commits
c263fd5c7e
...
v1.0.1
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
b6b081b04f | ||
|
|
13b4c20148 | ||
|
|
700d415422 | ||
|
|
6ca34b9008 | ||
|
|
bb334b10f5 | ||
|
|
4c23232c4e |
No files matched your search
@@ -0,0 +1,3 @@
|
||||
{
|
||||
"extends": ["@commitlint/config-conventional"]
|
||||
}
|
||||
@@ -0,0 +1,113 @@
|
||||
---
|
||||
description: >-
|
||||
Conventional Commits EvoBGP. Триггеры commit/коммит/закоммить/staged/commit message —
|
||||
ОБЯЗАТЕЛЬНО сначала Shell scripts/commit/staged-context.ps1, скилл commit-message.
|
||||
alwaysApply: false
|
||||
---
|
||||
|
||||
# Conventional Commits (EvoBGP)
|
||||
|
||||
Полное описание релизного пайплайна: [docs/releasing.md](../../docs/releasing.md).
|
||||
|
||||
## Триггеры (применить правило + скилл)
|
||||
|
||||
Любой запрос на коммит или сообщение коммита: `commit`, `коммит`, `закоммить`, `git commit`, `commit message`, `conventional commit`, `staged`, «сгенерируй коммит» — в т.ч. если это указано в плане или [AGENTS.md](../../AGENTS.md).
|
||||
|
||||
## Обязательный запуск скрипта (MUST)
|
||||
|
||||
1. Прочитать скилл [`.cursor/skills/commit-message/SKILL.md`](../skills/commit-message/SKILL.md).
|
||||
2. **Первым действием** выполнить Shell (из корня репо):
|
||||
|
||||
```powershell
|
||||
powershell -NoProfile -File scripts/commit/staged-context.ps1
|
||||
```
|
||||
|
||||
3. Сообщение коммита строить **только** по JSON из stdout скрипта (`groups`, `diff_excerpt`, `stat`).
|
||||
4. **Запрещено** генерировать commit message без успешного (exit 0) запуска скрипта; не заменять скрипт одним `git diff --cached`.
|
||||
|
||||
## Формат (строго)
|
||||
|
||||
```
|
||||
<type>(<scope>): <short summary in English>
|
||||
|
||||
<тело на русском: что изменено и зачем>
|
||||
```
|
||||
|
||||
- **Заголовок** — только английский; императив, без точки в конце; ≤72 символов.
|
||||
- **Тело** — только русский; полные предложения; пустая строка после заголовка.
|
||||
- Запрещены vague-сообщения: `fix bug`, `update code`, `wip`, `misc`.
|
||||
|
||||
## Типы (semantic-release)
|
||||
|
||||
| type | Когда | Версия |
|
||||
|------|--------|--------|
|
||||
| `feat` | новая функциональность | minor |
|
||||
| `fix` | исправление бага | patch |
|
||||
| `perf` | ускорение без смены API | patch |
|
||||
| `refactor` | реструктуризация без смены поведения | — |
|
||||
| `docs` | только документация | — |
|
||||
| `test` | тесты | — |
|
||||
| `ci` | CI/CD (`.gitea/`, workflows) | — |
|
||||
| `chore` | обслуживание, deps, `.cursor/` | — |
|
||||
|
||||
## Breaking changes
|
||||
|
||||
- Заголовок: `feat!` / `fix!` **или** в теле строка `BREAKING CHANGE:` (на английском ключевое слово) + описание impact **на русском**.
|
||||
|
||||
## Scope (EvoBGP)
|
||||
|
||||
Выбирать по доминирующему пути из staged diff:
|
||||
|
||||
| Префикс | scope |
|
||||
|---------|-------|
|
||||
| `internal/httpapi/` | `httpapi` |
|
||||
| `internal/store/`, `internal/repository/`, `internal/db/` | `store` |
|
||||
| `internal/jobs/` | `jobs` |
|
||||
| `internal/pipeline/` | `pipeline` |
|
||||
| `internal/birdfmt/` | `birdfmt` |
|
||||
| `internal/birddeploy/` | `birddeploy` |
|
||||
| `internal/bundle/`, `internal/signing/` | `bundle` |
|
||||
| `cmd/` | `cmd` |
|
||||
| `web/` | `web` |
|
||||
| `docs/openapi.yaml`, `redocly.yaml` | `openapi` |
|
||||
| `docs/` (остальное) | `docs` |
|
||||
| `migrations/` | `db` |
|
||||
| `.gitea/` | `ci` |
|
||||
| `deploy/` | `deploy` |
|
||||
| `.cursor/` | `chore` |
|
||||
| прочее в корне | `chore` |
|
||||
|
||||
`type` определять по **содержимому diff**, не только по пути.
|
||||
|
||||
## Multi-change
|
||||
|
||||
- Несвязанные области → **отдельные коммиты** (auto-split по скиллу).
|
||||
- Одна логическая фича через несколько scope (OpenAPI + httpapi + web) → **один** коммит, scope по главной области.
|
||||
|
||||
## Обязательный вывод агенту
|
||||
|
||||
Для каждого коммита:
|
||||
|
||||
**1. Готовое сообщение** (копировать в `git commit -m` / HEREDOC):
|
||||
|
||||
```
|
||||
<type>(<scope>): <summary>
|
||||
|
||||
<тело RU>
|
||||
```
|
||||
|
||||
**2. Пояснение (RU):** почему выбран type; риск/impact; был ли split.
|
||||
|
||||
## Примеры
|
||||
|
||||
```
|
||||
feat(httpapi): add version endpoint and wire web footer
|
||||
|
||||
Добавлен GET /v1/version. В Web UI версия в footer берётся из API вместо захардкоженного значения.
|
||||
```
|
||||
|
||||
```
|
||||
fix(auth): correct token validation edge case
|
||||
|
||||
Исправлена ложная 401 при истёкшем refresh-токене с валидной сессией.
|
||||
```
|
||||
@@ -0,0 +1,124 @@
|
||||
---
|
||||
name: commit-message
|
||||
description: >-
|
||||
ОБЯЗАТЕЛЬНО при commit, коммит, закоммить, commit message, conventional commit,
|
||||
staged, semantic-release, «сгенерируй коммит», git commit: ПЕРВЫМ делом Shell —
|
||||
scripts/commit/staged-context.ps1; затем Conventional Commit (заголовок EN, тело RU).
|
||||
---
|
||||
|
||||
# Commit message (EvoBGP)
|
||||
|
||||
## Когда применять (сразу читать этот скилл)
|
||||
|
||||
Триггеры в промпте пользователя или в плане/AGENTS.md:
|
||||
|
||||
- `commit`, `коммит`, `закоммить`, `commit message`, `conventional commit`
|
||||
- `git commit`, `staged`, `сообщение коммита`, `сгенерируй коммит`
|
||||
- агент собирается выполнить `git commit` или предложить текст коммита
|
||||
|
||||
## Шаг 0 — ОБЯЗАТЕЛЬНО (до любого текста коммита)
|
||||
|
||||
**Первый вызов инструментов** в этой задаче — Shell из корня репозитория:
|
||||
|
||||
```powershell
|
||||
powershell -NoProfile -File scripts/commit/staged-context.ps1
|
||||
```
|
||||
|
||||
| Правило | Деталь |
|
||||
|---------|--------|
|
||||
| **MUST** | Запустить скрипт до генерации заголовка/тела коммита |
|
||||
| **MUST NOT** | Строить сообщение только по `git diff --cached` / `git status` без скрипта |
|
||||
| **MUST NOT** | Пропускать скрипт, даже если diff «и так понятен» |
|
||||
| Exit `1` | Index пуст — сообщить пользователю, **не коммитить** |
|
||||
| Exit `0` | Разобрать JSON stdout: `staged_count`, `groups[]` (`scope`, `files`, `stat`, `diff_excerpt`) |
|
||||
|
||||
Дополнительно: [.cursor/rules/conventional-commits.mdc](../../rules/conventional-commits.mdc).
|
||||
|
||||
## Слияние групп (одна фича)
|
||||
|
||||
Скрипт группирует **только по путям**. Перед split проверьте логическую связность:
|
||||
|
||||
**Объединить в один коммит**, если это одна задача:
|
||||
|
||||
- `docs/openapi.yaml` + `internal/httpapi/` (+ опционально `web/`) — один endpoint/контракт;
|
||||
- `migrations/` + `internal/store/` / `repository/` — одна схема;
|
||||
- правки теста рядом с кодом той же фичи.
|
||||
|
||||
При объединении: один scope (доминирующий пакет, часто `httpapi` или `openapi`), один type, одно тело RU.
|
||||
|
||||
**Auto-split** — если группы **не связаны** (например `web/` + `internal/birdfmt/` без общего смысла): **отдельный коммит на группу**, без вопроса пользователю.
|
||||
|
||||
## Порядок коммитов при split
|
||||
|
||||
1. `openapi`
|
||||
2. `httpapi`, `store`, `jobs`
|
||||
3. `pipeline`, `birdfmt`, `birddeploy`, `bundle`
|
||||
4. `web`
|
||||
5. `ci`, `deploy`, `db`
|
||||
6. `docs`
|
||||
7. `chore`, `cmd`
|
||||
|
||||
## Алгоритм auto-split (уровень файлов)
|
||||
|
||||
1. `git status` и `git diff --cached --stat` — зафиксировать полный список staged-файлов.
|
||||
2. `git reset HEAD` — снять всё из index (working tree не трогать).
|
||||
3. Для каждой (объединённой) группы по порядку выше:
|
||||
- `git add -- <paths…>`
|
||||
- Сгенерировать сообщение по правилу conventional-commits.
|
||||
- Закоммитить (см. ниже).
|
||||
4. После всех коммитов — `git status` для проверки.
|
||||
|
||||
**Ограничение:** частичный stage одного файла с разной семантикой — предупредить; split по hunk'ам не делать; предложить разнести правки по файлам.
|
||||
|
||||
## Создание коммита
|
||||
|
||||
Только если пользователь **явно** просил закоммитить. Иначе — только вывести готовые сообщения.
|
||||
|
||||
```powershell
|
||||
git commit -m "$( @'
|
||||
<type>(<scope>): <summary in English>
|
||||
|
||||
<Тело на русском.>
|
||||
'@ )"
|
||||
```
|
||||
|
||||
В PowerShell для многострочного тела используйте here-string как выше или `-m` для заголовка и `-m` для тела.
|
||||
|
||||
Перед коммитом: `git status`, `git diff --cached` для группы — убедиться, что stage соответствует сообщению.
|
||||
|
||||
## Генерация текста
|
||||
|
||||
По `groups[].diff_excerpt`, `stat`, `files`:
|
||||
|
||||
- **type** — по смыслу diff (`feat` / `fix` / …), не по умолчанию `chore`.
|
||||
- **scope** — из JSON группы или доминирующий при merge.
|
||||
- **summary** — конкретный, английский, императив.
|
||||
- **body** — русский: что, зачем, edge cases, breaking impact.
|
||||
|
||||
## Вывод пользователю
|
||||
|
||||
Для **каждого** коммита:
|
||||
|
||||
### 1. Готовое сообщение
|
||||
|
||||
```
|
||||
<type>(<scope>): <summary>
|
||||
|
||||
<тело>
|
||||
```
|
||||
|
||||
### 2. Пояснение (RU)
|
||||
|
||||
- Почему выбран type/scope.
|
||||
- Риски и impact.
|
||||
- Split: сколько коммитов и почему.
|
||||
|
||||
## Не смешивать
|
||||
|
||||
Один commit message — одна primary intent. Не объединять несвязанный `fix` и `feat` в один заголовок.
|
||||
|
||||
## Ссылки
|
||||
|
||||
- Правило: `.cursor/rules/conventional-commits.mdc`
|
||||
- Скрипт: `scripts/commit/staged-context.ps1`
|
||||
- Просмотр групп: `powershell -File scripts/commit/staged-context.ps1 | ConvertFrom-Json`
|
||||
+29
-51
@@ -1,75 +1,53 @@
|
||||
# Gitea Actions
|
||||
|
||||
В репозитории включён workflow [workflows/ci.yaml](workflows/ci.yaml).
|
||||
Workflow: [workflows/ci.yaml](workflows/ci.yaml).
|
||||
|
||||
Job **changes** вычисляет два флага:
|
||||
| Job | PR | push в main |
|
||||
|-----|-----|-------------|
|
||||
| changes, openapi, web, go, bird2 | quality gates | quality gates |
|
||||
| commitlint | да | — |
|
||||
| **release** | — | semantic-release + docker push (один run) |
|
||||
|
||||
- **`openapi`** — в диффе есть `docs/openapi.yaml` или `redocly.yaml` → запускается **openapi** (Redocly lint).
|
||||
- **`code`** — в диффе есть **хотя бы один файл вне «только документация»** → запускаются **go**, **bird2** и при push в main/master **docker-images**.
|
||||
Подробнее: [docs/releasing.md](../docs/releasing.md).
|
||||
|
||||
Если изменены **только** файлы из списка ниже, **`code=false`**: Go-тесты, bird2 и сборка образов **не** запускаются; при необходимости всё ещё выполняется **openapi**, если трогали OpenAPI или `redocly.yaml`.
|
||||
## CI (quality gates)
|
||||
|
||||
**Список путей «только документация» (без полной сборки):** всё под `docs/`, корневой `README.md`, `web/README.md`, `.gitea/README.md`, `redocly.yaml`.
|
||||
Job **changes** вычисляет флаги по путям в diff. Изменение `.gitea/workflows/*` поднимает полный прогон.
|
||||
|
||||
Любой другой путь (в т.ч. `.gitea/workflows/ci.yaml`, `go.mod`, `web/package.json`, `deploy/**`, `**/*.go`) включает полный пайплайн.
|
||||
На **pull request** — **commitlint** (Conventional Commits).
|
||||
|
||||
Требования к runner:
|
||||
Runner: `ubuntu-latest`, **bird2** из apt, Docker для job **release**.
|
||||
|
||||
- Метка `ubuntu-latest` (или измените `runs-on` под свои labels).
|
||||
- Job `bird2` ставит пакет **bird2** из репозитория **Ubuntu** на runner (как образ `evobgp-bird2` на `ubuntu:noble`) и вызывает `bird -c … -p` по файлам в checkout (без вложенного Docker: иначе bind-mount `/workspace/...` часто не виден демону Docker на хосте).
|
||||
- Job **docker-images** требует **Docker** и (для push) доступ к Container Registry Gitea с runner’а.
|
||||
## Release (job в ci.yaml)
|
||||
|
||||
## Сборка и публикация образов (`docker-images`)
|
||||
После успешных quality gates на **push в main** job **release**:
|
||||
|
||||
Запускается только при **push** в ветки `main` или `master` (после успешного job `go`). Собираются образы из `deploy/docker/*` (все бинарники через `gobinary`, плюс `evobgp-web`, `evobgp-agent`, `evobgp-bird2`).
|
||||
1. `npx semantic-release` — тег `vX.Y.Z` на **текущий commit** (без дополнительного commit в main).
|
||||
2. Gitea Release + `CHANGELOG.md` как attachment (не в git).
|
||||
3. `docker buildx bake default --push` с `VERSION=X.Y.Z` — в том же job.
|
||||
|
||||
### Секреты репозитория (для push в реестр)
|
||||
Если releasable-коммитов нет — semantic-release no-op, образы не публикуются.
|
||||
|
||||
Хост Container Registry в workflow **зашит**: **`git.shts.su`** (`docker/login-action` и теги образов).
|
||||
### Секреты
|
||||
|
||||
В настройках репозитория Gitea: **Settings → Actions → Secrets** (или аналог):
|
||||
**`ACTIONS_PAT`**: push tags, releases, Container Registry. Fallback: **`gitea.token`**.
|
||||
|
||||
| Секрет | Назначение |
|
||||
|--------|------------|
|
||||
| `ACTIONS_PAT` | (Опционально) PAT с правом **записи пакетов**. Если не задан, для `docker login` используется **`gitea.token`** (нужны права у workflow на пакеты). |
|
||||
|
||||
Вход в реестр в workflow:
|
||||
|
||||
- **registry:** `git.shts.su`;
|
||||
- **username:** `${{ gitea.actor }}`;
|
||||
- **password:** `${{ secrets.ACTIONS_PAT || gitea.token }}`.
|
||||
|
||||
### Почему шаг «Log in to Gitea Registry» мог быть skipped
|
||||
|
||||
Раньше в `if:` стояло **`secrets.ACTIONS_PAT != ''`**. В **Gitea act_runner** такая проверка секрета в условии шага часто **всегда ложна**, из‑за этого логин **всегда skipped**. Сейчас условие только `docker_push == 'true'` (всегда при нормальном шаге **Prepare**), пароль: **`secrets.ACTIONS_PAT || gitea.token`**.
|
||||
|
||||
Если после правки `docker login` падает с 401/403 — выдайте job-токену или PAT право **записи в Container Registry** (в настройках Gitea / организации / репозитория, в зависимости от версии).
|
||||
|
||||
Если **`gitea.token`** в выражениях недоступен, в [ci.yaml](workflows/ci.yaml) замените пароль на `${{ secrets.ACTIONS_PAT || github.token }}`.
|
||||
|
||||
### Имена образов и теги
|
||||
|
||||
Формат:
|
||||
### Теги образов
|
||||
|
||||
```text
|
||||
git.shts.su/<owner_lowercase>/<имя>:<тег>
|
||||
git.shts.su/<owner>/<имя>:latest
|
||||
git.shts.su/<owner>/<имя>:v1.2.3
|
||||
git.shts.su/<owner>/<имя>:1.2.3
|
||||
git.shts.su/<owner>/<имя>:<short-sha>
|
||||
git.shts.su/<owner>/<имя>:sha-<full-sha>
|
||||
```
|
||||
|
||||
где `<имя>` — одно из: `evobgp-api`, `evobgp-all`, `evobgp-scheduler`, `evobgp-ingest`, `evobgp-render`, `evobgp-deploy`, `evobgp-node`, `evobgp-web`, `evobgp-web-all` (UI с прокси на `evobgp-all` для профиля Compose `microvps-full`), `evobgp-agent`, `evobgp-bird2`.
|
||||
Имена образов: `evobgp-api`, `evobgp-all`, `evobgp-scheduler`, `evobgp-ingest`, `evobgp-render`, `evobgp-deploy`, `evobgp-node`, `evobgp-web`, `evobgp-web-all`, `evobgp-agent`, `evobgp-bird2`.
|
||||
|
||||
Теги: `latest`, короткий SHA (7 символов), `sha-<полный_commit_sha>`.
|
||||
|
||||
Пример pull:
|
||||
Пример:
|
||||
|
||||
```bash
|
||||
docker pull git.shts.su/myuser/evobgp-api:latest
|
||||
docker pull git.shts.su/myuser/evobgp-api:1.2.3
|
||||
```
|
||||
|
||||
Полная таблица образов, тегов, ссылок на страницы пакетов в Gitea и минимальный `docker run` для сервера без сборки — в [docs/quickstart.md](../docs/quickstart.md) (раздел «Готовые образы без сборки»).
|
||||
|
||||
Убедитесь, что в **Gitea** включены пакеты (Container Registry) и у токена есть права на создание/обновление образов. Другой хост реестра — правьте **`registry:`** и префикс **`IMG=`** в [ci.yaml](workflows/ci.yaml).
|
||||
|
||||
### Примечания
|
||||
|
||||
- Сборка с платформой `linux/amd64`. При необходимости добавьте `linux/arm64` и multi-arch в `docker buildx build`.
|
||||
- Для PR job **docker-images** не выполняется (только push в main/master), чтобы не публиковать образы из чужих форков без явной политики.
|
||||
См. [deploy/docker/README.md](../deploy/docker/README.md), [docs/quickstart.md](../docs/quickstart.md).
|
||||
+58
-91
@@ -208,88 +208,99 @@ jobs:
|
||||
done
|
||||
|
||||
# ---------------------------------------------------------------------------
|
||||
# Docker: все Go-образы одним buildx bake (один go mod download, одна компиляция cmd/*,
|
||||
# birdc собирается один раз для api/all). Registry cache: evobgp-buildcache:go-buildcache.
|
||||
# ---------------------------------------------------------------------------
|
||||
docker-go:
|
||||
needs: [changes, go]
|
||||
if: >-
|
||||
always() &&
|
||||
needs.changes.result == 'success' &&
|
||||
needs.go.result != 'failure' &&
|
||||
github.event_name == 'push' &&
|
||||
(github.ref == 'refs/heads/main' || github.ref == 'refs/heads/master') &&
|
||||
(needs.changes.outputs.go == 'true' || needs.changes.outputs.docker_go == 'true')
|
||||
commitlint:
|
||||
if: github.event_name == 'pull_request'
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- name: Set up Docker Buildx
|
||||
uses: docker/setup-buildx-action@v3
|
||||
- name: Prepare image metadata
|
||||
id: meta
|
||||
run: |
|
||||
set -euo pipefail
|
||||
owner_lc="$(echo '${{ github.repository_owner }}' | tr '[:upper:]' '[:lower:]')"
|
||||
echo "owner_lc=$owner_lc" >> "$GITHUB_OUTPUT"
|
||||
short_sha="$(echo '${{ github.sha }}' | cut -c1-7)"
|
||||
echo "short_sha=$short_sha" >> "$GITHUB_OUTPUT"
|
||||
- name: Log in to Gitea Registry
|
||||
uses: docker/login-action@v3
|
||||
with:
|
||||
registry: git.shts.su
|
||||
username: ${{ gitea.actor }}
|
||||
password: ${{ secrets.ACTIONS_PAT || gitea.token }}
|
||||
- name: Build and push Go images (bake)
|
||||
env:
|
||||
REGISTRY: git.shts.su/${{ steps.meta.outputs.owner_lc }}
|
||||
IMAGE_TAG: latest
|
||||
SHORT_SHA: ${{ steps.meta.outputs.short_sha }}
|
||||
SHA_FULL: ${{ github.sha }}
|
||||
CACHE_REF_GO: git.shts.su/${{ steps.meta.outputs.owner_lc }}/evobgp-buildcache:go-buildcache
|
||||
CACHE_REF_WEB: ""
|
||||
BUILDX_BAKE_ENTITLEMENTS_FS: "0"
|
||||
fetch-depth: 0
|
||||
- uses: actions/setup-node@v4
|
||||
with:
|
||||
node-version: "22"
|
||||
cache: npm
|
||||
cache-dependency-path: package-lock.json
|
||||
- name: Lint commit messages
|
||||
run: |
|
||||
set -euxo pipefail
|
||||
cd "${{ github.workspace }}/deploy/docker"
|
||||
sh write-bake-override.sh
|
||||
docker buildx bake --allow=fs.read="${{ github.workspace }}" \
|
||||
-f docker-bake.hcl -f docker-bake.override.hcl go-images --push
|
||||
npm ci
|
||||
npx commitlint --from "${{ github.event.pull_request.base.sha }}" --to "${{ github.event.pull_request.head.sha }}"
|
||||
|
||||
# ---------------------------------------------------------------------------
|
||||
# Docker: Web (evobgp-web, evobgp-web-all) — один npm ci (кэш) + два nginx-тега.
|
||||
# Один push в main: semantic-release (тег на текущий commit, без доп. commit) + docker push.
|
||||
# ---------------------------------------------------------------------------
|
||||
docker-web:
|
||||
needs: [changes]
|
||||
release:
|
||||
needs: [changes, openapi, web, go, bird2]
|
||||
if: >-
|
||||
always() &&
|
||||
github.event_name == 'push' &&
|
||||
(github.ref == 'refs/heads/main' || github.ref == 'refs/heads/master') &&
|
||||
(needs.changes.outputs.web == 'true' || needs.changes.outputs.docker_web == 'true')
|
||||
needs.changes.result == 'success' &&
|
||||
(needs.openapi.result == 'success' || needs.openapi.result == 'skipped') &&
|
||||
(needs.web.result == 'success' || needs.web.result == 'skipped') &&
|
||||
(needs.go.result == 'success' || needs.go.result == 'skipped') &&
|
||||
(needs.bird2.result == 'success' || needs.bird2.result == 'skipped')
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
with:
|
||||
fetch-depth: 0
|
||||
token: ${{ secrets.ACTIONS_PAT || gitea.token }}
|
||||
persist-credentials: true
|
||||
- uses: actions/setup-node@v4
|
||||
with:
|
||||
node-version: "22"
|
||||
cache: npm
|
||||
cache-dependency-path: package-lock.json
|
||||
- name: Install release tooling
|
||||
run: npm ci
|
||||
- name: Semantic release
|
||||
run: npx semantic-release
|
||||
env:
|
||||
GITEA_URL: https://git.shts.su
|
||||
GITEA_TOKEN: ${{ secrets.ACTIONS_PAT || gitea.token }}
|
||||
- name: Detect new release
|
||||
id: rel
|
||||
run: |
|
||||
set -euo pipefail
|
||||
if [ -f .release-version ]; then
|
||||
echo "version=$(tr -d '[:space:]' < .release-version)" >> "$GITHUB_OUTPUT"
|
||||
echo "released=true" >> "$GITHUB_OUTPUT"
|
||||
else
|
||||
echo "released=false" >> "$GITHUB_OUTPUT"
|
||||
echo "No releasable commits — skipping image publish"
|
||||
fi
|
||||
- name: Set up Docker Buildx
|
||||
if: steps.rel.outputs.released == 'true'
|
||||
uses: docker/setup-buildx-action@v3
|
||||
- name: Prepare image metadata
|
||||
if: steps.rel.outputs.released == 'true'
|
||||
id: meta
|
||||
run: |
|
||||
set -euo pipefail
|
||||
echo "version=${{ steps.rel.outputs.version }}" >> "$GITHUB_OUTPUT"
|
||||
owner_lc="$(echo '${{ github.repository_owner }}' | tr '[:upper:]' '[:lower:]')"
|
||||
echo "owner_lc=$owner_lc" >> "$GITHUB_OUTPUT"
|
||||
short_sha="$(echo '${{ github.sha }}' | cut -c1-7)"
|
||||
echo "short_sha=$short_sha" >> "$GITHUB_OUTPUT"
|
||||
echo "build_time=$(date -u +%Y-%m-%dT%H:%M:%SZ)" >> "$GITHUB_OUTPUT"
|
||||
- name: Log in to Gitea Registry
|
||||
if: steps.rel.outputs.released == 'true'
|
||||
uses: docker/login-action@v3
|
||||
with:
|
||||
registry: git.shts.su
|
||||
username: ${{ gitea.actor }}
|
||||
password: ${{ secrets.ACTIONS_PAT || gitea.token }}
|
||||
- name: Build and push Web images (bake)
|
||||
- name: Build and push images (bake)
|
||||
if: steps.rel.outputs.released == 'true'
|
||||
env:
|
||||
REGISTRY: git.shts.su/${{ steps.meta.outputs.owner_lc }}
|
||||
IMAGE_TAG: latest
|
||||
VERSION: ${{ steps.meta.outputs.version }}
|
||||
SHORT_SHA: ${{ steps.meta.outputs.short_sha }}
|
||||
SHA_FULL: ${{ github.sha }}
|
||||
CACHE_REF_GO: ""
|
||||
BUILD_TIME: ${{ steps.meta.outputs.build_time }}
|
||||
CACHE_REF_GO: git.shts.su/${{ steps.meta.outputs.owner_lc }}/evobgp-buildcache:go-buildcache
|
||||
CACHE_REF_WEB: git.shts.su/${{ steps.meta.outputs.owner_lc }}/evobgp-buildcache:web-buildcache
|
||||
BUILDX_BAKE_ENTITLEMENTS_FS: "0"
|
||||
run: |
|
||||
@@ -297,49 +308,5 @@ jobs:
|
||||
cd "${{ github.workspace }}/deploy/docker"
|
||||
sh write-bake-override.sh
|
||||
docker buildx bake --allow=fs.read="${{ github.workspace }}" \
|
||||
-f docker-bake.hcl -f docker-bake.override.hcl web-images --push
|
||||
-f docker-bake.hcl -f docker-bake.override.hcl default --push
|
||||
|
||||
# ---------------------------------------------------------------------------
|
||||
# Docker: BIRD2 (evobgp-bird2).
|
||||
# Собирается только при изменении deploy/bird/ или Dockerfile bird2.
|
||||
# ---------------------------------------------------------------------------
|
||||
docker-bird:
|
||||
needs: [changes]
|
||||
if: >-
|
||||
github.event_name == 'push' &&
|
||||
(github.ref == 'refs/heads/main' || github.ref == 'refs/heads/master') &&
|
||||
(needs.changes.outputs.bird_conf == 'true' || needs.changes.outputs.docker_bird == 'true')
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- name: Set up Docker Buildx
|
||||
uses: docker/setup-buildx-action@v3
|
||||
- name: Prepare image metadata
|
||||
id: meta
|
||||
run: |
|
||||
set -euo pipefail
|
||||
owner_lc="$(echo '${{ github.repository_owner }}' | tr '[:upper:]' '[:lower:]')"
|
||||
echo "owner_lc=$owner_lc" >> "$GITHUB_OUTPUT"
|
||||
short_sha="$(echo '${{ github.sha }}' | cut -c1-7)"
|
||||
echo "short_sha=$short_sha" >> "$GITHUB_OUTPUT"
|
||||
- name: Log in to Gitea Registry
|
||||
uses: docker/login-action@v3
|
||||
with:
|
||||
registry: git.shts.su
|
||||
username: ${{ gitea.actor }}
|
||||
password: ${{ secrets.ACTIONS_PAT || gitea.token }}
|
||||
- name: Build and push evobgp-bird2 (bake)
|
||||
env:
|
||||
REGISTRY: git.shts.su/${{ steps.meta.outputs.owner_lc }}
|
||||
IMAGE_TAG: latest
|
||||
SHORT_SHA: ${{ steps.meta.outputs.short_sha }}
|
||||
SHA_FULL: ${{ github.sha }}
|
||||
CACHE_REF_GO: ""
|
||||
CACHE_REF_WEB: ""
|
||||
BUILDX_BAKE_ENTITLEMENTS_FS: "0"
|
||||
run: |
|
||||
set -euxo pipefail
|
||||
cd "${{ github.workspace }}/deploy/docker"
|
||||
sh write-bake-override.sh
|
||||
docker buildx bake --allow=fs.read="${{ github.workspace }}" \
|
||||
-f docker-bake.hcl -f docker-bake.override.hcl evobgp-bird2 --push
|
||||
+18
@@ -0,0 +1,18 @@
|
||||
# Root npm (semantic-release, commitlint) — npm ci in CI, never commit deps
|
||||
node_modules/
|
||||
|
||||
# Generated by deploy/docker/write-bake-override.sh (CI/local bake)
|
||||
deploy/docker/docker-bake.override.hcl
|
||||
|
||||
# CI-only markers (not committed)
|
||||
.release-version
|
||||
CHANGELOG.md
|
||||
|
||||
# OS
|
||||
.DS_Store
|
||||
Thumbs.db
|
||||
|
||||
# Env / secrets (local only)
|
||||
.env
|
||||
.env.*
|
||||
!.env.example
|
||||
@@ -0,0 +1,48 @@
|
||||
{
|
||||
"branches": ["main", "master"],
|
||||
"tagFormat": "v${version}",
|
||||
"plugins": [
|
||||
[
|
||||
"@semantic-release/commit-analyzer",
|
||||
{
|
||||
"preset": "conventionalcommits",
|
||||
"releaseRules": [
|
||||
{ "type": "feat", "release": "minor" },
|
||||
{ "type": "fix", "release": "patch" },
|
||||
{ "type": "perf", "release": "patch" },
|
||||
{ "breaking": true, "release": "major" }
|
||||
]
|
||||
}
|
||||
],
|
||||
[
|
||||
"@semantic-release/release-notes-generator",
|
||||
{
|
||||
"preset": "conventionalcommits"
|
||||
}
|
||||
],
|
||||
[
|
||||
"@semantic-release/changelog",
|
||||
{
|
||||
"changelogFile": "CHANGELOG.md"
|
||||
}
|
||||
],
|
||||
[
|
||||
"@semantic-release/exec",
|
||||
{
|
||||
"successCmd": "echo ${nextRelease.version} > .release-version"
|
||||
}
|
||||
],
|
||||
[
|
||||
"@markwylde/semantic-release-gitea",
|
||||
{
|
||||
"giteaUrl": "https://git.shts.su",
|
||||
"assets": [
|
||||
{
|
||||
"path": "CHANGELOG.md",
|
||||
"label": "Changelog"
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
]
|
||||
}
|
||||
@@ -36,6 +36,15 @@
|
||||
- **Повторное использование:** если [docs/architecture.md](docs/architecture.md) уже описывает поток — не пересказывайте его длинно; укажите документ и конкретный подпункт задачи.
|
||||
- **Длинные планы:** `.cursor/plans/*.plan.md` — для истории решений; для навигации пользователю достаточно `docs/`; не читайте план целиком без причины.
|
||||
|
||||
## Коммиты (Conventional Commits)
|
||||
|
||||
Если пользователь просит **коммит**, **commit message**, **закоммить**, **git commit** или это следует из плана — **сразу**:
|
||||
|
||||
1. Shell: `powershell -NoProfile -File scripts/commit/staged-context.ps1` (первый вызов, до текста коммита).
|
||||
2. Скилл [.cursor/skills/commit-message/SKILL.md](.cursor/skills/commit-message/SKILL.md) и правило [.cursor/rules/conventional-commits.mdc](.cursor/rules/conventional-commits.mdc).
|
||||
|
||||
Без вывода скрипта (exit 0) **не** придумывать сообщение коммита. Заголовок — EN, тело — RU; несвязанные области — auto-split (скилл).
|
||||
|
||||
## Команды и среда
|
||||
|
||||
- Консоль пользователя: **PowerShell**; пути в стиле `deploy\compose`.
|
||||
|
||||
+150
@@ -0,0 +1,150 @@
|
||||
## 1.0.0 (2026-05-19)
|
||||
|
||||
* feat: add @tanstack/table-core dependency and integrate module name mapping in operations page. Upda ([b140dcb](http://192.168.100.56:3000/denozord/EvoBGP/commit/b140dcb))
|
||||
* feat: add @types/node and undici-types to devDependencies in package.json and package-lock.json for ([d92a06c](http://192.168.100.56:3000/denozord/EvoBGP/commit/d92a06c))
|
||||
* feat: add aggregated router-lists catalog endpoint and update module listing filters ([c811c43](http://192.168.100.56:3000/denozord/EvoBGP/commit/c811c43))
|
||||
* feat: add auto-updater service for Docker stack with configurable settings ([ae9fbe3](http://192.168.100.56:3000/denozord/EvoBGP/commit/ae9fbe3))
|
||||
* feat: add bird configuration preview functionality with tabbed interface for fragments and prefixes. ([1969a00](http://192.168.100.56:3000/denozord/EvoBGP/commit/1969a00))
|
||||
* feat: add BIRD status endpoint and integrate birdc checks into job processing. Enhance UI to display ([94a4c6a](http://192.168.100.56:3000/denozord/EvoBGP/commit/94a4c6a))
|
||||
* feat: add CSV import and export functionality for module entries. Implement endpoints for exporting ([bae2d68](http://192.168.100.56:3000/denozord/EvoBGP/commit/bae2d68))
|
||||
* feat: add debug logging and optimize database queries in Postgres repository ([6d0e214](http://192.168.100.56:3000/denozord/EvoBGP/commit/6d0e214))
|
||||
* feat: add diagnostic log endpoint for revisions and implement download functionality ([2260ecf](http://192.168.100.56:3000/denozord/EvoBGP/commit/2260ecf))
|
||||
* feat: add enabled state to BGP peer types and UI. Implement peer enabling toggle in the network page ([80965ad](http://192.168.100.56:3000/denozord/EvoBGP/commit/80965ad))
|
||||
* feat: add last refreshed timestamp to modules and CDN sources ([1e13ef9](http://192.168.100.56:3000/denozord/EvoBGP/commit/1e13ef9))
|
||||
* feat: add live BGP protocol state tracking to peer listing ([8a50ba4](http://192.168.100.56:3000/denozord/EvoBGP/commit/8a50ba4))
|
||||
* feat: add optional name field to BGP peer creation and update UI ([1a61ae2](http://192.168.100.56:3000/denozord/EvoBGP/commit/1a61ae2))
|
||||
* feat: add optional name field to DoH profile schema and types ([6bee5ad](http://192.168.100.56:3000/denozord/EvoBGP/commit/6bee5ad))
|
||||
* feat: add smart aggregation for IPv4 CIDR prefixes in refresh pipeline. Implement functionality to a ([0428b83](http://192.168.100.56:3000/denozord/EvoBGP/commit/0428b83))
|
||||
* feat: add strict bind configuration to BGP templates. Enhance BGP template rendering by including 's ([07f2ce5](http://192.168.100.56:3000/denozord/EvoBGP/commit/07f2ce5))
|
||||
* feat: add tenant refresh functionality to HTTP API and job processing ([7a2b015](http://192.168.100.56:3000/denozord/EvoBGP/commit/7a2b015))
|
||||
* feat: auto-refresh non-IP module updates and add full project manual ([1e77e1a](http://192.168.100.56:3000/denozord/EvoBGP/commit/1e77e1a))
|
||||
* feat: enhance API client with pagination and new data fetching functions. Implement `apiPageAll` for ([9f0b128](http://192.168.100.56:3000/denozord/EvoBGP/commit/9f0b128))
|
||||
* feat: enhance BGP template and peer configuration handling. Introduce policies_json parameter for lo ([be8e7b5](http://192.168.100.56:3000/denozord/EvoBGP/commit/be8e7b5))
|
||||
* feat: enhance CDN source management with last refreshed timestamp ([4776434](http://192.168.100.56:3000/denozord/EvoBGP/commit/4776434))
|
||||
* feat: enhance community attribute handling in BIRD format. Add validation for community route bodies ([b541794](http://192.168.100.56:3000/denozord/EvoBGP/commit/b541794))
|
||||
* feat: enhance community label handling and improve UI localization across operations pages. Introduc ([53fd527](http://192.168.100.56:3000/denozord/EvoBGP/commit/53fd527))
|
||||
* feat: enhance deployment configuration for secure Web UI with Traefik integration ([4acd685](http://192.168.100.56:3000/denozord/EvoBGP/commit/4acd685))
|
||||
* feat: enhance DoH profile management and resolver policy in modules ([34ecc5c](http://192.168.100.56:3000/denozord/EvoBGP/commit/34ecc5c))
|
||||
* feat: enhance EvoBGP with new command-line options for the evobgp-agent, including a watch command f ([6a55f72](http://192.168.100.56:3000/denozord/EvoBGP/commit/6a55f72))
|
||||
* feat: enhance evobgp with new command-line tools for bundle management, including pull, verify, and ([bf52b21](http://192.168.100.56:3000/denozord/EvoBGP/commit/bf52b21))
|
||||
* feat: enhance job detail handling in UI with caching and loading states. Introduce SvelteMap for job ([05bc5c4](http://192.168.100.56:3000/denozord/EvoBGP/commit/05bc5c4))
|
||||
* feat: enhance job detail UI with improved overflow handling and layout adjustments. Update card comp ([67ce135](http://192.168.100.56:3000/denozord/EvoBGP/commit/67ce135))
|
||||
* feat: enhance job filtering and display functionality in operations page. Introduce job search and f ([e1c4cbc](http://192.168.100.56:3000/denozord/EvoBGP/commit/e1c4cbc))
|
||||
* feat: enhance job processing with detailed logging and UI improvements. Introduce revision log entri ([c48cc4e](http://192.168.100.56:3000/denozord/EvoBGP/commit/c48cc4e))
|
||||
* feat: enhance monitoring page with detailed status tracking and error handling ([458a885](http://192.168.100.56:3000/denozord/EvoBGP/commit/458a885))
|
||||
* feat: enhance observability with prefix aggregation metrics ([07b97bd](http://192.168.100.56:3000/denozord/EvoBGP/commit/07b97bd))
|
||||
* feat: enhance peer neighbor handling by introducing NormalizePeerNeighborString function. Update Cre ([80f3a8d](http://192.168.100.56:3000/denozord/EvoBGP/commit/80f3a8d))
|
||||
* feat: enhance RefreshModule to aggregate prefixes from all enabled tenant modules. Update documentat ([431c6a9](http://192.168.100.56:3000/denozord/EvoBGP/commit/431c6a9))
|
||||
* feat: enhance RefreshModule to support legacy ASN resolution and improve prefix handling. Introduce ([e1aaa18](http://192.168.100.56:3000/denozord/EvoBGP/commit/e1aaa18))
|
||||
* feat: enhance settings page layout and section management ([d91667f](http://192.168.100.56:3000/denozord/EvoBGP/commit/d91667f))
|
||||
* feat: enhance UI components with improved styling and layout adjustments. Update button sizes, text ([6a72842](http://192.168.100.56:3000/denozord/EvoBGP/commit/6a72842))
|
||||
* feat: enhance worker functionality by adding job queuing for deploy_apply. Introduce a Registry fiel ([42d956d](http://192.168.100.56:3000/denozord/EvoBGP/commit/42d956d))
|
||||
* feat: implement AS holder name resolution and enhance ASEntry structure. Add ASHolderName function t ([c958d5a](http://192.168.100.56:3000/denozord/EvoBGP/commit/c958d5a))
|
||||
* feat: implement bulk selection and deletion for modules and entries. Add functionality for selecting ([e816d17](http://192.168.100.56:3000/denozord/EvoBGP/commit/e816d17))
|
||||
* feat: implement CDN source preview functionality and enhance data handling. Add a new endpoint for p ([e20c9f3](http://192.168.100.56:3000/denozord/EvoBGP/commit/e20c9f3))
|
||||
* feat: implement countAll function for API data retrieval ([40c8a8d](http://192.168.100.56:3000/denozord/EvoBGP/commit/40c8a8d))
|
||||
* feat: implement domain resolution via DoH in refresh pipeline. Enhance the collectModulePrefixRows f ([678bd27](http://192.168.100.56:3000/denozord/EvoBGP/commit/678bd27))
|
||||
* feat: implement effective peer enabling logic during peer creation. Update CreatePeer methods in Pos ([3e85431](http://192.168.100.56:3000/denozord/EvoBGP/commit/3e85431))
|
||||
* feat: implement module refresh job management with concurrency control. Add CountOtherActiveModuleRe ([149fb0a](http://192.168.100.56:3000/denozord/EvoBGP/commit/149fb0a))
|
||||
* feat: implement peer reconciliation job for BGP peers ([3b17228](http://192.168.100.56:3000/denozord/EvoBGP/commit/3b17228))
|
||||
* feat: implement revision retention settings and pruning logic ([4f50350](http://192.168.100.56:3000/denozord/EvoBGP/commit/4f50350))
|
||||
* feat: implement theme selection and automatic theme application based on user preference. Update App ([b70a643](http://192.168.100.56:3000/denozord/EvoBGP/commit/b70a643))
|
||||
* feat: implement versioning and release management in the project ([bb334b1](http://192.168.100.56:3000/denozord/EvoBGP/commit/bb334b1))
|
||||
* feat: improve BGP protocol summary handling by introducing isBGPProtocolSummaryRow function. Update ([399622c](http://192.168.100.56:3000/denozord/EvoBGP/commit/399622c))
|
||||
* feat: improve job detail UI with enhanced spacing and layout adjustments. Update table cell classes ([97908b5](http://192.168.100.56:3000/denozord/EvoBGP/commit/97908b5))
|
||||
* feat: improve peer speaker assignment UX in network settings ([f877ec8](http://192.168.100.56:3000/denozord/EvoBGP/commit/f877ec8))
|
||||
* feat: improve UI components with enhanced overflow handling and layout adjustments. Update card and ([0ca5542](http://192.168.100.56:3000/denozord/EvoBGP/commit/0ca5542))
|
||||
* feat: improve UI layout and responsiveness in module and operations pages. Refactor component struct ([233f29b](http://192.168.100.56:3000/denozord/EvoBGP/commit/233f29b))
|
||||
* feat: redesign settings page with structured controls ([2da6d49](http://192.168.100.56:3000/denozord/EvoBGP/commit/2da6d49))
|
||||
* feat: refactor materialization logic to group static routes by community in the refresh pipeline. In ([1c5a9c9](http://192.168.100.56:3000/denozord/EvoBGP/commit/1c5a9c9))
|
||||
* feat: refactor operations page to enhance UI and component structure. Remove unused card components ([88226f6](http://192.168.100.56:3000/denozord/EvoBGP/commit/88226f6))
|
||||
* feat: refine BGP template handling and documentation updates. Remove local IP requirements in BGP te ([6266a39](http://192.168.100.56:3000/denozord/EvoBGP/commit/6266a39))
|
||||
* feat: refine job detail UI with improved layout and scrolling capabilities. Enhance spacing and over ([57c09ac](http://192.168.100.56:3000/denozord/EvoBGP/commit/57c09ac))
|
||||
* feat: update BGP community structure and related documentation. Refactor BGP community handling by r ([a2cbfac](http://192.168.100.56:3000/denozord/EvoBGP/commit/a2cbfac))
|
||||
* feat: update BGP template configuration and OpenAPI documentation. Change import settings from 'all' ([133ea93](http://192.168.100.56:3000/denozord/EvoBGP/commit/133ea93))
|
||||
* feat: update BIRD installation process to build from source. Modify CI workflow to ensure BIRD 2.14 ([e2002ba](http://192.168.100.56:3000/denozord/EvoBGP/commit/e2002ba))
|
||||
* feat: update CI configuration and Dockerfiles for improved build efficiency ([80229b3](http://192.168.100.56:3000/denozord/EvoBGP/commit/80229b3))
|
||||
* feat: update collectModulePrefixRows to support prior snapshots and enhance CDN/domain prefix collec ([ee364c8](http://192.168.100.56:3000/denozord/EvoBGP/commit/ee364c8))
|
||||
* feat: update Go module dependencies and enhance DNS resolution in refresh pipeline. Upgrade Go versi ([655d750](http://192.168.100.56:3000/denozord/EvoBGP/commit/655d750))
|
||||
* feat: update OpenAPI documentation and internal logic for BIRD settings. Enhance the description of ([62bf987](http://192.168.100.56:3000/denozord/EvoBGP/commit/62bf987))
|
||||
* feat: update RevisionDiff type and enhance OperationsDiffTab component for better diff handling. Int ([4aaaa30](http://192.168.100.56:3000/denozord/EvoBGP/commit/4aaaa30))
|
||||
* feat: update Traefik deployment configuration for Let's Encrypt and logging ([39ba976](http://192.168.100.56:3000/denozord/EvoBGP/commit/39ba976))
|
||||
* docs: add router-lists-ui integration documentation to README ([b7a8aab](http://192.168.100.56:3000/denozord/EvoBGP/commit/b7a8aab))
|
||||
* docs: add Web UI section to EvoBGP architecture plan, detailing Svelte and shadcn-svelte integration ([272542b](http://192.168.100.56:3000/denozord/EvoBGP/commit/272542b))
|
||||
* docs: enhance EvoBGP architecture plan with new risk mitigation strategies, testing matrix for BIRD2 ([3992d01](http://192.168.100.56:3000/denozord/EvoBGP/commit/3992d01))
|
||||
* docs: enhance README and CI workflow documentation for Docker image building and publishing process, ([9eb3e33](http://192.168.100.56:3000/denozord/EvoBGP/commit/9eb3e33))
|
||||
* docs: enhance README and CI workflow to clarify Docker image build conditions and Gitea Container Re ([c6687b2](http://192.168.100.56:3000/denozord/EvoBGP/commit/c6687b2))
|
||||
* docs: openapi.html со встроенным Redoc; инструкция для Gitea и скрипт сборки ([a754e1d](http://192.168.100.56:3000/denozord/EvoBGP/commit/a754e1d))
|
||||
* docs: update AGENTS and README to include guidelines for Conventional Commits ([4c23232](http://192.168.100.56:3000/denozord/EvoBGP/commit/4c23232))
|
||||
* docs: update AGENTS and README with engineering rules and guidelines ([aff27e8](http://192.168.100.56:3000/denozord/EvoBGP/commit/aff27e8))
|
||||
* docs: update architecture and API documentation to clarify job processing, in-memory registry usage, ([acc0608](http://192.168.100.56:3000/denozord/EvoBGP/commit/acc0608))
|
||||
* docs: update EvoBGP architecture plan with detailed REST API section, including general agreements, ([1a86d6c](http://192.168.100.56:3000/denozord/EvoBGP/commit/1a86d6c))
|
||||
* docs: update EvoBGP architecture plan with new repository structure, refined sections, and detailed ([4480d64](http://192.168.100.56:3000/denozord/EvoBGP/commit/4480d64))
|
||||
* docs: update README and CI workflow to clarify Docker workspace mounting for bird2 job ([6be46db](http://192.168.100.56:3000/denozord/EvoBGP/commit/6be46db))
|
||||
* docs: update README and CI workflow to clarify Gitea Registry login process and improve Docker image ([f1b33b9](http://192.168.100.56:3000/denozord/EvoBGP/commit/f1b33b9))
|
||||
* docs: update README and Docker Compose configurations to clarify usage of pre-built images from the ([7fac79c](http://192.168.100.56:3000/denozord/EvoBGP/commit/7fac79c))
|
||||
* docs: update README to include information about the EvoBGP web interface, linking to quickstart and ([5d21f01](http://192.168.100.56:3000/denozord/EvoBGP/commit/5d21f01))
|
||||
* docs: статическая Redoc-страница OpenAPI (для просмотра в Gitea без встроенного рендера) ([5c9d2e2](http://192.168.100.56:3000/denozord/EvoBGP/commit/5c9d2e2))
|
||||
* docs: убрать типографское тире и невидимый Unicode из текстов API ([fbe9cd3](http://192.168.100.56:3000/denozord/EvoBGP/commit/fbe9cd3))
|
||||
* refactor: adjust BootstrapWorkers function call in server.go to change the order of returned values, ([032fa99](http://192.168.100.56:3000/denozord/EvoBGP/commit/032fa99))
|
||||
* refactor: consolidate module interval display logic and improve edit form handling ([1f85654](http://192.168.100.56:3000/denozord/EvoBGP/commit/1f85654))
|
||||
* refactor: enhance bird metrics polling with context support ([f6b94a4](http://192.168.100.56:3000/denozord/EvoBGP/commit/f6b94a4))
|
||||
* refactor: enhance CI workflow for granular change detection by module and improve idempotency key ge ([54eaad3](http://192.168.100.56:3000/denozord/EvoBGP/commit/54eaad3))
|
||||
* refactor: enhance job status checks in worker tests ([6107c3e](http://192.168.100.56:3000/denozord/EvoBGP/commit/6107c3e))
|
||||
* refactor: enhance JSON struct tags for various data models in backend and memory store. Update field ([4a860a0](http://192.168.100.56:3000/denozord/EvoBGP/commit/4a860a0))
|
||||
* refactor: enhance module display and input handling in module pages ([25374a6](http://192.168.100.56:3000/denozord/EvoBGP/commit/25374a6))
|
||||
* refactor: enhance module interval label logic and add reset buttons in edit forms ([b99fc17](http://192.168.100.56:3000/denozord/EvoBGP/commit/b99fc17))
|
||||
* refactor: improve code consistency and formatting across components ([c263fd5](http://192.168.100.56:3000/denozord/EvoBGP/commit/c263fd5))
|
||||
* refactor: improve handling of deployable fragments in ApplyRevision and buildPreviewFragments functi ([6932271](http://192.168.100.56:3000/denozord/EvoBGP/commit/6932271))
|
||||
* refactor: improve module save functionality and nullable value handling ([05ec176](http://192.168.100.56:3000/denozord/EvoBGP/commit/05ec176))
|
||||
* refactor: improve PATCH handling for module updates with nullable field support ([e700f90](http://192.168.100.56:3000/denozord/EvoBGP/commit/e700f90))
|
||||
* refactor: optimize job listing and counting logic for concurrency safety. Separate tenant filtering ([d83efe2](http://192.168.100.56:3000/denozord/EvoBGP/commit/d83efe2))
|
||||
* refactor: phase 2 structural alignment — reports, importer, nodecli, pagination ([250cdea](http://192.168.100.56:3000/denozord/EvoBGP/commit/250cdea))
|
||||
* refactor: remove strict bind configuration from BGP templates and update related logic. Eliminate 's ([482eeb1](http://192.168.100.56:3000/denozord/EvoBGP/commit/482eeb1))
|
||||
* refactor: streamline Docker build process in CI configuration and documentation ([6025f38](http://192.168.100.56:3000/denozord/EvoBGP/commit/6025f38))
|
||||
* refactor: streamline edit form handling for community and DoH profile selections ([1765ab8](http://192.168.100.56:3000/denozord/EvoBGP/commit/1765ab8))
|
||||
* refactor: streamline module refresh process and enhance revision handling ([10e2415](http://192.168.100.56:3000/denozord/EvoBGP/commit/10e2415))
|
||||
* refactor: switch BIRD installation from source to Ubuntu package. Update CI workflow and Dockerfiles ([0b3ceaa](http://192.168.100.56:3000/denozord/EvoBGP/commit/0b3ceaa))
|
||||
* refactor: update AS entry handling in API and database to support ASN-only entries. Remove prefix fi ([1db6b5b](http://192.168.100.56:3000/denozord/EvoBGP/commit/1db6b5b))
|
||||
* refactor: update Docker build context and paths in CI configuration and documentation ([7d52a4a](http://192.168.100.56:3000/denozord/EvoBGP/commit/7d52a4a))
|
||||
* refactor: update Docker build variable syntax in CI configuration and documentation ([27e01d0](http://192.168.100.56:3000/denozord/EvoBGP/commit/27e01d0))
|
||||
* refactor: update job kind display logic in schedule page ([dc1f4a5](http://192.168.100.56:3000/denozord/EvoBGP/commit/dc1f4a5))
|
||||
* refactor: update ListModules and GetModule methods in Postgres repository to include cron expression ([73d0003](http://192.168.100.56:3000/denozord/EvoBGP/commit/73d0003))
|
||||
* refactor: update Switch component styles and structure for improved accessibility and responsiveness ([d4282c2](http://192.168.100.56:3000/denozord/EvoBGP/commit/d4282c2))
|
||||
* refactor: update worker processes in EvoBGP to accept dependencies for shared store and job registry ([b7968db](http://192.168.100.56:3000/denozord/EvoBGP/commit/b7968db))
|
||||
* ci: add conditional job for OpenAPI linting based on file changes; update README with workflow detai ([3adfc49](http://192.168.100.56:3000/denozord/EvoBGP/commit/3adfc49))
|
||||
* ci: phase 4 automation — web job, lint-httpapi, golangci ([64516c3](http://192.168.100.56:3000/denozord/EvoBGP/commit/64516c3))
|
||||
* ci: update README and CI workflow to improve bird2 job installation process and clarify Docker usage ([eddc6b8](http://192.168.100.56:3000/denozord/EvoBGP/commit/eddc6b8))
|
||||
* chore: phase 3 code quality — pipeline prefixes, godoc baseline ([ea65451](http://192.168.100.56:3000/denozord/EvoBGP/commit/ea65451))
|
||||
* chore: update CI configuration to disable filesystem entitlement for Docker builds ([9ab7de6](http://192.168.100.56:3000/denozord/EvoBGP/commit/9ab7de6))
|
||||
* chore: update package dependencies and enhance AppShell component with new navigation items and tool ([7d85fef](http://192.168.100.56:3000/denozord/EvoBGP/commit/7d85fef))
|
||||
* chore: upgrade Go version to 1.24 in Dockerfiles for evobgp-agent and gobinary. This change ensures ([4ba5b03](http://192.168.100.56:3000/denozord/EvoBGP/commit/4ba5b03))
|
||||
* fix: adjust Postgres resource limits and healthcheck parameters. Increase memory limit to 256M and m ([20e674c](http://192.168.100.56:3000/denozord/EvoBGP/commit/20e674c))
|
||||
* fix: improve input handling and validation for settings fields ([1386b63](http://192.168.100.56:3000/denozord/EvoBGP/commit/1386b63))
|
||||
* fix: improve sidebar and navigation layout in AppShell component ([9f02128](http://192.168.100.56:3000/denozord/EvoBGP/commit/9f02128))
|
||||
* fix: update BGP configuration in test scenarios to use new IPv6 addresses ([9fd9745](http://192.168.100.56:3000/denozord/EvoBGP/commit/9fd9745))
|
||||
* fix: update bird build script and Dockerfile to include libncurses dependency ([0cb5a7d](http://192.168.100.56:3000/denozord/EvoBGP/commit/0cb5a7d))
|
||||
* fix: update bird build script and Dockerfile to include readline dependency ([35ed79b](http://192.168.100.56:3000/denozord/EvoBGP/commit/35ed79b))
|
||||
* fix: update bird build script and Dockerfile to remove readline dependency ([fefadac](http://192.168.100.56:3000/denozord/EvoBGP/commit/fefadac))
|
||||
* fix: update deployment configuration for Traefik and secure Web UI ([00bd037](http://192.168.100.56:3000/denozord/EvoBGP/commit/00bd037))
|
||||
* fix: update Docker build commands in CI configuration and documentation ([b5061a5](http://192.168.100.56:3000/denozord/EvoBGP/commit/b5061a5))
|
||||
* fix(httpapi): phase 1 compliance — ERR-01, SEC-04, CDN client, scheduler docs ([1dd7135](http://192.168.100.56:3000/denozord/EvoBGP/commit/1dd7135))
|
||||
* test: add CDN prefetch tests for 304 responses and snapshot handling ([a536a2d](http://192.168.100.56:3000/denozord/EvoBGP/commit/a536a2d))
|
||||
* test: add validation for 'strict bind on' in BGP rendering tests. Enhance BGP test cases to ensure ' ([dc5e20e](http://192.168.100.56:3000/denozord/EvoBGP/commit/dc5e20e))
|
||||
* docker: refactor cache handling in Docker bake configuration ([5e59a1c](http://192.168.100.56:3000/denozord/EvoBGP/commit/5e59a1c))
|
||||
* docker: simplify web image targets in Docker bake configuration ([765a805](http://192.168.100.56:3000/denozord/EvoBGP/commit/765a805))
|
||||
* docker: update web image build process and README ([92f6de8](http://192.168.100.56:3000/denozord/EvoBGP/commit/92f6de8))
|
||||
* web(ui): app layout — sidebar, mobile sheet, topbar, PageHeader ([2d78c81](http://192.168.100.56:3000/denozord/EvoBGP/commit/2d78c81))
|
||||
* web(ui): AppDataTable pattern and modules pilot ([b992301](http://192.168.100.56:3000/denozord/EvoBGP/commit/b992301))
|
||||
* web(ui): design tokens — spacing and component scale ([ac635e9](http://192.168.100.56:3000/denozord/EvoBGP/commit/ac635e9))
|
||||
* web(ui): feedback — confirm dialog, skeletons, README ([87309cf](http://192.168.100.56:3000/denozord/EvoBGP/commit/87309cf))
|
||||
* web(ui): form patterns — FormField, formsnap, directories pilot ([77a2cfd](http://192.168.100.56:3000/denozord/EvoBGP/commit/77a2cfd))
|
||||
* web(ui): foundation — ui/core layout, shadcn primitives, notify ([00a2a44](http://192.168.100.56:3000/denozord/EvoBGP/commit/00a2a44))
|
||||
* web(ui): migrate dashboard, schedule, monitoring to ui patterns ([57dcf30](http://192.168.100.56:3000/denozord/EvoBGP/commit/57dcf30))
|
||||
* web(ui): migrate network to ui patterns ([3a3056b](http://192.168.100.56:3000/denozord/EvoBGP/commit/3a3056b))
|
||||
* web(ui): migrate operations and module detail to ui patterns ([1f9930d](http://192.168.100.56:3000/denozord/EvoBGP/commit/1f9930d))
|
||||
* web(ui): migrate settings to ui patterns ([9808aca](http://192.168.100.56:3000/denozord/EvoBGP/commit/9808aca))
|
||||
* Enhance EvoBGP architecture plan by refining the overview, updating todos with detailed descriptions ([30efdbf](http://192.168.100.56:3000/denozord/EvoBGP/commit/30efdbf))
|
||||
* init ([cbf3aaa](http://192.168.100.56:3000/denozord/EvoBGP/commit/cbf3aaa))
|
||||
* Refine EvoBGP architecture plan with updated database schema details, improved descriptions for comp ([431ef3e](http://192.168.100.56:3000/denozord/EvoBGP/commit/431ef3e))
|
||||
* Update EvoBGP architecture plan with new overview and todos. Refined descriptions for control-plane ([31ff5bb](http://192.168.100.56:3000/denozord/EvoBGP/commit/31ff5bb))
|
||||
@@ -44,4 +44,12 @@ docker compose --profile reference up -d
|
||||
- **Go:** модуль `evobgp`, точки входа в `cmd/`.
|
||||
- **Веб:** SvelteKit в каталоге `web/` (см. [web/README.md](web/README.md)).
|
||||
|
||||
## Версионирование и релизы
|
||||
|
||||
Версии определяются автоматически из Conventional Commits при merge в `main` (semantic-release на Gitea Actions). Первая версия — **1.0.0**; ручной bump не нужен.
|
||||
|
||||
- [docs/releasing.md](docs/releasing.md) — пайплайн, commit conventions, секреты CI
|
||||
- API: `GET /version` и `GET /v1/version` (поле `version`)
|
||||
- Docker-образы: теги `latest`, `vX.Y.Z`, `X.Y.Z` — в **том же CI run**, что и релиз (job `release`)
|
||||
|
||||
Лицензия и условия использования — по политике владельца репозитория.
|
||||
@@ -19,6 +19,7 @@ import (
|
||||
"evobgp/internal/platform"
|
||||
"evobgp/internal/render"
|
||||
"evobgp/internal/scheduler"
|
||||
"evobgp/internal/version"
|
||||
)
|
||||
|
||||
// microVPS entrypoint: один процесс — HTTP API и фоновые воркеры scheduler, ingest, render, deploy (общий store и jobs.Registry).
|
||||
@@ -37,7 +38,7 @@ func main() {
|
||||
log.Fatal(err)
|
||||
}
|
||||
defer srv.Close()
|
||||
observability.SetBuildInfo("0.1.0", strings.TrimSpace(os.Getenv("EVOBGP_GIT_SHA")))
|
||||
observability.SetBuildInfo(version.Version, version.GitSHA)
|
||||
|
||||
ctx, stop := signal.NotifyContext(context.Background(), os.Interrupt, syscall.SIGTERM)
|
||||
defer stop()
|
||||
|
||||
@@ -15,6 +15,7 @@ import (
|
||||
"evobgp/internal/httpapi"
|
||||
"evobgp/internal/observability"
|
||||
"evobgp/internal/platform"
|
||||
"evobgp/internal/version"
|
||||
)
|
||||
|
||||
func main() {
|
||||
@@ -34,7 +35,7 @@ func main() {
|
||||
}
|
||||
defer srv.Close()
|
||||
|
||||
observability.SetBuildInfo("0.1.0", strings.TrimSpace(os.Getenv("EVOBGP_GIT_SHA")))
|
||||
observability.SetBuildInfo(version.Version, version.GitSHA)
|
||||
ctx, stop := signal.NotifyContext(context.Background(), os.Interrupt, syscall.SIGTERM)
|
||||
defer stop()
|
||||
|
||||
|
||||
@@ -2,7 +2,11 @@
|
||||
|
||||
## CI (Gitea Actions)
|
||||
|
||||
Сборка образов — **`docker buildx bake`** (`deploy/docker/docker-bake.hcl`), а не матрица из восьми отдельных `docker build`.
|
||||
Сборка образов — **`docker buildx bake`** (`deploy/docker/docker-bake.hcl`), не отдельные `docker build`.
|
||||
|
||||
**Publish:** push в `main` после quality gates — job **release** в [.gitea/workflows/ci.yaml](../.gitea/workflows/ci.yaml): semantic-release + bake с `VERSION` из релиза.
|
||||
|
||||
Локально BuildKit также кэширует `/go/pkg/mod` и `~/.cache/go-build` через `RUN --mount=type=cache`.
|
||||
|
||||
| Было | Стало |
|
||||
|------|--------|
|
||||
@@ -31,6 +35,8 @@ cd deploy/docker
|
||||
export REGISTRY=git.shts.su/<owner>
|
||||
export IMAGE_TAG=latest
|
||||
export SHORT_SHA=$(git rev-parse --short HEAD)
|
||||
export VERSION=dev
|
||||
export BUILD_TIME=
|
||||
sh write-bake-override.sh
|
||||
docker buildx bake --allow=fs.read=../.. -f docker-bake.hcl -f docker-bake.override.hcl go-images
|
||||
docker buildx bake --allow=fs.read=../.. -f docker-bake.hcl -f docker-bake.override.hcl web-images
|
||||
|
||||
@@ -19,6 +19,14 @@ variable "SHA_FULL" {
|
||||
default = ""
|
||||
}
|
||||
|
||||
variable "VERSION" {
|
||||
default = "dev"
|
||||
}
|
||||
|
||||
variable "BUILD_TIME" {
|
||||
default = ""
|
||||
}
|
||||
|
||||
variable "CACHE_REF_GO" {
|
||||
default = ""
|
||||
}
|
||||
@@ -87,6 +95,11 @@ target "go-build-all" {
|
||||
dockerfile = "deploy/docker/gobinary/Dockerfile"
|
||||
target = "build-all"
|
||||
platforms = ["linux/amd64"]
|
||||
args = {
|
||||
VERSION = VERSION
|
||||
GIT_SHA = notequal("", SHA_FULL) ? SHA_FULL : SHORT_SHA
|
||||
BUILD_TIME = BUILD_TIME
|
||||
}
|
||||
contexts = {
|
||||
deps = "target:go-deps"
|
||||
}
|
||||
@@ -127,14 +140,20 @@ target "_go-runtime-birdc" {
|
||||
|
||||
function "image-tags" {
|
||||
params = [name]
|
||||
result = notequal("", SHA_FULL) ? [
|
||||
"${REGISTRY}/${name}:${IMAGE_TAG}",
|
||||
"${REGISTRY}/${name}:${SHORT_SHA}",
|
||||
"${REGISTRY}/${name}:sha-${SHA_FULL}",
|
||||
] : [
|
||||
"${REGISTRY}/${name}:${IMAGE_TAG}",
|
||||
"${REGISTRY}/${name}:${SHORT_SHA}",
|
||||
]
|
||||
result = concat(
|
||||
notequal("", SHA_FULL) ? [
|
||||
"${REGISTRY}/${name}:${IMAGE_TAG}",
|
||||
"${REGISTRY}/${name}:${SHORT_SHA}",
|
||||
"${REGISTRY}/${name}:sha-${SHA_FULL}",
|
||||
] : [
|
||||
"${REGISTRY}/${name}:${IMAGE_TAG}",
|
||||
"${REGISTRY}/${name}:${SHORT_SHA}",
|
||||
],
|
||||
notequal(VERSION, "dev") && notequal(VERSION, "") ? [
|
||||
"${REGISTRY}/${name}:v${VERSION}",
|
||||
"${REGISTRY}/${name}:${VERSION}",
|
||||
] : []
|
||||
)
|
||||
}
|
||||
|
||||
target "evobgp-api" {
|
||||
|
||||
@@ -10,22 +10,38 @@ RUN --mount=type=cache,target=/go/pkg/mod,sharing=locked \
|
||||
|
||||
FROM deps AS build-all
|
||||
COPY . .
|
||||
ARG VERSION=dev
|
||||
ARG GIT_SHA=unknown
|
||||
ARG BUILD_TIME=
|
||||
RUN --mount=type=cache,target=/go/pkg/mod,sharing=locked \
|
||||
--mount=type=cache,target=/root/.cache/go-build,sharing=locked \
|
||||
set -eux; \
|
||||
mkdir -p /out; \
|
||||
for d in cmd/*/; do \
|
||||
name="$(basename "$d")"; \
|
||||
CGO_ENABLED=0 go build -trimpath -ldflags="-s -w" -o "/out/${name}" "./cmd/${name}"; \
|
||||
CGO_ENABLED=0 go build -trimpath \
|
||||
-ldflags="-s -w \
|
||||
-X evobgp/internal/version.Version=${VERSION} \
|
||||
-X evobgp/internal/version.GitSHA=${GIT_SHA} \
|
||||
-X evobgp/internal/version.BuildTime=${BUILD_TIME}" \
|
||||
-o "/out/${name}" "./cmd/${name}"; \
|
||||
done
|
||||
|
||||
# Один бинарь (локальная сборка); в CI — build-all + runtime.
|
||||
FROM deps AS build
|
||||
COPY . .
|
||||
ARG BIN=evobgp-api
|
||||
ARG VERSION=dev
|
||||
ARG GIT_SHA=unknown
|
||||
ARG BUILD_TIME=
|
||||
RUN --mount=type=cache,target=/go/pkg/mod,sharing=locked \
|
||||
--mount=type=cache,target=/root/.cache/go-build,sharing=locked \
|
||||
CGO_ENABLED=0 go build -trimpath -ldflags="-s -w" -o /out/evobgp "./cmd/${BIN}"
|
||||
CGO_ENABLED=0 go build -trimpath \
|
||||
-ldflags="-s -w \
|
||||
-X evobgp/internal/version.Version=${VERSION} \
|
||||
-X evobgp/internal/version.GitSHA=${GIT_SHA} \
|
||||
-X evobgp/internal/version.BuildTime=${BUILD_TIME}" \
|
||||
-o /out/evobgp "./cmd/${BIN}"
|
||||
|
||||
FROM public.ecr.aws/docker/library/debian:bookworm-slim AS birdc
|
||||
ARG BIRD_VERSION=2.14
|
||||
|
||||
@@ -6,6 +6,8 @@ REGISTRY="${REGISTRY:?REGISTRY required}"
|
||||
IMAGE_TAG="${IMAGE_TAG:-latest}"
|
||||
SHORT_SHA="${SHORT_SHA:-dev}"
|
||||
SHA_FULL="${SHA_FULL:-}"
|
||||
VERSION="${VERSION:-dev}"
|
||||
BUILD_TIME="${BUILD_TIME:-}"
|
||||
CACHE_REF_GO="${CACHE_REF_GO:-}"
|
||||
CACHE_REF_WEB="${CACHE_REF_WEB:-}"
|
||||
cat >"$OUT" <<EOF
|
||||
@@ -22,6 +24,12 @@ variable "SHORT_SHA" {
|
||||
variable "SHA_FULL" {
|
||||
default = "${SHA_FULL}"
|
||||
}
|
||||
variable "VERSION" {
|
||||
default = "${VERSION}"
|
||||
}
|
||||
variable "BUILD_TIME" {
|
||||
default = "${BUILD_TIME}"
|
||||
}
|
||||
variable "CACHE_REF_GO" {
|
||||
default = "${CACHE_REF_GO}"
|
||||
}
|
||||
|
||||
@@ -20,6 +20,7 @@
|
||||
| [api.md](api.md) | REST: префикс `/v1`, публичные маршруты, ссылки на OpenAPI |
|
||||
| [router-lists-ui-integration.md](router-lists-ui-integration.md) | Интеграция `router-lists-ui` с EvoBGP API (`DOMAINS/IP_RANGES/AS_PREFIXES/communities`) |
|
||||
| [access.md](access.md) | Выдача доступа: API-ключи, роли, нода, CORS |
|
||||
| [releasing.md](releasing.md) | Автоматические релизы, Conventional Commits, CI |
|
||||
| [openapi.yaml](openapi.yaml) | Источник правды по контракту API |
|
||||
| [OPENAPI-GITEA.md](OPENAPI-GITEA.md) | Как открыть HTML-документацию API (в т.ч. из Gitea) |
|
||||
| [evobgp-api-sketches.md](evobgp-api-sketches.md) | Ранний черновик идей API (контекст, не замена OpenAPI) |
|
||||
@@ -31,7 +32,13 @@
|
||||
| [../.cursor/rules/engineering.mdc](../.cursor/rules/engineering.mdc) | Go, API, migrations, security, enforcement |
|
||||
| [../.cursor/rules/web-shadcn.mdc](../.cursor/rules/web-shadcn.mdc) | SvelteKit, shadcn-svelte |
|
||||
| [../.cursor/rules/networking-bird.mdc](../.cursor/rules/networking-bird.mdc) | BIRD2, BGP policy, IP/CIDR |
|
||||
| [../.cursor/rules/conventional-commits.mdc](../.cursor/rules/conventional-commits.mdc) | Conventional Commits (заголовок EN, тело RU) |
|
||||
|
||||
### Сообщения коммитов (Cursor)
|
||||
|
||||
После `git add` попросите агента: **«сгенерируй коммит по staged»**, **«закоммить»**, **«commit message»** — агент **обязан первым делом** запустить `scripts/commit/staged-context.ps1`, затем скилл [commit-message](../.cursor/skills/commit-message/SKILL.md) (заголовок EN, тело RU, auto-split). Просмотр групп вручную: `powershell -NoProfile -File scripts/commit/staged-context.ps1 | ConvertFrom-Json`.
|
||||
|
||||
## Репозиторий и CI
|
||||
|
||||
- [../.gitea/README.md](../.gitea/README.md) — Gitea Actions, runner, сборка образов и push в Container Registry.
|
||||
- [releasing.md](releasing.md) — semantic-release, commit conventions, теги образов.
|
||||
+30
-2
@@ -1,10 +1,12 @@
|
||||
openapi: 3.1.0
|
||||
info:
|
||||
title: EvoBGP Control Plane API
|
||||
version: 0.1.0
|
||||
version: 1.0.0
|
||||
description: |
|
||||
REST API управления префиксами, модулями ingest, ревизиями конфигурации BIRD и задачами (async jobs).
|
||||
|
||||
**Актуальная semver-сборка:** `GET /version` или `GET /v1/version` (поле `version`; совпадает с git-тегом `vX.Y.Z`).
|
||||
|
||||
**Соглашения:** префикс `/v1`; идентификаторы - UUID v7 или ULID (строки); время - ISO 8601 UTC.
|
||||
Ошибки - `application/problem+json` ([RFC 9457](https://www.rfc-editor.org/rfc/rfc9457)).
|
||||
Пагинация списков - `cursor` + `limit`; ответ содержит `items`, `next_cursor`, `has_more`.
|
||||
@@ -758,8 +760,14 @@ components:
|
||||
VersionInfo:
|
||||
type: object
|
||||
properties:
|
||||
version:
|
||||
type: string
|
||||
description: Semver сборки (git tag без префикса v).
|
||||
example: "1.2.3"
|
||||
api_version:
|
||||
type: string
|
||||
deprecated: true
|
||||
description: Alias поля `version` (сохранён для обратной совместимости).
|
||||
git_sha:
|
||||
type: string
|
||||
build_time:
|
||||
@@ -967,11 +975,31 @@ paths:
|
||||
default:
|
||||
$ref: "#/components/responses/DefaultProblem"
|
||||
|
||||
/version:
|
||||
get:
|
||||
tags: [System]
|
||||
summary: Версия сборки (корневой путь)
|
||||
description: |
|
||||
Аналог `GET /v1/version`. Публичный маршрут без аутентификации.
|
||||
Semver в поле `version` задаётся при сборке Docker-образов из git-тега.
|
||||
operationId: getVersionRoot
|
||||
responses:
|
||||
"200":
|
||||
description: Метаданные сборки.
|
||||
content:
|
||||
application/json:
|
||||
schema:
|
||||
$ref: "#/components/schemas/VersionInfo"
|
||||
default:
|
||||
$ref: "#/components/responses/DefaultProblem"
|
||||
|
||||
/v1/version:
|
||||
get:
|
||||
tags: [System]
|
||||
summary: Версия сборки
|
||||
description: Версия API и control-plane (`git_sha`, `build_time` и др.).
|
||||
description: |
|
||||
Semver control-plane и метаданные сборки (`git_sha`, `build_time`).
|
||||
Дублирует `GET /version`.
|
||||
operationId: getVersion
|
||||
responses:
|
||||
"200":
|
||||
|
||||
@@ -0,0 +1,72 @@
|
||||
# Релизы и версионирование EvoBGP
|
||||
|
||||
EvoBGP использует [Conventional Commits](https://www.conventionalcommits.org/) и [semantic-release](https://semantic-release.gitbook.io/) для полностью автоматических релизов на Gitea (`git.shts.su`). Ручное повышение версии в коде не требуется.
|
||||
|
||||
## Как определяется версия
|
||||
|
||||
| Тип коммита | Bump |
|
||||
|-------------|------|
|
||||
| `feat` | minor (1.0.0 → 1.1.0) |
|
||||
| `fix`, `perf` | patch (1.0.0 → 1.0.1) |
|
||||
| `feat!`, `fix!` или `BREAKING CHANGE:` в теле | major (1.0.0 → 2.0.0) |
|
||||
| `docs`, `chore`, `ci`, `test`, `refactor` | без релиза |
|
||||
|
||||
Первый релиз при отсутствии git-тегов — **1.0.0**, если есть releasable-коммиты.
|
||||
|
||||
Подробные правила сообщений коммитов: [.cursor/rules/conventional-commits.mdc](../.cursor/rules/conventional-commits.mdc).
|
||||
|
||||
## CI-пайплайн (один push в main)
|
||||
|
||||
```text
|
||||
push/merge в main
|
||||
→ CI: openapi, web, go, bird2 (параллельно)
|
||||
→ job release (в том же workflow, после quality gates):
|
||||
→ semantic-release: git tag vX.Y.Z на текущий commit (без доп. commit)
|
||||
→ Gitea Release + CHANGELOG.md как attachment
|
||||
→ docker buildx bake с VERSION=X.Y.Z
|
||||
→ образы: latest, vX.Y.Z, X.Y.Z, sha-*, короткий SHA
|
||||
```
|
||||
|
||||
Pull request: только quality gates + commitlint; релиз и образы **не** публикуются.
|
||||
|
||||
Workflow: [.gitea/workflows/ci.yaml](../.gitea/workflows/ci.yaml) (job **release**).
|
||||
|
||||
Конфиг semantic-release: [.releaserc.json](../.releaserc.json) — без `@semantic-release/git` (CHANGELOG не коммитится в репозиторий).
|
||||
|
||||
## Секреты Gitea
|
||||
|
||||
Один PAT — **`ACTIONS_PAT`** (Settings → Actions → Secrets).
|
||||
|
||||
| Право PAT | Зачем |
|
||||
|-----------|--------|
|
||||
| push tags | git tag `vX.Y.Z` на commit merge |
|
||||
| releases | Gitea Release + notes |
|
||||
| packages (Container Registry) | push образов |
|
||||
|
||||
Fallback: **`gitea.token`** (нужны права на releases и packages).
|
||||
|
||||
## Источник правды для версии в runtime
|
||||
|
||||
Semver из git-тега пробрасывается в Go-бинарники через `-ldflags` при сборке Docker (`deploy/docker/gobinary/Dockerfile`). Пакет [`internal/version`](../internal/version/version.go):
|
||||
|
||||
- локально (`go run`) — `version: "dev"`
|
||||
- в образе после релиза — совпадает с тегом (например `1.2.3`)
|
||||
|
||||
API: `GET /version`, `GET /v1/version` — поля `version`, `git_sha`, `build_time`.
|
||||
|
||||
Web UI показывает версию из API (footer sidebar, страница «Мониторинг»).
|
||||
|
||||
## CHANGELOG
|
||||
|
||||
Release notes — в Gitea Release; файл `CHANGELOG.md` генерируется в CI и прикрепляется как asset, **не** попадает в git history.
|
||||
|
||||
## Проверка после релиза
|
||||
|
||||
1. Один run workflow **CI** на push в main: job **release** зелёный.
|
||||
2. Gitea: тег `vX.Y.Z` на том же commit, что и merge; Release с notes.
|
||||
3. Container Registry: `evobgp-api:vX.Y.Z`, `evobgp-api:X.Y.Z`, `evobgp-api:latest`.
|
||||
4. `curl http://localhost:8080/version` → `"version":"X.Y.Z"`.
|
||||
|
||||
## Первый релиз (bootstrap)
|
||||
|
||||
Merge в `main` с `feat(release): ...` → **v1.0.0** в том же CI run.
|
||||
@@ -21,6 +21,7 @@ import (
|
||||
"evobgp/internal/pipeline"
|
||||
"evobgp/internal/reports"
|
||||
"evobgp/internal/store"
|
||||
"evobgp/internal/version"
|
||||
)
|
||||
|
||||
// Handler returns the root HTTP handler (system routes public; rest under /v1/ authenticated).
|
||||
@@ -30,6 +31,7 @@ func (s *Server) Handler() http.Handler {
|
||||
wrappedV1 := http.StripPrefix("/v1", v1)
|
||||
|
||||
s.mux.Handle("GET /metrics", observability.MetricsHandler())
|
||||
s.mux.HandleFunc("GET /version", s.handleVersion)
|
||||
s.mux.HandleFunc("GET /v1/health", s.handleHealth)
|
||||
s.mux.HandleFunc("GET /v1/ready", s.handleReady)
|
||||
s.mux.HandleFunc("GET /v1/version", s.handleVersion)
|
||||
@@ -96,11 +98,16 @@ func (s *Server) handleReady(w http.ResponseWriter, r *http.Request) {
|
||||
}
|
||||
|
||||
func (s *Server) handleVersion(w http.ResponseWriter, r *http.Request) {
|
||||
sha := strings.TrimSpace(os.Getenv("EVOBGP_GIT_SHA"))
|
||||
if sha == "" {
|
||||
sha = "unknown"
|
||||
ver, sha, buildTime := version.Info()
|
||||
resp := map[string]string{
|
||||
"version": ver,
|
||||
"api_version": ver,
|
||||
"git_sha": sha,
|
||||
}
|
||||
writeJSON(w, http.StatusOK, map[string]string{"api_version": "0.1.0", "git_sha": sha})
|
||||
if buildTime != "" {
|
||||
resp["build_time"] = buildTime
|
||||
}
|
||||
writeJSON(w, http.StatusOK, resp)
|
||||
}
|
||||
|
||||
func moduleJSON(mod *store.Module) map[string]any {
|
||||
|
||||
@@ -13,6 +13,7 @@ import (
|
||||
|
||||
"evobgp/internal/jobs"
|
||||
"evobgp/internal/signing"
|
||||
"evobgp/internal/version"
|
||||
)
|
||||
|
||||
const testBundleSeed = "0101010101010101010101010101010101010101010101010101010101010101"
|
||||
@@ -371,3 +372,60 @@ evobgp_p_01234567 BGP master start 2026-04-09 Connect
|
||||
t.Fatalf("expected protocol state Connect, got %q", got["evobgp_p_01234567"])
|
||||
}
|
||||
}
|
||||
|
||||
func TestVersionEndpoints(t *testing.T) {
|
||||
srv, err := New(Options{
|
||||
InsecureDev: true,
|
||||
SeedDemo: true,
|
||||
BundleSeedHex: testBundleSeed,
|
||||
})
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
defer srv.Close()
|
||||
|
||||
oldVer := version.Version
|
||||
oldSHA := version.GitSHA
|
||||
oldTime := version.BuildTime
|
||||
version.Version = "1.2.3"
|
||||
version.GitSHA = "abc123def456"
|
||||
version.BuildTime = "2026-05-20T12:00:00Z"
|
||||
t.Cleanup(func() {
|
||||
version.Version = oldVer
|
||||
version.GitSHA = oldSHA
|
||||
version.BuildTime = oldTime
|
||||
})
|
||||
|
||||
ts := httptest.NewServer(srv.Handler())
|
||||
defer ts.Close()
|
||||
|
||||
for _, path := range []string{"/version", "/v1/version"} {
|
||||
t.Run(path, func(t *testing.T) {
|
||||
resp, err := http.Get(ts.URL + path)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
defer resp.Body.Close()
|
||||
if resp.StatusCode != http.StatusOK {
|
||||
b, _ := io.ReadAll(resp.Body)
|
||||
t.Fatalf("status %d: %s", resp.StatusCode, b)
|
||||
}
|
||||
var body map[string]string
|
||||
if err := json.NewDecoder(resp.Body).Decode(&body); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if body["version"] != "1.2.3" {
|
||||
t.Fatalf("version=%q want 1.2.3", body["version"])
|
||||
}
|
||||
if body["api_version"] != "1.2.3" {
|
||||
t.Fatalf("api_version=%q want 1.2.3", body["api_version"])
|
||||
}
|
||||
if body["git_sha"] != "abc123def456" {
|
||||
t.Fatalf("git_sha=%q", body["git_sha"])
|
||||
}
|
||||
if body["build_time"] != "2026-05-20T12:00:00Z" {
|
||||
t.Fatalf("build_time=%q", body["build_time"])
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,23 @@
|
||||
// Package version holds build metadata injected at link time via -ldflags.
|
||||
package version
|
||||
|
||||
// Link-time variables (defaults for local development).
|
||||
var (
|
||||
Version = "dev"
|
||||
GitSHA = "unknown"
|
||||
BuildTime = ""
|
||||
)
|
||||
|
||||
// Info returns build metadata, substituting safe defaults for empty values.
|
||||
func Info() (ver, gitSHA, buildTime string) {
|
||||
ver = Version
|
||||
if ver == "" {
|
||||
ver = "dev"
|
||||
}
|
||||
gitSHA = GitSHA
|
||||
if gitSHA == "" {
|
||||
gitSHA = "unknown"
|
||||
}
|
||||
buildTime = BuildTime
|
||||
return ver, gitSHA, buildTime
|
||||
}
|
||||
Generated
+7720
File diff suppressed because it is too large.
Load diff
@@ -0,0 +1,14 @@
|
||||
{
|
||||
"name": "evobgp-release",
|
||||
"private": true,
|
||||
"devDependencies": {
|
||||
"@commitlint/cli": "^19.8.1",
|
||||
"@commitlint/config-conventional": "^19.8.1",
|
||||
"@markwylde/semantic-release-gitea": "^2.2.0",
|
||||
"@semantic-release/changelog": "^6.0.3",
|
||||
"@semantic-release/commit-analyzer": "^13.0.1",
|
||||
"@semantic-release/exec": "^7.0.0",
|
||||
"@semantic-release/release-notes-generator": "^14.0.3",
|
||||
"semantic-release": "^25.0.2"
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,181 @@
|
||||
#Requires -Version 5.1
|
||||
<#
|
||||
.SYNOPSIS
|
||||
Staged git changes grouped by EvoBGP scope for Conventional Commit generation.
|
||||
|
||||
.DESCRIPTION
|
||||
Outputs JSON to stdout: file groups, --stat, and truncated patch per group.
|
||||
Exit 1 if nothing is staged. Does not write commit messages.
|
||||
|
||||
.EXAMPLE
|
||||
powershell -NoProfile -File scripts/commit/staged-context.ps1
|
||||
powershell -NoProfile -File scripts/commit/staged-context.ps1 | ConvertFrom-Json
|
||||
#>
|
||||
Set-StrictMode -Version Latest
|
||||
$ErrorActionPreference = 'Stop'
|
||||
|
||||
$MaxLinesPerGroup = 120
|
||||
$MaxLinesTotal = 500
|
||||
|
||||
function Test-GitRepository {
|
||||
$null = git rev-parse --git-dir 2>$null
|
||||
if ($LASTEXITCODE -ne 0) {
|
||||
Write-Error 'staged-context: not a git repository'
|
||||
exit 2
|
||||
}
|
||||
}
|
||||
|
||||
function Get-ScopeForPath {
|
||||
param([string]$Path)
|
||||
$p = $Path -replace '\\', '/'
|
||||
|
||||
# Longest / most specific prefixes first
|
||||
$rules = @(
|
||||
@{ Prefix = 'docs/openapi.yaml'; Scope = 'openapi' }
|
||||
@{ Prefix = 'redocly.yaml'; Scope = 'openapi' }
|
||||
@{ Prefix = 'internal/httpapi/'; Scope = 'httpapi' }
|
||||
@{ Prefix = 'internal/store/'; Scope = 'store' }
|
||||
@{ Prefix = 'internal/repository/'; Scope = 'store' }
|
||||
@{ Prefix = 'internal/db/'; Scope = 'store' }
|
||||
@{ Prefix = 'internal/jobs/'; Scope = 'jobs' }
|
||||
@{ Prefix = 'internal/pipeline/'; Scope = 'pipeline' }
|
||||
@{ Prefix = 'internal/birdfmt/'; Scope = 'birdfmt' }
|
||||
@{ Prefix = 'internal/birddeploy/'; Scope = 'birddeploy' }
|
||||
@{ Prefix = 'internal/bundle/'; Scope = 'bundle' }
|
||||
@{ Prefix = 'internal/signing/'; Scope = 'bundle' }
|
||||
@{ Prefix = 'cmd/'; Scope = 'cmd' }
|
||||
@{ Prefix = 'web/'; Scope = 'web' }
|
||||
@{ Prefix = 'migrations/'; Scope = 'db' }
|
||||
@{ Prefix = 'docs/'; Scope = 'docs' }
|
||||
@{ Prefix = '.gitea/'; Scope = 'ci' }
|
||||
@{ Prefix = 'deploy/'; Scope = 'deploy' }
|
||||
@{ Prefix = '.cursor/'; Scope = 'chore' }
|
||||
)
|
||||
|
||||
foreach ($rule in $rules) {
|
||||
if ($p -eq $rule.Prefix.TrimEnd('/') -or $p.StartsWith($rule.Prefix)) {
|
||||
return $rule.Scope
|
||||
}
|
||||
}
|
||||
return 'chore'
|
||||
}
|
||||
|
||||
function Get-ScopeSortOrder {
|
||||
param([string]$Scope)
|
||||
$order = @{
|
||||
openapi = 10
|
||||
httpapi = 20
|
||||
store = 21
|
||||
jobs = 22
|
||||
pipeline = 30
|
||||
birdfmt = 31
|
||||
birddeploy = 32
|
||||
bundle = 33
|
||||
web = 40
|
||||
ci = 50
|
||||
deploy = 51
|
||||
db = 52
|
||||
docs = 60
|
||||
cmd = 70
|
||||
chore = 80
|
||||
}
|
||||
if ($order.ContainsKey($Scope)) { return $order[$Scope] }
|
||||
return 99
|
||||
}
|
||||
|
||||
function Invoke-Git {
|
||||
param([string[]]$GitArgs)
|
||||
$out = & git @GitArgs 2>&1
|
||||
if ($LASTEXITCODE -ne 0) {
|
||||
$msg = ($out | Out-String).Trim()
|
||||
throw "git $($GitArgs -join ' ') failed: $msg"
|
||||
}
|
||||
return ($out | Out-String).TrimEnd()
|
||||
}
|
||||
|
||||
function New-GitArgs {
|
||||
param([string[]]$Base, [string[]]$Paths)
|
||||
if ($Paths.Count -eq 0) { return $Base }
|
||||
return $Base + '--' + $Paths
|
||||
}
|
||||
|
||||
function Get-TruncatedDiff {
|
||||
param(
|
||||
[string[]]$Files,
|
||||
[int]$MaxLines,
|
||||
[ref]$TotalLinesUsed
|
||||
)
|
||||
if ($Files.Count -eq 0) { return '' }
|
||||
|
||||
$remaining = $MaxLinesTotal - $TotalLinesUsed.Value
|
||||
if ($remaining -le 0) {
|
||||
return '[diff truncated: global line budget exceeded]'
|
||||
}
|
||||
|
||||
$cap = [Math]::Min($MaxLines, $remaining)
|
||||
$diff = Invoke-Git -GitArgs (New-GitArgs -Base @(
|
||||
'diff', '--cached', '--no-color', '--unified=3'
|
||||
) -Paths $Files)
|
||||
if ([string]::IsNullOrWhiteSpace($diff)) { return '' }
|
||||
|
||||
$lines = $diff -split "`n", -1
|
||||
if ($lines.Count -le $cap) {
|
||||
$TotalLinesUsed.Value += $lines.Count
|
||||
return $diff
|
||||
}
|
||||
|
||||
$truncated = ($lines[0..($cap - 1)] -join "`n") + "`n... [truncated: $($lines.Count - $cap) more lines]"
|
||||
$TotalLinesUsed.Value += $cap
|
||||
return $truncated
|
||||
}
|
||||
|
||||
Test-GitRepository
|
||||
|
||||
$stagedRaw = Invoke-Git -GitArgs @('diff', '--cached', '--name-only')
|
||||
if ([string]::IsNullOrWhiteSpace($stagedRaw)) {
|
||||
[Console]::Error.WriteLine('staged-context: no staged changes (git index is empty)')
|
||||
exit 1
|
||||
}
|
||||
|
||||
$stagedFiles = @(
|
||||
$stagedRaw -split "`n" | ForEach-Object { $_.Trim() } | Where-Object { $_ -ne '' }
|
||||
)
|
||||
$stagedFiles = $stagedFiles | Sort-Object -Unique
|
||||
|
||||
$scopeBuckets = @{}
|
||||
foreach ($f in $stagedFiles) {
|
||||
$scope = Get-ScopeForPath -Path $f
|
||||
if (-not $scopeBuckets.ContainsKey($scope)) {
|
||||
$scopeBuckets[$scope] = [System.Collections.Generic.List[string]]::new()
|
||||
}
|
||||
$scopeBuckets[$scope].Add($f) | Out-Null
|
||||
}
|
||||
|
||||
$totalLinesUsed = 0
|
||||
$groups = New-Object System.Collections.Generic.List[object]
|
||||
|
||||
$scopeKeys = @($scopeBuckets.Keys | Sort-Object { Get-ScopeSortOrder $_ })
|
||||
|
||||
foreach ($scope in $scopeKeys) {
|
||||
$files = @($scopeBuckets[$scope] | Sort-Object)
|
||||
$stat = Invoke-Git -GitArgs (New-GitArgs -Base @('diff', '--cached', '--stat') -Paths $files)
|
||||
$diffExcerpt = Get-TruncatedDiff -Files $files -MaxLines $MaxLinesPerGroup -TotalLinesUsed ([ref]$totalLinesUsed)
|
||||
|
||||
$groups.Add([ordered]@{
|
||||
scope = $scope
|
||||
files = $files
|
||||
stat = $stat
|
||||
diff_excerpt = $diffExcerpt
|
||||
}) | Out-Null
|
||||
}
|
||||
|
||||
$result = [ordered]@{
|
||||
staged_count = $stagedFiles.Count
|
||||
groups = $groups.ToArray()
|
||||
}
|
||||
|
||||
$json = $result | ConvertTo-Json -Depth 6 -Compress:$false
|
||||
# UTF-8 stdout for agents / ConvertFrom-Json
|
||||
[Console]::OutputEncoding = [System.Text.UTF8Encoding]::new($false)
|
||||
Write-Output $json
|
||||
exit 0
|
||||
@@ -0,0 +1,49 @@
|
||||
/** Ответ GET /v1/version (см. docs/openapi.yaml VersionInfo). */
|
||||
export type VersionInfo = {
|
||||
version?: string;
|
||||
api_version?: string;
|
||||
git_sha?: string;
|
||||
build_time?: string;
|
||||
};
|
||||
|
||||
const PLACEHOLDER_SHA = new Set(['', 'unknown']);
|
||||
|
||||
function nonEmptyString(value: unknown): string {
|
||||
if (typeof value === 'string') {
|
||||
const s = value.trim();
|
||||
return s;
|
||||
}
|
||||
if (value == null) return '';
|
||||
return String(value).trim();
|
||||
}
|
||||
|
||||
/** Semver или legacy api_version; пустая строка, если полей нет. */
|
||||
export function resolveVersionString(info: VersionInfo | null | undefined): string {
|
||||
if (!info) return '';
|
||||
return nonEmptyString(info.version) || nonEmptyString(info.api_version);
|
||||
}
|
||||
|
||||
/** Короткий git SHA; пусто для placeholder «unknown». */
|
||||
export function resolveGitSha(info: VersionInfo | null | undefined): string {
|
||||
if (!info) return '';
|
||||
const sha = nonEmptyString(info.git_sha);
|
||||
if (!sha || PLACEHOLDER_SHA.has(sha.toLowerCase())) return '';
|
||||
return sha;
|
||||
}
|
||||
|
||||
/** Заголовок карточки «Версия» на /monitoring. */
|
||||
export function formatVersionHeadline(info: VersionInfo | null | undefined): string {
|
||||
if (!info) return '—';
|
||||
const ver = resolveVersionString(info);
|
||||
const sha = resolveGitSha(info);
|
||||
if (ver && sha) return `${ver} (${sha.slice(0, 12)})`;
|
||||
if (ver) return ver;
|
||||
if (sha) return sha.slice(0, 12);
|
||||
return '—';
|
||||
}
|
||||
|
||||
/** Подпись в сайдбаре (v1.2.3). */
|
||||
export function formatVersionSidebarLabel(info: VersionInfo | null | undefined): string | null {
|
||||
const ver = resolveVersionString(info);
|
||||
return ver ? `v${ver}` : null;
|
||||
}
|
||||
@@ -7,6 +7,7 @@
|
||||
import PanelLeftClose from '@lucide/svelte/icons/panel-left-close';
|
||||
import PanelLeftOpen from '@lucide/svelte/icons/panel-left-open';
|
||||
import AppNavLinks from './app-nav-links.svelte';
|
||||
import AppVersion from './app-version.svelte';
|
||||
import ThemeMenu from './theme-menu.svelte';
|
||||
|
||||
let {
|
||||
@@ -54,4 +55,5 @@
|
||||
</div>
|
||||
<Separator />
|
||||
<AppNavLinks {collapsed} />
|
||||
<AppVersion />
|
||||
</aside>
|
||||
@@ -0,0 +1,22 @@
|
||||
<script lang="ts">
|
||||
import { onMount } from 'svelte';
|
||||
import { apiJSON } from '$lib/api/client.js';
|
||||
import { formatVersionSidebarLabel, type VersionInfo } from '$lib/api/version-info.js';
|
||||
|
||||
let label = $state<string | null>(null);
|
||||
|
||||
onMount(() => {
|
||||
void (async () => {
|
||||
try {
|
||||
const info = await apiJSON<VersionInfo>('/v1/version');
|
||||
label = formatVersionSidebarLabel(info);
|
||||
} catch {
|
||||
label = null;
|
||||
}
|
||||
})();
|
||||
});
|
||||
</script>
|
||||
|
||||
{#if label}
|
||||
<p class="truncate px-3 pb-3 text-xs text-sidebar-foreground/45" title={label}>{label}</p>
|
||||
{/if}
|
||||
@@ -2,6 +2,11 @@
|
||||
import { onMount } from 'svelte';
|
||||
import { apiJSON, apiFetch } from '$lib/api/client.js';
|
||||
import type { BirdStatus, JobsResponse } from '$lib/api/types.js';
|
||||
import {
|
||||
formatVersionHeadline,
|
||||
resolveVersionString,
|
||||
type VersionInfo
|
||||
} from '$lib/api/version-info.js';
|
||||
import { Badge } from '$lib/components/ui/badge/index.js';
|
||||
import { Button } from '$lib/components/ui/button/index.js';
|
||||
import {
|
||||
@@ -21,12 +26,6 @@
|
||||
import PageHeader from '$lib/ui/app/page-header/page-header.svelte';
|
||||
|
||||
type ReadyStatus = { status: string; checks?: Record<string, unknown> };
|
||||
type VersionInfo = {
|
||||
version?: string;
|
||||
git_sha?: string;
|
||||
build_time?: string;
|
||||
[k: string]: unknown;
|
||||
};
|
||||
type JobsKpi = { running: number; failed: number; total: number };
|
||||
|
||||
let health = $state<{ ok: boolean; status?: string; error?: string } | null>(null);
|
||||
@@ -98,11 +97,16 @@
|
||||
return `${bird.bgp_established}/${bird.bgp_sessions_total}`;
|
||||
});
|
||||
|
||||
const versionText = $derived.by(() => {
|
||||
if (!version) return '—';
|
||||
const sha =
|
||||
typeof version.git_sha === 'string' && version.git_sha ? version.git_sha : 'unknown';
|
||||
return sha.slice(0, 12);
|
||||
const versionText = $derived(formatVersionHeadline(version));
|
||||
|
||||
const versionFooter = $derived.by(() => {
|
||||
if (!version) return '';
|
||||
const parts: string[] = [];
|
||||
const ver = resolveVersionString(version);
|
||||
if (ver) parts.push(`version: ${ver}`);
|
||||
if (version.git_sha) parts.push(`git_sha: ${version.git_sha}`);
|
||||
if (version.build_time) parts.push(`build: ${version.build_time}`);
|
||||
return parts.join(' · ');
|
||||
});
|
||||
|
||||
async function load() {
|
||||
@@ -258,10 +262,8 @@
|
||||
</div>
|
||||
{#if versionError}
|
||||
<p class="mt-2 text-xs text-destructive">{versionError}</p>
|
||||
{:else if version}
|
||||
<p class="mt-2 truncate text-xs text-muted-foreground">
|
||||
api_version: {String(version.api_version ?? 'unknown')}
|
||||
</p>
|
||||
{:else if versionFooter}
|
||||
<p class="mt-2 truncate text-xs text-muted-foreground">{versionFooter}</p>
|
||||
{/if}
|
||||
</CardContent>
|
||||
</Card>
|
||||
|
||||
Reference in new issue
Block a user