Files
mdm-ordinis/RELEASE.md
T
Zimin A.N. 1da515276c ci(lint): forbid literal skip-ci marker в commit messages
GitLab платформа skip'ает pipeline на любом commit'е чей subject или body
содержит pattern с square brackets вокруг 'skip ci'. Trap: semantic-release
включает release notes из всех commit'ов в release commit body — если хотя
бы один commit имеет этот pattern в subject/body, release tag pipeline
скипается → нет images → нет auto-PR.

Реальный случай: v2.16.1 — commit fix(release): 'remove ... из semantic-
release commit message' содержал паттерн дословно. Pipeline 6831 (v2.16.1
tag) skipped → пришлось trigger через API.

Новый CI job 'commit-msg-lint' (stage: test):
- Запускается на MR pipeline'ах и feature-branch push'ах
- Скипает chore(release) commit'ы (semantic-release auto-bot)
- Для каждого commit'а в range проверяет subject+body на square-bracket
  skip-ci pattern
- Fails CI с ясной подсказкой (как переформулировать) при match

RELEASE.md обновлён с разделом 'Skip-ci marker trap': примеры
правильных формулировок (skip-ci marker, the directive, etc) и
escape hatch (manual API trigger).
2026-05-13 00:54:19 +03:00

179 lines
7.2 KiB
Markdown
Raw 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-PR в ordinis-infra (опционально)
`propose-prod-image-bump` job открывает MR в `ordinis-infra` после каждого
release tag — bump'ит `image.tag` в `values-prod.yaml` на свежий immutable
SHA tag (e.g. `v2-16-0-abc12345`). Чинит historical bug когда manual deploys
через infra repo получали `:latest` (stale cache → split-brain pods).
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: `Developer`
- Scopes: `write_repository`, `api`
- Expiry: 1 год
4. **Add CI variable в ordinis project (367)**:
- Key: `INFRA_PROJECT_TOKEN`
- Value: (token из шага 3)
- Flags: `Masked` ✅, `Protected`
Без этой переменной job exits early (`allow_failure: true`), tag pipeline
не блокируется. Manual MR в `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>` не собирались → `propose-prod-image-bump`
> auto-PR не запускался → manual ручной MR в 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 отдельно.