b76997e170
propose-prod-image-bump открывал MR в ordinis-infra на каждый release. MR никто не мерджил — накопилось 5+ открытых, image.tag в values отставал на несколько релизов, и infra deploy-prod (helm upgrade -f values-prod.yaml) откатывал prod на старый образ. Переименован в commit-prod-image-bump: коммитит bump image.tag в values-prod.yaml + values-staging.yaml напрямую в infra main с rebase-retry. Убран промежуточный MR и 405-квирк при merge вечно-manual инфра-пайплайна. Токену нужна роль Maintainer для push в protected main (было Developer); api-scope больше не нужен.
189 lines
7.9 KiB
Markdown
189 lines
7.9 KiB
Markdown
# 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*-*-*-<sha>` не собирались → `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 отдельно.
|