chore: adopt linear git model without release-to-main branch (#34) #35
@@ -42,8 +42,15 @@ Issue и PR выбирай `hermes-agent`; незанятую задачу не
|
|||||||
`refactor`, docs → `docs`, test → `test`, ci → `ci`, chore → `chore`,
|
`refactor`, docs → `docs`, test → `test`, ci → `ci`, chore → `chore`,
|
||||||
performance → `perf`. Номер обязателен для новых рабочих веток; короткий
|
performance → `perf`. Номер обязателен для новых рабочих веток; короткий
|
||||||
английский slug в kebab-case. Ветки `main`/`develop` не место для прямой
|
английский slug в kebab-case. Ветки `main`/`develop` не место для прямой
|
||||||
разработки. Release/hotfix, обратная синхронизация, Renovate и исторические
|
разработки. Release, Renovate и исторические ветки подчиняются существующему
|
||||||
ветки подчиняются существующему release/automation процессу, а не переименовываются.
|
release/automation процессу, а не переименовываются.
|
||||||
|
|
||||||
|
Рабочий PR сливается squash или rebase-merge; merge-коммит не используется.
|
||||||
|
`main` обновляется только fast-forward из `develop`, поэтому `origin/main`
|
||||||
|
всегда предок `origin/develop`. Срочный fix — обычная ветка `fix/*` от
|
||||||
|
`develop`; отдельного hotfix-пути от `main` в обычном процессе нет.
|
||||||
|
Merge policy, версии, теги и исключения принадлежат
|
||||||
|
[процессу релиза](../../README.md#релизы-и-ветки).
|
||||||
|
|
||||||
## Commit Conventions
|
## Commit Conventions
|
||||||
|
|
||||||
@@ -140,17 +147,19 @@ Frontend/backend запускают их как jobs в едином `verify.yml
|
|||||||
## Exceptions and Recovery
|
## Exceptions and Recovery
|
||||||
|
|
||||||
Срочный hotfix: немедленно завести/найти Issue, затем исправление в
|
Срочный hotfix: немедленно завести/найти Issue, затем исправление в
|
||||||
`release/hotfix-<name>` от актуального `main` по существующему release process;
|
обычной ветке `fix/<issue>-<slug>` от актуального `develop`, PR в `develop`
|
||||||
проверки и PR в `main`, затем вернуть результат в `develop`. Связать Issue
|
и релиз по существующему процессу. Отдельного пути от `main` нет: он сделал бы
|
||||||
в PR и сопровождающем коммите; специальная release-ветка исключена из обычного
|
`main` не-предком `develop`. Если `develop` содержит незаконченную работу,
|
||||||
шаблона имён. Не отменять Issue или проверки из-за срочности.
|
быстрого фикса нет; обход требует явного решения и описан в
|
||||||
|
[процессе релиза](../../README.md#ограничения-модели). Связать Issue
|
||||||
|
в PR и сопровождающем коммите. Не отменять Issue или проверки из-за срочности.
|
||||||
Если CI красный или PR устарел — исправить в той же ветке и перепроверить
|
Если CI красный или PR устарел — исправить в той же ветке и перепроверить
|
||||||
актуальный HEAD; если AC изменились — обновить Issue и при SDD вернуть change
|
актуальный HEAD; если AC изменились — обновить Issue и при SDD вернуть change
|
||||||
к затронутому approval gate. При недоступной Gitea продолжать только анализ.
|
к затронутому approval gate. При недоступной Gitea продолжать только анализ.
|
||||||
Изменение PR title после зелёного CI: проверить, что check пересчитан;
|
Изменение PR title после зелёного CI: проверить, что check пересчитан;
|
||||||
если `edited` trigger у существующих checks отсутствует, reviewer проверяет
|
если `edited` trigger у существующих checks отсутствует, reviewer проверяет
|
||||||
текущий title вручную. Новые правила не применяются к историческим коммитам
|
текущий title вручную. Новые правила не применяются к историческим коммитам
|
||||||
и автоматической обратной синхронизации релиза.
|
и merge-коммитам, уже находящимся в истории.
|
||||||
|
|
||||||
Примеры: фича с новым поведением — Issue → согласованный OpenSpec change →
|
Примеры: фича с новым поведением — Issue → согласованный OpenSpec change →
|
||||||
ветки Apply → PR каждого affected repo → archive → закрытие Issue. Локальный
|
ветки Apply → PR каждого affected repo → archive → закрытие Issue. Локальный
|
||||||
@@ -158,5 +167,5 @@ bugfix — Issue → `fix/124-invalid-balance` → регрессия → PR →
|
|||||||
Cross-repo контракт — одна Issue с общими AC, `backend` и `frontend` PR с
|
Cross-repo контракт — одна Issue с общими AC, `backend` и `frontend` PR с
|
||||||
`WealthLedger/main#27` и взаимными ссылками; если независимые acceptance/owners,
|
`WealthLedger/main#27` и взаимными ссылками; если независимые acceptance/owners,
|
||||||
отдельные связанные Issues. Рефакторинг без изменения поведения — lightweight
|
отдельные связанные Issues. Рефакторинг без изменения поведения — lightweight
|
||||||
`refactor/125-simplify-api-client`. Hotfix — Issue → `release/hotfix-*` от
|
`refactor/125-simplify-api-client`. Hotfix — Issue → `fix/*` от `develop` →
|
||||||
`main` → PR/release → back-merge в `develop` → закрытие.
|
PR в `develop` → релиз → закрытие.
|
||||||
|
|||||||
@@ -16,7 +16,11 @@
|
|||||||
- Не выполняй `git clean -fdx`, reset чужих изменений, force-push, удаление или
|
- Не выполняй `git clean -fdx`, reset чужих изменений, force-push, удаление или
|
||||||
перенос опубликованных тегов как обычную очистку. Для опасной операции нужны
|
перенос опубликованных тегов как обычную очистку. Для опасной операции нужны
|
||||||
явная необходимость, сохранение данных и соответствующая авторизация.
|
явная необходимость, сохранение данных и соответствующая авторизация.
|
||||||
- Обычный PR допускает squash. Release/hotfix, синхронизация main/develop,
|
- Обычный PR сливается squash или rebase-merge; merge-коммит не используется.
|
||||||
release-конфигурация требуют чтения
|
`main` обновляется только fast-forward из `develop`, поэтому
|
||||||
|
`origin/main` всегда предок `origin/develop`. Проверка:
|
||||||
|
`git merge-base --is-ancestor origin/main origin/develop`.
|
||||||
|
- Release, hotfix и release-конфигурация требуют чтения
|
||||||
[процесса релиза](../../README.md#релизы-и-ветки): он владеет merge policy,
|
[процесса релиза](../../README.md#релизы-и-ветки): он владеет merge policy,
|
||||||
версиями и тегами. Прямой push в `main` запрещён; версии вручную не повышай.
|
версиями и тегами. Прямой push в `main` запрещён; версии вручную не повышай.
|
||||||
|
Отдельной синхронизации `main → develop` в обычном процессе нет.
|
||||||
|
|||||||
@@ -160,8 +160,24 @@ T-Bank. Полные команды и ограничения принадлеж
|
|||||||
`main` хранит принятые релизы, `develop` — интеграцию разработки. Новая задача:
|
`main` хранит принятые релизы, `develop` — интеграцию разработки. Новая задача:
|
||||||
отдельная [issue-linked ветка](.ai/conventions/engineering-workflow.md#git-branching)
|
отдельная [issue-linked ветка](.ai/conventions/engineering-workflow.md#git-branching)
|
||||||
от свежего `develop`, затем PR в `develop`.
|
от свежего `develop`, затем PR в `develop`.
|
||||||
В `main` напрямую не пушить. Обычные PR можно squash-merge; релизные PR и
|
В `main` напрямую не пушить. Рабочие PR сливать squash или rebase-merge;
|
||||||
обратную синхронизацию `main` с `develop` сливать с сохранением истории.
|
merge-коммиты запрещены. `main` получает изменения только fast-forward из
|
||||||
|
`develop`, поэтому история обеих веток линейна:
|
||||||
|
|
||||||
|
```text
|
||||||
|
<type>/<issue>-<slug> --squash--> develop --ff-only--> main --> tag vX.Y.Z
|
||||||
|
```
|
||||||
|
|
||||||
|
Инвариант: `origin/main` всегда предок `origin/develop`. Проверка в любом repo:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
git fetch -q origin
|
||||||
|
git merge-base --is-ancestor origin/main origin/develop && echo ok || echo BROKEN
|
||||||
|
```
|
||||||
|
|
||||||
|
Инвариант нарушается только осознанным исключением
|
||||||
|
([Ограничения модели](#ограничения-модели)); после него `develop` возвращают
|
||||||
|
в согласованное состояние до следующего релиза.
|
||||||
|
|
||||||
Commits и заголовки PR — Conventional Commits. `feat:` повышает minor,
|
Commits и заголовки PR — Conventional Commits. `feat:` повышает minor,
|
||||||
`fix:`/`perf:` — patch, `!` или `BREAKING CHANGE:` — major, в том числе до 1.0.
|
`fix:`/`perf:` — patch, `!` или `BREAKING CHANGE:` — major, в том числе до 1.0.
|
||||||
@@ -174,8 +190,8 @@ Commits и заголовки PR — Conventional Commits. `feat:` повыша
|
|||||||
### Подготовка релиза
|
### Подготовка релиза
|
||||||
|
|
||||||
1. Обновить `main`, `develop` и теги командой `git fetch origin --tags`.
|
1. Обновить `main`, `develop` и теги командой `git fetch origin --tags`.
|
||||||
Проверить, что текущий `develop` содержит последний релиз из `main`.
|
Проверить инвариант. Создать `release/X.Y.Z` от проверенного `develop`
|
||||||
Создать `release/<name>` от проверенного `develop` и выполнить `npm ci`.
|
и выполнить `npm ci`.
|
||||||
2. Проверить локальные входы и публичные artifacts выпускаемого репозитория.
|
2. Проверить локальные входы и публичные artifacts выпускаемого репозитория.
|
||||||
Совместимость развёрнутых участников согласовать отдельно через публичные
|
Совместимость развёрнутых участников согласовать отдельно через публичные
|
||||||
контракты; CI не скачивает соседние checkout и не требует их SHA.
|
контракты; CI не скачивает соседние checkout и не требует их SHA.
|
||||||
@@ -186,19 +202,20 @@ Commits и заголовки PR — Conventional Commits. `feat:` повыша
|
|||||||
наличие коммитов. Теги и соответствие версии последнему принятому тегу
|
наличие коммитов. Теги и соответствие версии последнему принятому тегу
|
||||||
проверяются вручную; уже подготовленную версию повторно не повышать.
|
проверяются вручную; уже подготовленную версию повторно не повышать.
|
||||||
4. Проверить diff релизного коммита и штатные gates. Отправить только релизную
|
4. Проверить diff релизного коммита и штатные gates. Отправить только релизную
|
||||||
ветку: `git push -u origin HEAD`. Открыть PR в `main` с заголовком
|
ветку: `git push -u origin HEAD`. Открыть PR в **`develop`** с заголовком
|
||||||
`chore(release): X.Y.Z`. После зелёных обязательных проверок (`commits` в root;
|
`chore(release): X.Y.Z`. После зелёных обязательных проверок (`commits` в root;
|
||||||
`verify`, `security`, `commits` в остальных repo)
|
`verify`, `security`, `commits` в остальных repo)
|
||||||
выполнить **fast-forward-only merge**. Если `main` изменился, сначала
|
выполнить **fast-forward-only merge**. Если `develop` изменился, обновить
|
||||||
согласовать историю и повторить проверку кандидата; не обходить защиту.
|
релизную ветку и повторить проверку кандидата; не обходить защиту.
|
||||||
5. После merge получить `main` и убедиться, что его HEAD — проверенный релизный
|
5. Открыть PR `develop → main` с тем же заголовком и слить
|
||||||
коммит, версия и lockfile совпадают. На этом SHA создать аннотированный тег:
|
**fast-forward-only**. `main` получает ровно проверенный релизный коммит;
|
||||||
|
merge-коммит здесь невозможен по определению ff.
|
||||||
|
6. Получить `main` и убедиться, что его HEAD — проверенный релизный коммит,
|
||||||
|
версия и lockfile совпадают. На этом SHA создать аннотированный тег:
|
||||||
`git tag -a vX.Y.Z <release-sha> -m "Release X.Y.Z"`, затем отправить
|
`git tag -a vX.Y.Z <release-sha> -m "Release X.Y.Z"`, затем отправить
|
||||||
только этот тег: `git push origin refs/tags/vX.Y.Z`. Повторно release-it
|
только этот тег: `git push origin refs/tags/vX.Y.Z`. Повторно release-it
|
||||||
на `main` не запускать: он уже подготовил версию.
|
на `main` не запускать: он уже подготовил версию. Обратная синхронизация
|
||||||
6. Через PR `main → develop` вернуть релизный коммит в разработку без squash
|
не нужна: релизный коммит попал в `develop` на шаге 4.
|
||||||
и rebase. Убедиться, что `develop` содержит тег. После первого выпуска
|
|
||||||
`develop` создаётся непосредственно от релизного SHA.
|
|
||||||
|
|
||||||
Перед публикацией тегов сравнить local SHA с удалённым `main`; PR и проверки
|
Перед публикацией тегов сравнить local SHA с удалённым `main`; PR и проверки
|
||||||
должны относиться к этому кандидату. Опубликованные теги не перемещать.
|
должны относиться к этому кандидату. Опубликованные теги не перемещать.
|
||||||
@@ -212,11 +229,20 @@ Commits и заголовки PR — Conventional Commits. `feat:` повыша
|
|||||||
Исторические неконвенциональные commits не переписываются; первый changelog
|
Исторические неконвенциональные commits не переписываются; первый changelog
|
||||||
требует просмотра человеком. Дальнейшие версии выбираются по commits.
|
требует просмотра человеком. Дальнейшие версии выбираются по commits.
|
||||||
|
|
||||||
### Hotfix, проверки и защита
|
### Ограничения модели
|
||||||
|
|
||||||
Срочный fix начинается от `main`, проходит проверки и релизный PR из
|
Freeze релиза не поддерживается: кандидат нельзя заморозить и параллельно
|
||||||
`release/hotfix-<name>` в `main`, затем обязательно возвращается в `develop`.
|
принимать работу в `develop`. Пока открыт релизный PR, `develop` не двигают.
|
||||||
Если ломается публичный контракт, major сохраняется и для hotfix.
|
|
||||||
|
Срочный fix — обычная ветка `fix/<issue>-<slug>` от `develop` с последующим
|
||||||
|
релизом. Отдельного пути от `main` нет: он сделал бы `main` не-предком
|
||||||
|
`develop` и заблокировал следующий ff. Если `develop` содержит незаконченную
|
||||||
|
работу, быстрого фикса нет — это цена отказа от release-ветки в `main`.
|
||||||
|
Исключение требует явного решения: `release/hotfix-<name>` от `main`,
|
||||||
|
PR в `main`, тег, затем merge `main → develop` для восстановления инварианта.
|
||||||
|
Такой релиз оставляет merge-коммит в `develop`; cherry-pick из `develop`
|
||||||
|
в hotfix-ветку порождает дубль коммита при последующем rebase и не
|
||||||
|
применяется. Если ломается публичный контракт, major сохраняется и для hotfix.
|
||||||
|
|
||||||
Каждый CI использует только свой checkout. Backend генерирует OpenAPI без сервера,
|
Каждый CI использует только свой checkout. Backend генерирует OpenAPI без сервера,
|
||||||
сверяет генератор с configured app и не хранит JSON в Git; frontend проверяет types по входному
|
сверяет генератор с configured app и не хранит JSON в Git; frontend проверяет types по входному
|
||||||
@@ -227,12 +253,17 @@ snapshot. Docs проверяет внутренние ссылки; внешн
|
|||||||
доступ к root для выпуска другого репозитория не требуется.
|
доступ к root для выпуска другого репозитория не требуется.
|
||||||
|
|
||||||
В Gitea для `main` и `develop` нужны запрет прямого push и force-push,
|
В Gitea для `main` и `develop` нужны запрет прямого push и force-push,
|
||||||
обязательные проверки: в root только `commits`, в остальных repo —
|
блокировка устаревшей ветки перед merge, обязательные проверки: в root только
|
||||||
`verify`, `security`, `commits`; также нужен запрет обхода правил
|
`commits`, в остальных repo — `verify`, `security`, `commits`; также нужен
|
||||||
администратором. Требование отдельного одобрения review не вводится для
|
запрет обхода правил администратором. Требование отдельного одобрения review
|
||||||
разработки одним владельцем. Ветка по умолчанию — `develop`; Renovate тоже
|
не вводится для разработки одним владельцем. Ветка по умолчанию — `develop`;
|
||||||
отправляет PR в `develop`. Теги `v*` защищены, создание разрешается владельцу
|
Renovate тоже отправляет PR в `develop`. Теги `v*` защищены, создание
|
||||||
релиза; их удаление или перенос не входит в обычный процесс.
|
разрешается владельцу релиза; их удаление или перенос не входит в обычный процесс.
|
||||||
|
|
||||||
|
Линейность истории Gitea отдельной опцией не выражает, её обеспечивают
|
||||||
|
разрешённые merge styles репозитория: merge-коммиты и rebase с `--no-ff`
|
||||||
|
выключены, доступны squash, rebase-merge и fast-forward-only. Стиль по
|
||||||
|
умолчанию — squash, обновление ветки — rebase, ветка после merge удаляется.
|
||||||
|
|
||||||
В root нет собственного test/verify runner и release hooks. Release-it и
|
В root нет собственного test/verify runner и release hooks. Release-it и
|
||||||
commitlint используются напрямую. Остальные репозитории сохраняют свои gates.
|
commitlint используются напрямую. Остальные репозитории сохраняют свои gates.
|
||||||
|
|||||||
Reference in New Issue
Block a user