ci(hooks): добавить commit-msg и шаблон Conventional Commits
quality / commitlint (push) Skipped
CD / update-wiki (push) Successful in 6s
quality / changes (push) Successful in 4s
quality / docker-check (push) Skipped
quality / web (push) Successful in 57s
quality / api (push) Successful in 41s
CD / quality (push) Successful in 1m46s
CD / publish (push) Successful in 11m48s
quality / commitlint (push) Skipped
CD / update-wiki (push) Successful in 6s
quality / changes (push) Successful in 4s
quality / docker-check (push) Skipped
quality / web (push) Successful in 57s
quality / api (push) Successful in 41s
CD / quality (push) Successful in 1m46s
CD / publish (push) Successful in 11m48s
Без type/scope semantic-release не ставил тег и пропускал bake.
This commit is contained in:
@@ -1,36 +1,50 @@
|
||||
---
|
||||
description: Conventional commits на русском языке
|
||||
description: Conventional commits на русском — обязательно для semantic-release
|
||||
globs: "**/*"
|
||||
alwaysApply: false
|
||||
alwaysApply: true
|
||||
---
|
||||
|
||||
# Commit messages (русский)
|
||||
|
||||
Формат: `<type>[optional scope]: <описание>`
|
||||
Формат: `тип(scope): описание на русском`
|
||||
|
||||
semantic-release на push в `main` создаёт тег и публикует Docker **только** если среди коммитов с прошлого тега есть `feat` / `fix` / `perf` / `ci` / `refactor` (или BREAKING).
|
||||
Сообщения вроде `Init Commit`, `Update CDN Manager: …`, `Enhance …` **не парсятся** → CD пишет `No releasable commits — skipping image publish`.
|
||||
|
||||
## Типы
|
||||
|
||||
- `feat` — только новая UX-фича для пользователя
|
||||
- `fix` — исправление бага
|
||||
- `chore` — конфиг, зависимости, правила, CI
|
||||
- `refactor` — рефакторинг без изменения поведения
|
||||
- `docs` — документация
|
||||
- `test` — тесты
|
||||
- `perf` — производительность
|
||||
- `fix` — исправление бага / регрессии / вёрстки
|
||||
- `chore` / `docs` / `build` — конфиг, правила, документация (**без** релиза)
|
||||
- `ci` — CI/CD, hooks (patch-релиз)
|
||||
- `refactor` — рефакторинг без смены UX (patch)
|
||||
- `test` / `style` / `perf` — как обычно
|
||||
|
||||
## Правила
|
||||
|
||||
- Префикс `type` и `scope` — **английский**; текст после `:` — **только русский** (кириллица)
|
||||
- Subject в **императиве**, без точки в конце
|
||||
- Subject и body — **на русском**
|
||||
- Body (опционально) — что и зачем, не как
|
||||
- Scope в скобках при необходимости: `feat(domains): добавить фильтр по статусу`
|
||||
- Scope — **один** идентификатор **без запятых**: `web`, `api`, `ci`, `docker`, `fleet`
|
||||
- Локальный hook `.husky/commit-msg` гоняет `commitlint`
|
||||
|
||||
## Примеры
|
||||
|
||||
```
|
||||
fix(frontend): заменить raw table на shadcn Table на странице доменов
|
||||
feat(fleet): добавить CRUD нод и алиасов
|
||||
|
||||
feat(certificates): добавить предупреждение об истечении срока
|
||||
fix(web): выровнять AttentionQueue на дашборде
|
||||
|
||||
chore(rules): консолидировать правила shadcn/ui для Cursor
|
||||
ci(hooks): добавить commit-msg для Conventional Commits
|
||||
|
||||
chore(docs): описать Traefik deploy
|
||||
```
|
||||
|
||||
## Запрещено
|
||||
|
||||
```
|
||||
Init Commit
|
||||
Update CDN Manager: Add …
|
||||
Enhance CDN Manager: …
|
||||
Refactor: Remove …
|
||||
feat: add nodes
|
||||
```
|
||||
|
||||
+12
@@ -0,0 +1,12 @@
|
||||
# Conventional Commits — шаблон для git commit -t / commit.template
|
||||
#
|
||||
# Формат: type(scope): описание на русском
|
||||
# Примеры:
|
||||
# feat(fleet): добавить CRUD нод и алиасов
|
||||
# fix(web): исправить фильтр синхронизации
|
||||
# ci(hooks): добавить commit-msg для commitlint
|
||||
#
|
||||
# Releasable (semantic-release → Docker): feat | fix | perf | ci | refactor
|
||||
# Без релиза: docs | chore | test | style | build
|
||||
#
|
||||
# НЕ использовать: Init Commit | Update … | Enhance … | английский subject
|
||||
@@ -0,0 +1,3 @@
|
||||
#!/usr/bin/env sh
|
||||
# Enforce Conventional Commits (semantic-release + commitlint).
|
||||
pnpm exec commitlint --edit "$1"
|
||||
@@ -76,3 +76,5 @@ GET /api/v1/cloudflare/zones
|
||||
Gitea: `.gitea/workflows/{ci,cd,quality}.yaml`. Образы: `deploy/docker` (`cdnmanager` + alias `cdn-manager`).
|
||||
Prod: [`docs/deploy-traefik.md`](docs/deploy-traefik.md) — `deploy/docker-compose.traefik.yml` + `deploy/env.traefik.example`.
|
||||
Без Traefik: [`docs/deploy-docker.md`](docs/deploy-docker.md) · Compose: `docker-compose.yml` / `deploy/compose/docker-compose.example.yaml`.
|
||||
|
||||
**Коммиты:** только Conventional Commits (`feat`/`fix`/… + русский subject). Иначе CD: `No releasable commits — skipping image publish`. См. [`.cursor/rules/commit-messages-ru.mdc`](.cursor/rules/commit-messages-ru.mdc), [`docs/releasing.md`](docs/releasing.md).
|
||||
|
||||
+15
-2
@@ -10,9 +10,22 @@
|
||||
|
||||
## Commits
|
||||
|
||||
Format: `type(scope): description`
|
||||
**Обязательно** Conventional Commits — иначе semantic-release на `main` не создаст тег и **не опубликует Docker-образ**.
|
||||
|
||||
Types: `feat`, `fix`, `docs`, `style`, `refactor`, `test`, `chore`
|
||||
Формат: `type(scope): описание на русском`
|
||||
|
||||
| Type | Релиз |
|
||||
|------|--------|
|
||||
| `feat` | minor |
|
||||
| `fix`, `perf`, `ci`, `refactor` | patch |
|
||||
| `docs`, `chore`, `test`, `style`, `build` | нет |
|
||||
|
||||
Scope — один идентификатор без запятых (`web`, `api`, `ci`, `docker`, `fleet`).
|
||||
Локально: husky `.husky/commit-msg` → `commitlint`. Шаблон: [`.gitmessage`](.gitmessage) (`git config commit.template .gitmessage`).
|
||||
Правила Cursor: [`.cursor/rules/commit-messages-ru.mdc`](.cursor/rules/commit-messages-ru.mdc).
|
||||
|
||||
Плохо: `Init Commit`, `Update CDN Manager: Add…`.
|
||||
Хорошо: `feat(fleet): добавить CRUD нод и алиасов`.
|
||||
|
||||
## Pull requests
|
||||
|
||||
|
||||
+33
-6
@@ -8,17 +8,44 @@ module.exports = {
|
||||
if (scope && scope.includes(',')) {
|
||||
return [
|
||||
false,
|
||||
'scope must not contain commas (semantic-release will not parse the commit type)'
|
||||
'scope must not contain commas (semantic-release will not parse the commit type)',
|
||||
];
|
||||
}
|
||||
return [true];
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
/** Subject after «:» must contain Cyrillic (project convention). */
|
||||
'subject-cyrillic': ({ subject }) => {
|
||||
if (!subject) return [true];
|
||||
if (/[а-яёА-ЯЁ]/.test(subject)) return [true];
|
||||
return [
|
||||
false,
|
||||
'subject after «:» must be in Russian (Cyrillic), e.g. feat(web): добавить экран нод',
|
||||
];
|
||||
},
|
||||
},
|
||||
},
|
||||
],
|
||||
rules: {
|
||||
// Subject после «:» на русском — не английский sentence-case.
|
||||
'subject-case': [0],
|
||||
'scope-no-commas': [2, 'always']
|
||||
}
|
||||
'scope-no-commas': [2, 'always'],
|
||||
'subject-cyrillic': [2, 'always'],
|
||||
'type-enum': [
|
||||
2,
|
||||
'always',
|
||||
[
|
||||
'feat',
|
||||
'fix',
|
||||
'perf',
|
||||
'ci',
|
||||
'refactor',
|
||||
'docs',
|
||||
'chore',
|
||||
'test',
|
||||
'style',
|
||||
'build',
|
||||
'revert',
|
||||
],
|
||||
],
|
||||
},
|
||||
};
|
||||
|
||||
+9
-1
@@ -63,7 +63,15 @@ Semver из git-тега пробрасывается в образ как `APP_
|
||||
|
||||
На push в `main` job **publish** запускает `scripts/commit/verify-release-commits.mjs` — в логе будут предупреждения о непарсящихся коммитах.
|
||||
|
||||
Если релиз «не создался», а CI зелёный: смотрите лог release — часто `No releasable commits`. Исправление: новый коммит с корректным заголовком (например `fix(web): …`).
|
||||
Если релиз «не создался», а CI зелёный: смотрите лог **Detect new release** / **Verify releasable** — часто `No releasable commits`.
|
||||
|
||||
Причины:
|
||||
|
||||
1. Subject не Conventional Commits (`Update CDN Manager: …`, `Init Commit`) — parser не видит `type`.
|
||||
2. Только `docs` / `chore` / `test` с прошлого тега — намеренный no-op.
|
||||
3. Scope с запятыми: `refactor(a, b): …` — type не парсится.
|
||||
|
||||
Исправление: новый коммит с корректным заголовком (например `fix(web): …` или `feat(fleet): …`) и push в `main`. Локально husky `commit-msg` + commitlint; шаблон [`.gitmessage`](../.gitmessage).
|
||||
|
||||
## Перезапуск упавшего job publish
|
||||
|
||||
|
||||
@@ -45,6 +45,11 @@ for (const { hash, subject } of commits) {
|
||||
unparseable.push({ hash: hash.slice(0, 7), subject });
|
||||
continue;
|
||||
}
|
||||
// semantic-release expects lowercase types (feat|fix|…); "Refactor: …" is not releasable.
|
||||
if (parsed.type !== parsed.type.toLowerCase()) {
|
||||
unparseable.push({ hash: hash.slice(0, 7), subject });
|
||||
continue;
|
||||
}
|
||||
if (RELEASABLE.has(parsed.type)) {
|
||||
releasable.push({ hash: hash.slice(0, 7), subject, type: parsed.type });
|
||||
}
|
||||
@@ -64,9 +69,20 @@ if (unparseable.length > 0) {
|
||||
}
|
||||
|
||||
if (releasable.length > 0) {
|
||||
console.log(`Releasable since ${tag}: ${releasable.length} commit(s).`);
|
||||
console.log(`Releasable since ${tag || 'initial'}: ${releasable.length} commit(s).`);
|
||||
for (const r of releasable) {
|
||||
console.log(` ${r.hash} ${r.type} ${r.subject}`);
|
||||
}
|
||||
} else {
|
||||
console.warn('::warning:: No releasable commits since last tag — release job will no-op.');
|
||||
console.warn(
|
||||
'Need at least one: feat|fix|perf|ci|refactor with Conventional Commits, e.g. feat(fleet): …',
|
||||
);
|
||||
if (!tag) {
|
||||
console.warn(
|
||||
'::warning:: No git tags yet — first release (v1.0.0) also requires a releasable commit on HEAD history.',
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
process.exit(0);
|
||||
|
||||
Reference in New Issue
Block a user