ci: semantic-release automation for tag/changelog (Option D)
Закрывает эту тему окончательно. После merge в main CI анализирует conventional commits с последнего tag'а, бампит package.json + CHANGELOG.md и пушит git tag vX.Y.Z. Tag pipeline далее собирает images и ждёт manual gate на trigger-deploy-prod. Что добавил: - ordinis-admin-ui/.releaserc.json — config (commit-analyzer, release-notes, changelog, npm[no-publish], git, gitlab). branches: ['main']. Маппинг conventional-commit types к bump levels. - ordinis-admin-ui/package.json — devDeps: semantic-release@24.2 + 6 plugins. - .gitlab-ci.yml — new 'release' stage + release job: - needs: trigger-deploy-staging (release ТОЛЬКО после зелёного staging) - image: node:22-alpine + pnpm@9.15.4 - GL_TOKEN из CI variable GITLAB_TOKEN (semantic-release/gitlab требует именно GL_TOKEN env name) - allow_failure: true (нет релизных commits = OK, не блокирует pipeline) - RELEASE.md — operator docs: - One-time setup: создать Project Access Token + GITLAB_TOKEN CI var - Commit conventions table (feat→minor, fix→patch, breaking→major) - Skip release (use chore: type или [skip ci]) - Dry-run command для local validation Prod safety: trigger-deploy-prod остаётся manual (when: manual). Автоматизация ТОЛЬКО tag-создания, не deploy. Tag → CI runs → human кликает Deploy. ACTION REQUIRED от user (one-time): 1. GitLab → Settings → Access Tokens → создать Project Access Token scopes: write_repository + api, role: Maintainer 2. GitLab → Settings → CI/CD → Variables → GITLAB_TOKEN (Masked, Protected) Без этого release job будет fail'ить (но pipeline не покраснеет — allow_failure).
This commit is contained in:
@@ -11,6 +11,7 @@ stages:
|
||||
- build
|
||||
- publish
|
||||
- trigger-deploy
|
||||
- release
|
||||
|
||||
variables:
|
||||
MAVEN_OPTS: "-Dmaven.repo.local=$CI_PROJECT_DIR/.m2/repository -Dorg.slf4j.simpleLogger.log.org.apache.maven.cli.transfer.Slf4jMavenTransferListener=warn"
|
||||
@@ -366,3 +367,51 @@ trigger-deploy-prod:
|
||||
rules:
|
||||
- if: '$CI_COMMIT_TAG =~ /^v[0-9]+\.[0-9]+\.[0-9]+$/'
|
||||
when: manual
|
||||
|
||||
# =============================================================
|
||||
# Release automation — semantic-release.
|
||||
# =============================================================
|
||||
# Анализирует conventional commits с последнего tag'а, решает bump (patch/
|
||||
# minor/major), генерирует CHANGELOG.md, bump'ит package.json, создаёт git tag
|
||||
# vX.Y.Z и GitLab Release. Tag pipeline далее автоматически собирает images
|
||||
# и ждёт manual gate на trigger-deploy-prod.
|
||||
#
|
||||
# Запускается ТОЛЬКО на main, ТОЛЬКО после успешного staging deploy.
|
||||
# Если нет релизных commits (только chore/test/ci/docs/style) — exit 0 без
|
||||
# tag'а. Loop prevention: коммит `chore(release): vX.Y.Z [skip ci]` —
|
||||
# `[skip ci]` стандартный GitLab marker, новый pipeline не триггерится.
|
||||
#
|
||||
# Setup ONE-TIME (см. RELEASE.md):
|
||||
# 1. Settings → Access Tokens → create Project Access Token
|
||||
# - role: Maintainer
|
||||
# - scopes: write_repository, api
|
||||
# - expiry: 1 год (renew раз в год)
|
||||
# 2. Settings → CI/CD → Variables → add:
|
||||
# GITLAB_TOKEN = <token> (Masked, Protected)
|
||||
#
|
||||
# Tuning: чтобы НЕ релизить feat: автоматом — добавь в commit message
|
||||
# `[skip release]` и semantic-release пропустит этот commit (см. parserOpts).
|
||||
release:
|
||||
stage: release
|
||||
image: node:22-alpine
|
||||
needs:
|
||||
- job: trigger-deploy-staging
|
||||
artifacts: false
|
||||
rules:
|
||||
- if: '$CI_COMMIT_BRANCH == "main"'
|
||||
when: on_success
|
||||
variables:
|
||||
# semantic-release требует full git history для commit analysis.
|
||||
GIT_DEPTH: "0"
|
||||
# GL_TOKEN — @semantic-release/gitlab plugin requires this name specifically.
|
||||
GL_TOKEN: $GITLAB_TOKEN
|
||||
before_script:
|
||||
- apk add --no-cache git ca-certificates
|
||||
- npm install -g --silent pnpm@9.15.4
|
||||
- cd ordinis-admin-ui
|
||||
- pnpm install --frozen-lockfile --silent
|
||||
script:
|
||||
- npx semantic-release
|
||||
# Не блокирует следующие pipelines если release fail'нул (e.g. нет
|
||||
# релизных commits). Manual investigation если нужно.
|
||||
allow_failure: true
|
||||
|
||||
+99
@@ -0,0 +1,99 @@
|
||||
# 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` будет работать.
|
||||
|
||||
## 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.
|
||||
```
|
||||
|
||||
## Skip release for specific commit
|
||||
|
||||
Если хочешь смержить `feat:` или `fix:` но **не** делать release сейчас —
|
||||
добавь `[skip ci]` в commit message ИЛИ используй non-release type
|
||||
(`chore:`, `docs:`, etc).
|
||||
|
||||
```
|
||||
chore(deps): bump axios 1.7 -> 1.8
|
||||
```
|
||||
|
||||
## Loop prevention
|
||||
|
||||
Release job создаёт commit `chore(release): vX.Y.Z [skip ci]`. `[skip ci]` —
|
||||
стандартный GitLab marker, новый pipeline не триггерится. Версия package.json
|
||||
+ CHANGELOG.md обновляются в этом коммите.
|
||||
|
||||
## 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 отдельно.
|
||||
@@ -0,0 +1,77 @@
|
||||
{
|
||||
"branches": ["main"],
|
||||
"tagFormat": "v${version}",
|
||||
"plugins": [
|
||||
[
|
||||
"@semantic-release/commit-analyzer",
|
||||
{
|
||||
"preset": "conventionalcommits",
|
||||
"releaseRules": [
|
||||
{ "type": "feat", "release": "minor" },
|
||||
{ "type": "fix", "release": "patch" },
|
||||
{ "type": "perf", "release": "patch" },
|
||||
{ "type": "refactor", "release": "patch" },
|
||||
{ "type": "revert", "release": "patch" },
|
||||
{ "type": "docs", "release": false },
|
||||
{ "type": "style", "release": false },
|
||||
{ "type": "chore", "release": false },
|
||||
{ "type": "test", "release": false },
|
||||
{ "type": "build", "release": false },
|
||||
{ "type": "ci", "release": false },
|
||||
{ "breaking": true, "release": "major" }
|
||||
],
|
||||
"parserOpts": {
|
||||
"noteKeywords": ["BREAKING CHANGE", "BREAKING CHANGES", "BREAKING"]
|
||||
}
|
||||
}
|
||||
],
|
||||
[
|
||||
"@semantic-release/release-notes-generator",
|
||||
{
|
||||
"preset": "conventionalcommits",
|
||||
"presetConfig": {
|
||||
"types": [
|
||||
{ "type": "feat", "section": "✨ Features" },
|
||||
{ "type": "fix", "section": "🐛 Fixes" },
|
||||
{ "type": "perf", "section": "⚡ Performance" },
|
||||
{ "type": "refactor", "section": "♻️ Refactoring" },
|
||||
{ "type": "revert", "section": "⏪ Reverts" },
|
||||
{ "type": "docs", "section": "📝 Docs", "hidden": false },
|
||||
{ "type": "chore", "hidden": true },
|
||||
{ "type": "test", "hidden": true },
|
||||
{ "type": "ci", "hidden": true },
|
||||
{ "type": "build", "hidden": true },
|
||||
{ "type": "style", "hidden": true }
|
||||
]
|
||||
}
|
||||
}
|
||||
],
|
||||
[
|
||||
"@semantic-release/changelog",
|
||||
{
|
||||
"changelogFile": "CHANGELOG.md",
|
||||
"changelogTitle": "# Ordinis Release Notes\n\nAuto-generated by semantic-release based on conventional commits.\nSee `.releaserc.json` for release rules."
|
||||
}
|
||||
],
|
||||
[
|
||||
"@semantic-release/npm",
|
||||
{
|
||||
"npmPublish": false,
|
||||
"tarballDir": false
|
||||
}
|
||||
],
|
||||
[
|
||||
"@semantic-release/git",
|
||||
{
|
||||
"assets": ["package.json", "CHANGELOG.md"],
|
||||
"message": "chore(release): v${nextRelease.version} [skip ci]\n\n${nextRelease.notes}"
|
||||
}
|
||||
],
|
||||
[
|
||||
"@semantic-release/gitlab",
|
||||
{
|
||||
"gitlabUrl": "https://git.nstart.cloud"
|
||||
}
|
||||
]
|
||||
]
|
||||
}
|
||||
@@ -48,6 +48,12 @@
|
||||
"tw-animate-css": "^1.4.0"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@semantic-release/changelog": "^6.0.3",
|
||||
"@semantic-release/commit-analyzer": "^13.0.0",
|
||||
"@semantic-release/git": "^10.0.1",
|
||||
"@semantic-release/gitlab": "^13.2.4",
|
||||
"@semantic-release/npm": "^12.0.1",
|
||||
"@semantic-release/release-notes-generator": "^14.0.1",
|
||||
"@tailwindcss/vite": "^4.0.0-beta.7",
|
||||
"@tanstack/router-vite-plugin": "^1.86.0",
|
||||
"@testing-library/jest-dom": "^6.9.1",
|
||||
@@ -59,6 +65,7 @@
|
||||
"@types/react-dom": "^19.0.0",
|
||||
"@vitejs/plugin-react": "^4.3.4",
|
||||
"jsdom": "^29.1.1",
|
||||
"semantic-release": "^24.2.1",
|
||||
"tailwindcss": "^4.0.0-beta.7",
|
||||
"typescript": "^5.7.2",
|
||||
"vite": "^6.0.5",
|
||||
|
||||
Generated
+2465
File diff suppressed because it is too large
Load Diff
Reference in New Issue
Block a user