# Релизы и версионирование EvoBGP EvoBGP использует [Conventional Commits](https://www.conventionalcommits.org/) и [semantic-release](https://semantic-release.gitbook.io/) для полностью автоматических релизов на Gitea (`git.shx.one`). Ручное повышение версии в коде не требуется. ## Как определяется версия | Тип коммита | Bump | |-------------|------| | `feat` | minor (1.0.0 → 1.1.0) | | `fix`, `perf`, `ci`, `refactor` | patch (1.5.1 → 1.5.2) | | `feat!`, `fix!` или `BREAKING CHANGE:` в теле | major (1.0.0 → 2.0.0) | | `docs`, `chore`, `test` | без релиза | **Scope:** один идентификатор **без запятых** (`web`, `httpapi`, `api`). Заголовок `refactor(a, b): …` **не парсится** semantic-release → релиз не создаётся (commitlint на PR это тоже отклонит). Подробнее — раздел «Scope и semantic-release» ниже. `refactor` — patch без новых функций: перестройка кода/UI при том же поведении для пользователя. По semver на одном уровне с `fix`, но семантически «мельче» `feat` (не minor). Отдельного суффикса `1.x.y.fix` в semver нет: «fix» в Conventional Commits означает **patch** (третья цифра). Для починки пайплайна без смены продукта — `fix(ci):` или `ci:` (оба дают patch после настройки `.releaserc.json`). Первый релиз при отсутствии git-тегов — **1.0.0**, если есть releasable-коммиты. **Как не перепутать `feat` и `fix`:** см. раздел «Выбор type: semver, а не «красивые слова»» в [.cursor/rules/conventional-commits.mdc](../.cursor/rules/conventional-commits.mdc). Кратко: `feat` — новая возможность (minor); `fix` — починка ожидаемого поведения (patch); follow-up баги после недавнего `feat` — всегда `fix`, даже если diff большой. Подробные правила сообщений коммитов: [.cursor/rules/conventional-commits.mdc](../.cursor/rules/conventional-commits.mdc). ## CI-пайплайн (push в main) ```text push/merge в main → workflow CD: quality (openapi, web, go, bird2) → job publish: → semantic-release: git tag vX.Y.Z на текущий commit (без доп. commit) → Gitea Release + CHANGELOG.md как attachment → зеркало base-образов в evobgp-buildcache:base-* → docker buildx bake с VERSION=X.Y.Z (pull=false, named builder evobgp) → образы: latest, vX.Y.Z, X.Y.Z, sha-*, короткий SHA ``` Pull request: workflow **CI** — quality gates + commitlint; релиз и образы **не** публикуются. Workflows: [.gitea/workflows/ci.yaml](../.gitea/workflows/ci.yaml), [.gitea/workflows/cd.yaml](../.gitea/workflows/cd.yaml), reusable [.gitea/workflows/quality.yaml](../.gitea/workflows/quality.yaml). Конфиг semantic-release: [.releaserc.json](../.releaserc.json) — без `@semantic-release/git` (CHANGELOG не коммитится в репозиторий). ## Секреты Gitea PAT репозитория — **`ACTIONS_PAT`** (Settings → Actions → Secrets). | Секрет | Зачем | |--------|--------| | `ACTIONS_PAT` | git tag `vX.Y.Z`, Gitea Release, push в Container Registry | | `docker_hub_token` | логин на Docker Hub при зеркале base-образов и bake (лимиты anonymous pull) | | `docker_hub_username` | имя пользователя Docker Hub; если пусто — `gitea.actor` | Fallback для **git tag**: `github.token`, если PAT недоступен. Push образов в Container Registry — **только `ACTIONS_PAT`** (у job token Gitea нет права 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, страница «Мониторинг»). ## Scope и semantic-release Парсер [conventional-commits-parser](https://github.com/conventional-changelog/conventional-changelog/tree/master/packages/conventional-commits-parser) (его использует semantic-release) **не понимает запятые в scope**: | Заголовок | Парсится | Релиз | |-----------|----------|-------| | `refactor(web): fix layout` | да, `refactor` | patch | | `refactor(NetworkOverviewTab, NetworkSpeakersCard): fix layout` | **нет**, `type: null` | **нет** | Правило: **один scope** из таблицы в [.cursor/rules/conventional-commits.mdc](../.cursor/rules/conventional-commits.mdc) (`web`, `httpapi`, `api`, …). На push в `main` job **publish** запускает `scripts/commit/verify-release-commits.mjs` — в логе будут предупреждения о непарсящихся коммитах. Если релиз «не создался», а CI зелёный: смотрите лог release — часто `No releasable commits`. Исправление: новый коммит с корректным заголовком (например `refactor(web): …`). ## Перезапуск упавшего job publish semantic-release пишет `.release-version` только в `successCmd` при **новом** релизе. Если тег `vX.Y.Z` уже создан, а `docker buildx bake` упал, повторный run того же SHA делает semantic-release no-op (файла нет). Job **publish** тогда берёт версию из git-тега на `HEAD` и публикует образы. Перезапускать нужно **весь job publish**, не отдельный шаг bake: checkout + semantic-release + detect + bake идут подряд. ## CHANGELOG Release notes — в Gitea Release; файл `CHANGELOG.md` генерируется в CI и прикрепляется как asset, **не** попадает в git history. ## Проверка после релиза 1. Один run workflow **CD** на push в main: job **publish** зелёный. 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.