remove commitlint configuration and update release documentation to enforce single scope in commit messages
CI / openapi (push) Has been cancelled
CI / web (push) Has been cancelled
CI / changes (push) Has been cancelled
CI / go (push) Has been cancelled
CI / bird2 (push) Has been cancelled
CI / commitlint (push) Has been cancelled
CI / release (push) Has been cancelled
CI / openapi (push) Has been cancelled
CI / web (push) Has been cancelled
CI / changes (push) Has been cancelled
CI / go (push) Has been cancelled
CI / bird2 (push) Has been cancelled
CI / commitlint (push) Has been cancelled
CI / release (push) Has been cancelled
This commit is contained in:
@@ -1,3 +0,0 @@
|
|||||||
{
|
|
||||||
"extends": ["@commitlint/config-conventional"]
|
|
||||||
}
|
|
||||||
@@ -142,6 +142,8 @@ feat(web): add module create dialog on /modules
|
|||||||
| `.cursor/` | `chore` |
|
| `.cursor/` | `chore` |
|
||||||
| прочее в корне | `chore` |
|
| прочее в корне | `chore` |
|
||||||
|
|
||||||
|
**Запрещено:** несколько scope через запятую (`refactor(web, httpapi): …`) — semantic-release не распознает `type`, релиз не будет (см. [docs/releasing.md](../../docs/releasing.md)).
|
||||||
|
|
||||||
`type` определять по **содержимому diff**, не только по пути.
|
`type` определять по **содержимому diff**, не только по пути.
|
||||||
|
|
||||||
## Multi-change
|
## Multi-change
|
||||||
|
|||||||
@@ -303,6 +303,8 @@ jobs:
|
|||||||
cache-dependency-path: package-lock.json
|
cache-dependency-path: package-lock.json
|
||||||
- name: Install release tooling
|
- name: Install release tooling
|
||||||
run: npm ci
|
run: npm ci
|
||||||
|
- name: Verify releasable commit messages
|
||||||
|
run: node scripts/commit/verify-release-commits.mjs
|
||||||
- name: Semantic release
|
- name: Semantic release
|
||||||
run: npx semantic-release
|
run: npx semantic-release
|
||||||
env:
|
env:
|
||||||
|
|||||||
@@ -0,0 +1,22 @@
|
|||||||
|
/** @type {import('@commitlint/types').UserConfig} */
|
||||||
|
module.exports = {
|
||||||
|
extends: ['@commitlint/config-conventional'],
|
||||||
|
plugins: [
|
||||||
|
{
|
||||||
|
rules: {
|
||||||
|
'scope-no-commas': ({ scope }) => {
|
||||||
|
if (scope && scope.includes(',')) {
|
||||||
|
return [
|
||||||
|
false,
|
||||||
|
'scope must not contain commas (semantic-release will not parse the commit type)'
|
||||||
|
];
|
||||||
|
}
|
||||||
|
return [true];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
],
|
||||||
|
rules: {
|
||||||
|
'scope-no-commas': [2, 'always']
|
||||||
|
}
|
||||||
|
};
|
||||||
@@ -11,6 +11,8 @@ EvoBGP использует [Conventional Commits](https://www.conventionalcommi
|
|||||||
| `feat!`, `fix!` или `BREAKING CHANGE:` в теле | major (1.0.0 → 2.0.0) |
|
| `feat!`, `fix!` или `BREAKING CHANGE:` в теле | major (1.0.0 → 2.0.0) |
|
||||||
| `docs`, `chore`, `test` | без релиза |
|
| `docs`, `chore`, `test` | без релиза |
|
||||||
|
|
||||||
|
**Scope:** один идентификатор **без запятых** (`web`, `httpapi`, `api`). Заголовок `refactor(a, b): …` **не парсится** semantic-release → релиз не создаётся (commitlint на PR это тоже отклонит). Подробнее — раздел «Scope и semantic-release» ниже.
|
||||||
|
|
||||||
`refactor` — patch без новых функций: перестройка кода/UI при том же поведении для пользователя. По semver на одном уровне с `fix`, но семантически «мельче» `feat` (не minor).
|
`refactor` — patch без новых функций: перестройка кода/UI при том же поведении для пользователя. По semver на одном уровне с `fix`, но семантически «мельче» `feat` (не minor).
|
||||||
|
|
||||||
Отдельного суффикса `1.x.y.fix` в semver нет: «fix» в Conventional Commits означает **patch** (третья цифра). Для починки пайплайна без смены продукта — `fix(ci):` или `ci:` (оба дают patch после настройки `.releaserc.json`).
|
Отдельного суффикса `1.x.y.fix` в semver нет: «fix» в Conventional Commits означает **patch** (третья цифра). Для починки пайплайна без смены продукта — `fix(ci):` или `ci:` (оба дают patch после настройки `.releaserc.json`).
|
||||||
@@ -62,6 +64,21 @@ API: `GET /version`, `GET /v1/version` — поля `version`, `git_sha`, `build
|
|||||||
|
|
||||||
Web UI показывает версию из API (footer sidebar, страница «Мониторинг»).
|
Web UI показывает версию из API (footer sidebar, страница «Мониторинг»).
|
||||||
|
|
||||||
|
## Scope и semantic-release
|
||||||
|
|
||||||
|
Парсер [conventional-commits-parser](https://github.com/conventional-changelog/conventional-changelog/tree/master/packages/conventional-commits-parser) (его использует semantic-release) **не понимает запятые в scope**:
|
||||||
|
|
||||||
|
| Заголовок | Парсится | Релиз |
|
||||||
|
|-----------|----------|-------|
|
||||||
|
| `refactor(web): fix layout` | да, `refactor` | patch |
|
||||||
|
| `refactor(NetworkOverviewTab, NetworkSpeakersCard): fix layout` | **нет**, `type: null` | **нет** |
|
||||||
|
|
||||||
|
Правило: **один scope** из таблицы в [.cursor/rules/conventional-commits.mdc](../.cursor/rules/conventional-commits.mdc) (`web`, `httpapi`, `api`, …).
|
||||||
|
|
||||||
|
На push в `main` job **release** запускает `scripts/commit/verify-release-commits.mjs` — при непарсящихся коммитах CI упадёт до semantic-release.
|
||||||
|
|
||||||
|
Если релиз «не создался», а CI зелёный: смотрите лог release — часто `No releasable commits`. Исправление: новый коммит с корректным заголовком (например `refactor(web): …`).
|
||||||
|
|
||||||
## CHANGELOG
|
## CHANGELOG
|
||||||
|
|
||||||
Release notes — в Gitea Release; файл `CHANGELOG.md` генерируется в CI и прикрепляется как asset, **не** попадает в git history.
|
Release notes — в Gitea Release; файл `CHANGELOG.md` генерируется в CI и прикрепляется как asset, **не** попадает в git history.
|
||||||
|
|||||||
@@ -0,0 +1,64 @@
|
|||||||
|
#!/usr/bin/env node
|
||||||
|
/**
|
||||||
|
* Warns about commits since the last tag that semantic-release cannot parse.
|
||||||
|
* Exit 0 always — semantic-release still decides release/no-op.
|
||||||
|
*/
|
||||||
|
import { execSync } from 'node:child_process';
|
||||||
|
import parser from 'conventional-commits-parser';
|
||||||
|
|
||||||
|
const RELEASABLE = new Set(['feat', 'fix', 'perf', 'ci', 'refactor']);
|
||||||
|
|
||||||
|
function lastTag() {
|
||||||
|
try {
|
||||||
|
return execSync('git describe --tags --abbrev=0', { encoding: 'utf8' }).trim();
|
||||||
|
} catch {
|
||||||
|
return '';
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function commitsSince(ref) {
|
||||||
|
const range = ref ? `${ref}..HEAD` : 'HEAD';
|
||||||
|
const out = execSync(`git log ${range} --format=%H%x09%s`, { encoding: 'utf8' }).trim();
|
||||||
|
if (!out) return [];
|
||||||
|
return out.split('\n').map((line) => {
|
||||||
|
const [hash, subject] = line.split('\t');
|
||||||
|
return { hash: hash.trim(), subject: subject.trim() };
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
const tag = lastTag();
|
||||||
|
const commits = commitsSince(tag);
|
||||||
|
const unparseable = [];
|
||||||
|
const releasable = [];
|
||||||
|
|
||||||
|
for (const { hash, subject } of commits) {
|
||||||
|
const parsed = parser.sync(subject);
|
||||||
|
if (!parsed.type) {
|
||||||
|
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 });
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (commits.length === 0) {
|
||||||
|
console.log(`No new commits since ${tag || 'initial'}.`);
|
||||||
|
process.exit(0);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (unparseable.length > 0) {
|
||||||
|
console.warn('::warning:: Commits not parseable by semantic-release (no version bump):');
|
||||||
|
for (const b of unparseable) {
|
||||||
|
console.warn(` ${b.hash} ${b.subject}`);
|
||||||
|
}
|
||||||
|
console.warn('Fix: single scope without commas, e.g. refactor(web): summary');
|
||||||
|
}
|
||||||
|
|
||||||
|
if (releasable.length > 0) {
|
||||||
|
console.log(`Releasable since ${tag}: ${releasable.length} commit(s).`);
|
||||||
|
} else {
|
||||||
|
console.warn('::warning:: No releasable commits since last tag — release job will no-op.');
|
||||||
|
}
|
||||||
|
|
||||||
|
process.exit(0);
|
||||||
Reference in New Issue
Block a user