# Release Automation Ordinis uses **semantic-release** для автоматического создания tag'ов на основе conventional commits. После merge в `main` CI анализирует commits, решает bump (patch/minor/major), генерирует `CHANGELOG.md` и пушит tag `vX.Y.Z`. Tag pipeline далее собирает images и ждёт manual gate на `trigger-deploy-prod`. Prod deploy остаётся под human control — semantic-release создаёт tag, human кликает Deploy. ## One-time setup 1. **Create Project Access Token** (GitLab UI): - `Settings → Access Tokens → Add new token` - Name: `semantic-release` - Role: `Maintainer` - Scopes: `write_repository`, `api` - Expiry: 1 год (renew annually) 2. **Add CI variable**: - `Settings → CI/CD → Variables → Add variable` - Key: `GITLAB_TOKEN` - Value: (token из шага 1) - Type: `Variable` - Flags: `Masked` ✅, `Protected` ✅ (только main + tag pipelines) После этого release job в `.gitlab-ci.yml` будет работать. ### Auto-bump image.tag в ordinis-infra (опционально) `commit-prod-image-bump` job коммитит обновление `image.tag` в `values-prod.yaml` + `values-staging.yaml` на свежий immutable SHA tag (e.g. `v2-16-0-abc12345`) **прямо в `ordinis-infra` main** после каждого release tag. Чинит historical bug когда manual deploys через infra repo получали `:latest` (stale cache → split-brain pods). > Раньше bump шёл через auto-MR — но MR никто не мерджил, values отставали > на несколько релизов, и infra `deploy-prod` откатывал prod на старый > образ. Перешли на direct commit (нет ручного шага, нет 405-квирка при > merge вечно-manual инфра-пайплайна). Setup: 3. **Create Group Access Token** в `2-6/2-6-4/terravault`: - `Group → Settings → Access Tokens → Add new token` - Name: `ordinis-ci-infra-bump` - Role: `Maintainer` (нужен push в protected `main`; Developer-роль push в protected branch не имеет) - Scopes: `write_repository` - Expiry: 1 год 4. **Add CI variable в ordinis project (367)**: - Key: `INFRA_PROJECT_TOKEN` - Value: (token из шага 3) - Flags: `Masked` ✅, `Protected` ✅ 5. **ordinis-infra → Protected branches → `main`**: убедись что `Allowed to push` покрывает этот токен (роль Maintainer покрывает по умолчанию). Без этой переменной job exits early (`allow_failure: true`), tag pipeline не блокируется. Ручной bump `image.tag` в `ordinis-infra` остаётся доступен как fallback. ## Commit conventions Conventional Commits (https://www.conventionalcommits.org/): | Type | Bump | Section в CHANGELOG | |------|------|---------------------| | `feat:` | **minor** | ✨ Features | | `fix:` | patch | 🐛 Fixes | | `perf:` | patch | ⚡ Performance | | `refactor:` | patch | ♻️ Refactoring | | `revert:` | patch | ⏪ Reverts | | `docs:` | none | 📝 Docs | | `chore:` | none | hidden | | `test:` | none | hidden | | `style:` | none | hidden | | `ci:` | none | hidden | | `build:` | none | hidden | **Breaking changes** — добавь `BREAKING CHANGE:` в footer commit message → major bump. ``` feat(api): drop legacy v1 endpoint BREAKING CHANGE: /api/v1/* paths больше не поддерживаются. Все клиенты должны мигрировать на /api/v2. ``` > **⚠ Lesson learned (v2.0.0 false-major):** до 2026-05-12 в .releaserc.json > noteKeywords содержал bare `"BREAKING"`. Слово «Breaking» в обычном > commit body (например «Breaking down PATCH into atomic ops…») триггерило > major bump. Конфиг исправлен — оставлены только canonical > `BREAKING CHANGE` / `BREAKING CHANGES`. Когда нужен major — используй > точно эти триггеры в footer. ## Skip release for specific commit Если хочешь смержить `feat:` или `fix:` но **не** делать release сейчас — используй non-release type (`chore:`, `docs:`, etc). ``` chore(deps): bump axios 1.7 -> 1.8 ``` > ⚠ **НЕ используй `[skip ci]`** в commit message — см. раздел > «Skip-ci marker trap» ниже. ## Loop prevention Release job создаёт commit `chore(release): vX.Y.Z`. Loop prevention работает через releaseRules в `.releaserc.json` — chore-тип имеет `release: false` → semantic-release на release commit'е detect'ит «no releasable commits» → exit 0 → нет нового tag → нет loop'а. > **Historical note:** до MR !154 (2026-05-12) сюда добавлялось `[skip ci]`. > Это работало для main pipeline'а, НО skip'ало и tag pipeline на том же SHA > → images с tag'ом `v*-*-*-` не собирались → `commit-prod-image-bump` > не запускался → manual ручной bump в infra-repo. Удалено. ## ⚠ Skip-ci marker trap GitLab pipeline pattern matcher honors `[skip ci]` в commit subject ИЛИ body — **где угодно в message'е**. semantic-release auto-генерирует release notes из subject+body всех commit'ов с момента последнего tag'а. **Trap:** если хоть один commit body содержит литеральное `[skip ci]` (даже описывая фикс этого marker'а в комментариях/changelog), эта строка попадёт в release commit body → GitLab пропустит tag pipeline → нет images → manual escape через `glab api -X POST projects/367/pipeline?ref=vX.Y.Z`. **Реальный случай:** v2.16.1 (2026-05-12). Commit `fix(release): remove [skip ci] из semantic-release commit message` — literal `[skip ci]` в subject. Release notes v2.16.1 содержали эту строку → tag pipeline 6831 skipped. Pipeline пришлось trigger вручную. **Защита:** CI job `commit-msg-lint` (см. `.gitlab-ci.yml`) fail'ит любой commit с substring `[skip ci]` в subject или body. Запускается на MR pipeline'ах и feature-branch push'ах. **Если нужно упомянуть skip-ci в commit message:** - ✅ `fix: remove skip-ci marker` (дефис вместо пробела) - ✅ `docs: explain GitLab pipeline skip syntax` - ✅ `chore: remove the skip-ci directive from .releaserc` - ❌ `fix: remove [skip ci]` (literal — fails lint) - ❌ `update [skip ci] policy` (literal — fails lint) ## Manually trigger skipped tag pipeline Если tag pipeline всё-таки попал в trap (legacy commits до commit-msg-lint или CI bypass через --no-verify): ```bash GITLAB_HOST=git.nstart.cloud glab api -X POST "projects/367/pipeline?ref=vX.Y.Z" ``` Создаёт новый pipeline на той же tag-ref. Escape hatch, не regular workflow. ## Branch policy Релизы создаются ТОЛЬКО с `main`. Feature branches (`feat/*`, `fix/*`) собираются для staging но НЕ создают tag'и. Релиз = merge в main + успешный staging deploy + наличие релизных commits. ## Local dry-run Проверить что semantic-release решит сделать без push: ```bash cd ordinis-admin-ui pnpm install npx semantic-release --dry-run --no-ci ``` Покажет следующую версию + preview CHANGELOG entries. ## Rollback Если случайно зарелизили — удали tag: ```bash git tag -d v1.3.1 git push origin :refs/tags/v1.3.1 ``` GitLab Releases UI — удали release object отдельно.