Files
Zimin A.N. b76997e170 ci: bump infra image.tag прямым коммитом в main вместо auto-MR
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 больше не нужен.
2026-05-21 19:25:58 +03:00

189 lines
7.9 KiB
Markdown
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# 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 отдельно.