[@dark-sun] ci(release): FF-промоушн dev→testnet→main вместо merge -X theirs
Релизный флоу переведён на линейную fast-forward модель — устраняет регулярные конфликты на 20 package.json при релизе. Корень проблемы: publish-alpha.sh/publish-prod.sh бампали версию НА КАЖДОЙ ветке (alpha-N на testnet, чистую на main) через `git merge -X theirs` + back-merge. Два независимых bump-коммита за цикл + merge'и плодили расхождение веток → конфликты (особенно при гонке push/pull). Новая модель: - Версию бампает lerna ОДИН раз на dev (scripts/cut-release.sh). - Тот же коммит едет вверх по FF: scripts/promote.sh testnet|main (server-side fast-forward push, рабочее дерево не трогается). - testnet/main не несут своих коммитов → ветки не диверджатся → конфликты структурно невозможны. release.yaml: - Триггер: push в testnet/main с изменением lerna.json (вместо тега v*). Окружение определяет ВЕТКА (main→prod, testnet→staging), не суффикс -alpha. - Версия читается из закоммиченного lerna.json (едет с коммитом по FF). - Гейты npm-publish/доки: branch == main (вместо !contains '-alpha'). - Образы/webhook по-прежнему версия-тегированы → playbooks/приёмник деплоя не затрагиваются. Удалены publish-alpha.sh/publish-prod.sh и мёртвые дубли тех же merge-X-theirs скриптов (root sync:main, components/contracts production/testnet/docs-publish). Документация — scripts/RELEASE.md + CLAUDE.md PR-flow. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -1,38 +1,39 @@
|
|||||||
name: Release
|
name: Release
|
||||||
|
|
||||||
# Единый релизный workflow по push'у тэга `v*`.
|
# Релизный workflow по push'у в testnet/main (FF-промоушн-модель, см.
|
||||||
|
# scripts/RELEASE.md).
|
||||||
#
|
#
|
||||||
# Порядок (через jobs.needs):
|
# Порядок (через jobs.needs):
|
||||||
# 1) release — контракты + контейнеры + webhook деплоя (атомарно)
|
# 1) release — контракты + контейнеры + webhook деплоя (атомарно)
|
||||||
# 2) publish-packages — npm publish через lerna (если не -alpha)
|
# 2) publish-packages — npm publish через lerna (только ветка main)
|
||||||
# 3) trigger-coopenomics — workflow_dispatch сборки сайта C9S/coopenomics на
|
# 3) trigger-coopenomics — workflow_dispatch сборки сайта C9S/coopenomics на
|
||||||
# Gitea (если не -alpha + ветка main); он сам тянет
|
# Gitea (только ветка main); он сам тянет mono и
|
||||||
# mono и пересобирает доки.
|
# пересобирает доки.
|
||||||
#
|
#
|
||||||
# publish-docs (gh-pages на github.com) удалён 2026-05-25 — после переезда на
|
# publish-docs (gh-pages на github.com) удалён 2026-05-25 — после переезда на
|
||||||
# Gitea Pages-публикация невалидна; доки деплоятся через C9S/coopenomics.
|
# Gitea Pages-публикация невалидна; доки деплоятся через C9S/coopenomics.
|
||||||
#
|
#
|
||||||
# Зачем последовательно: пакеты/доки/внешний триггер не должны уезжать,
|
# Зачем последовательно: пакеты/доки/внешний триггер не должны уезжать,
|
||||||
# если релиз контрактов или контейнеров провалился. Раньше четыре workflow'а
|
# если релиз контрактов или контейнеров провалился.
|
||||||
# стартовали параллельно по одному тэгу — отсюда гонка из инцидента 2026-05-13
|
|
||||||
# с ledger2 walletop sweep'ом (см. историю release.yaml до объединения).
|
|
||||||
#
|
#
|
||||||
# Гейт `-alpha` унифицирован: пакеты + доки + триггер docs пропускают только
|
# Триггер — push в testnet/main с изменением lerna.json. Версию бампает
|
||||||
# не-alpha тэги. -beta/-rc/-test в проекте не используются (см. CLAUDE.md),
|
# `lerna version` ОДИН раз на dev (scripts/cut-release.sh); тот же коммит едет
|
||||||
# поэтому проще иметь один паттерн `!contains(github.ref, '-alpha')`.
|
# вверх по fast-forward (scripts/promote.sh). lerna.json меняется только на
|
||||||
# IS_PROD в release-job остаётся на `(alpha|beta|rc|test)` — он определяет
|
# релизном бампе, поэтому обычные feature-пуши деплой не триггерят. dev в
|
||||||
# деплой-webhook (testnet vs production) и тэг `:latest` у образов;
|
# триггере НЕТ: cut на dev только бампает версию, деплой начинается с testnet
|
||||||
# намеренно строже, чтобы случайный -rc-тэг не уехал на прод.
|
# (staging) и завершается main (production).
|
||||||
#
|
#
|
||||||
# Резолв ветки. Тэг семантики окружения не несёт (например `v2026.5.13-alpha-2`
|
# Окружение определяет ВЕТКА (main → production, testnet → staging), а не
|
||||||
# может лежать на testnet или dev — определяет именно ветка, на которую сделан
|
# суффикс версии: единая версия едет во все среды, поэтому source of truth —
|
||||||
# merge перед тэгом). Поэтому ищем первое совпадение SHA с remote-ветками
|
# ветка, на которую сделан промоушн. Версия читается из закоммиченного
|
||||||
# main/testnet/dev. branch экспортируется как outputs.branch для downstream jobs.
|
# lerna.json. Гейты npm-publish/доки — `branch == main`.
|
||||||
|
|
||||||
on:
|
on:
|
||||||
push:
|
push:
|
||||||
tags:
|
branches: [testnet, main]
|
||||||
- 'v*'
|
paths:
|
||||||
|
- 'lerna.json'
|
||||||
|
workflow_dispatch:
|
||||||
|
|
||||||
permissions:
|
permissions:
|
||||||
contents: write
|
contents: write
|
||||||
@@ -50,22 +51,22 @@ jobs:
|
|||||||
with:
|
with:
|
||||||
fetch-depth: 0
|
fetch-depth: 0
|
||||||
|
|
||||||
- name: Resolve branch, build mode and tags
|
- name: Resolve branch, build mode and version
|
||||||
id: resolve
|
id: resolve
|
||||||
run: |
|
run: |
|
||||||
TAG_NAME="${{ github.ref_name }}"
|
# Ветка — это ветка push'а (или выбранная в workflow_dispatch).
|
||||||
|
BRANCH="${{ github.ref_name }}"
|
||||||
SHA="${{ github.sha }}"
|
SHA="${{ github.sha }}"
|
||||||
BRANCH=""
|
|
||||||
for candidate in main testnet dev; do
|
# Версия — из закоммиченного lerna.json. Её бампит lerna version ОДИН
|
||||||
if git branch -r --contains "$SHA" 2>/dev/null | grep -qE "^[[:space:]]*origin/${candidate}$"; then
|
# раз на dev (scripts/cut-release.sh), и тот же коммит едет по FF в
|
||||||
BRANCH="$candidate"
|
# testnet/main. Тэг как триггер больше не используется.
|
||||||
break
|
RAW_VERSION="$(sed -nE 's/.*"version"[[:space:]]*:[[:space:]]*"([^"]+)".*/\1/p' lerna.json | head -1)"
|
||||||
fi
|
if [ -z "$RAW_VERSION" ]; then
|
||||||
done
|
echo "::error::Не удалось прочитать version из lerna.json"
|
||||||
if [ -z "$BRANCH" ]; then
|
|
||||||
echo "::error::Тэг $TAG_NAME ($SHA) не лежит ни на одной из dev/testnet/main"
|
|
||||||
exit 1
|
exit 1
|
||||||
fi
|
fi
|
||||||
|
TAG_NAME="v${RAW_VERSION}"
|
||||||
|
|
||||||
case "$BRANCH" in
|
case "$BRANCH" in
|
||||||
main)
|
main)
|
||||||
@@ -83,17 +84,22 @@ jobs:
|
|||||||
CONTRACTS_TAG=dev
|
CONTRACTS_TAG=dev
|
||||||
CONTRACTS_EXTRA=
|
CONTRACTS_EXTRA=
|
||||||
;;
|
;;
|
||||||
|
*)
|
||||||
|
echo "::error::Неподдерживаемая ветка $BRANCH (ожидались testnet/main/dev)"
|
||||||
|
exit 1
|
||||||
|
;;
|
||||||
esac
|
esac
|
||||||
|
|
||||||
# IS_PROD определяет webhook деплоя (PRODUCTION vs TESTNET) и тэг :latest.
|
# Окружение деплоя определяет ВЕТКА: main → production, иначе → testnet.
|
||||||
# Сознательно строже гейта publish-* — случайный -rc/-beta тэг не должен
|
# IS_PROD управляет деплой-webhook (PRODUCTION vs TESTNET) и тэгом :latest
|
||||||
# уехать на прод-вебхук, даже если в npm/docs его опубликовали.
|
# у образов. Раньше критерием был суффикс -alpha у тэга; теперь единая
|
||||||
if [[ "$TAG_NAME" =~ -(alpha|beta|rc|test) ]]; then
|
# версия едет во все среды, поэтому источник истины — ветка промоушна.
|
||||||
IS_PROD=false
|
if [ "$BRANCH" = "main" ]; then
|
||||||
WEBHOOK_URL='${{ vars.TESTNET_WEBHOOK_URL }}'
|
|
||||||
else
|
|
||||||
IS_PROD=true
|
IS_PROD=true
|
||||||
WEBHOOK_URL='${{ vars.PRODUCTION_WEBHOOK_URL }}'
|
WEBHOOK_URL='${{ vars.PRODUCTION_WEBHOOK_URL }}'
|
||||||
|
else
|
||||||
|
IS_PROD=false
|
||||||
|
WEBHOOK_URL='${{ vars.TESTNET_WEBHOOK_URL }}'
|
||||||
fi
|
fi
|
||||||
|
|
||||||
{
|
{
|
||||||
@@ -334,14 +340,16 @@ jobs:
|
|||||||
|
|
||||||
# ============================================================================
|
# ============================================================================
|
||||||
# publish-packages — npm publish (бывший publish-packages.yaml).
|
# publish-packages — npm publish (бывший publish-packages.yaml).
|
||||||
# Гейт: не-alpha. Не зависит от ветки.
|
# Гейт: ветка main. Версия для publish — из закоммиченного package.json
|
||||||
|
# (lerna publish from-package); её бампит cut-release.sh на dev, тот же коммит
|
||||||
|
# доезжает до main по FF.
|
||||||
# Жёстко прибиваем версию pnpm, которой сгенерён lockfile (lockfileVersion 9.0
|
# Жёстко прибиваем версию pnpm, которой сгенерён lockfile (lockfileVersion 9.0
|
||||||
# = pnpm 9/10). Иначе action-setup может подтянуть другую версию и переписать
|
# = pnpm 9/10). Иначе action-setup может подтянуть другую версию и переписать
|
||||||
# pnpm-lock.yaml → Lerna EUNCOMMIT.
|
# pnpm-lock.yaml → Lerna EUNCOMMIT.
|
||||||
# ============================================================================
|
# ============================================================================
|
||||||
publish-packages:
|
publish-packages:
|
||||||
needs: release
|
needs: release
|
||||||
if: ${{ !contains(github.ref, '-alpha') }}
|
if: ${{ needs.release.outputs.branch == 'main' }}
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v4
|
- uses: actions/checkout@v4
|
||||||
@@ -364,7 +372,7 @@ jobs:
|
|||||||
|
|
||||||
# ============================================================================
|
# ============================================================================
|
||||||
# trigger-coopenomics-docs — пересборка сайта C9S/coopenomics.
|
# trigger-coopenomics-docs — пересборка сайта C9S/coopenomics.
|
||||||
# Гейт: не-alpha + ветка main.
|
# Гейт: ветка main.
|
||||||
#
|
#
|
||||||
# coopenomics переехал на Gitea (C9S/coopenomics); его publish-docs.yaml
|
# coopenomics переехал на Gitea (C9S/coopenomics); его publish-docs.yaml
|
||||||
# клонирует mono и пересобирает сайт. Gitea НЕ имеет API для
|
# клонирует mono и пересобирает сайт. Gitea НЕ имеет API для
|
||||||
@@ -376,7 +384,7 @@ jobs:
|
|||||||
# ============================================================================
|
# ============================================================================
|
||||||
trigger-coopenomics-docs:
|
trigger-coopenomics-docs:
|
||||||
needs: release
|
needs: release
|
||||||
if: ${{ !contains(github.ref, '-alpha') && needs.release.outputs.branch == 'main' }}
|
if: ${{ needs.release.outputs.branch == 'main' }}
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
steps:
|
steps:
|
||||||
- name: Trigger coopenomics website rebuild (Gitea workflow_dispatch)
|
- name: Trigger coopenomics website rebuild (Gitea workflow_dispatch)
|
||||||
@@ -392,11 +400,11 @@ jobs:
|
|||||||
# webhook DOCS_DEPLOY_WEBHOOK_URL. Отдельная от coopenomics публикация —
|
# webhook DOCS_DEPLOY_WEBHOOK_URL. Отдельная от coopenomics публикация —
|
||||||
# обе доки уезжают синхронно по релизу. Извлечён из бывшего publish-docs:
|
# обе доки уезжают синхронно по релизу. Извлечён из бывшего publish-docs:
|
||||||
# gh-pages на github.com выпилен как мёртвый, остался реальный deploy-webhook
|
# gh-pages на github.com выпилен как мёртвый, остался реальный deploy-webhook
|
||||||
# (приёмник деплоит доки на своей стороне). Гейт: не-alpha + ветка main.
|
# (приёмник деплоит доки на своей стороне). Гейт: ветка main.
|
||||||
# ============================================================================
|
# ============================================================================
|
||||||
trigger-mono-docs:
|
trigger-mono-docs:
|
||||||
needs: release
|
needs: release
|
||||||
if: ${{ !contains(github.ref, '-alpha') && needs.release.outputs.branch == 'main' }}
|
if: ${{ needs.release.outputs.branch == 'main' }}
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
steps:
|
steps:
|
||||||
- name: Trigger docs deployment webhook
|
- name: Trigger docs deployment webhook
|
||||||
|
|||||||
@@ -38,6 +38,8 @@ ln -s /home/admin/mono-ai-4/components/controller/.env $WT/components/controller
|
|||||||
|
|
||||||
**Базовая ветка mono-ai-1 — `dev`.** Прямые коммиты в `dev` для мелких фиксов разрешены и не требуют feature-ветки/PR (отменено 2026-05-22 пользователем). Для крупных задач — feature-ветка (`feat/...` или `fix/...`) от dev и PR в dev; merge делает пользователь на GitHub. Push в `main` / `mvp` — по-прежнему только через PR.
|
**Базовая ветка mono-ai-1 — `dev`.** Прямые коммиты в `dev` для мелких фиксов разрешены и не требуют feature-ветки/PR (отменено 2026-05-22 пользователем). Для крупных задач — feature-ветка (`feat/...` или `fix/...`) от dev и PR в dev; merge делает пользователь на GitHub. Push в `main` / `mvp` — по-прежнему только через PR.
|
||||||
|
|
||||||
|
**Релиз/деплой — FF-промоушн `dev → testnet → main`** (см. `scripts/RELEASE.md`). Версию бампает `lerna` ОДИН раз на dev (`scripts/cut-release.sh`), тот же коммит едет вверх по fast-forward (`scripts/promote.sh testnet|main`). `testnet`/`main` не несут своих коммитов — только FF-указатели, поэтому merge-конфликтов нет. **В `testnet`/`main` прямых коммитов и повторных бампов версии быть не должно** — это ломает FF. Деплой триггерит push в testnet/main с изменением `lerna.json`; окружение определяет ветка (main → prod). Старые `publish-alpha.sh`/`publish-prod.sh` (merge `-X theirs` + per-branch бамп + back-merge) удалены — они и плодили 20-package.json конфликты.
|
||||||
|
|
||||||
**Не stash'ить `-u` при unstaged WIP пользователя.** Это создаёт окно для потери при drop/конфликте. Если нужно временно убрать unstaged — `git stash push -- <конкретные-paths>` либо коммит-в-feature, и только свои файлы. Кейс 2026-05-18: после `git stash -u`+`drop` при конфликте потерял WIP пользователя (infra.ts/config.ts/quasar.config.cjs и др.).
|
**Не stash'ить `-u` при unstaged WIP пользователя.** Это создаёт окно для потери при drop/конфликте. Если нужно временно убрать unstaged — `git stash push -- <конкретные-paths>` либо коммит-в-feature, и только свои файлы. Кейс 2026-05-18: после `git stash -u`+`drop` при конфликте потерял WIP пользователя (infra.ts/config.ts/quasar.config.cjs и др.).
|
||||||
|
|
||||||
### Story-by-story для проекта «Стол заказов» (mono-ai-4 на marketplace2)
|
### Story-by-story для проекта «Стол заказов» (mono-ai-4 на marketplace2)
|
||||||
|
|||||||
@@ -9,9 +9,6 @@
|
|||||||
"build:one": "./build.sh $1",
|
"build:one": "./build.sh $1",
|
||||||
"build:one:test": "contract=$1 mode=test ./build.sh",
|
"build:one:test": "contract=$1 mode=test ./build.sh",
|
||||||
"enter": "./boot/scripts/enter.sh",
|
"enter": "./boot/scripts/enter.sh",
|
||||||
"testnet": "git checkout testnet && git merge dev && lerna publish prerelease && git push && git checkout dev && git merge testnet",
|
|
||||||
"production": "git checkout main && git merge -X theirs testnet && lerna publish --conventional-commits --conventional-graduate && git push && git checkout dev && git merge main",
|
|
||||||
"docs-publish": "pnpm gh-pages --nojekyll -d docs/site --repo git@github.com:coopenomics/monocoop.git",
|
|
||||||
"standards:dev": "pnpm --filter @coopenomics/standards-site dev",
|
"standards:dev": "pnpm --filter @coopenomics/standards-site dev",
|
||||||
"standards:build": "pnpm --filter @coopenomics/standards-site build",
|
"standards:build": "pnpm --filter @coopenomics/standards-site build",
|
||||||
"standards:preview": "pnpm --filter @coopenomics/standards-site preview"
|
"standards:preview": "pnpm --filter @coopenomics/standards-site preview"
|
||||||
|
|||||||
+3
-3
@@ -63,10 +63,10 @@
|
|||||||
"dev:lib": "lerna run dev --parallel --scope @coopenomics/factory --scope cooptypes",
|
"dev:lib": "lerna run dev --parallel --scope @coopenomics/factory --scope cooptypes",
|
||||||
"dev:all": "lerna run dev --parallel",
|
"dev:all": "lerna run dev --parallel",
|
||||||
"gpt-commit": "git add . && commitgpt",
|
"gpt-commit": "git add . && commitgpt",
|
||||||
"testnet": "bash scripts/publish-alpha.sh",
|
"cut": "bash scripts/cut-release.sh",
|
||||||
"production": "bash scripts/publish-prod.sh",
|
"testnet": "bash scripts/promote.sh testnet",
|
||||||
|
"production": "bash scripts/promote.sh main",
|
||||||
"docs-publish": "pnpm gh-pages --nojekyll -d docs/site --repo git@github.com:coopenomics/monocoop.git",
|
"docs-publish": "pnpm gh-pages --nojekyll -d docs/site --repo git@github.com:coopenomics/monocoop.git",
|
||||||
"sync:main": "git checkout main && git merge -X theirs testnet && git push && git checkout dev && git merge main",
|
|
||||||
"test": "pnpm run test:unit && pnpm run test:component && pnpm run test:integration",
|
"test": "pnpm run test:unit && pnpm run test:component && pnpm run test:integration",
|
||||||
"test:unit": "pnpm --filter cooptypes run test && pnpm --filter @coopenomics/parser run test && pnpm --filter @coopenomics/notifications run test",
|
"test:unit": "pnpm --filter cooptypes run test && pnpm --filter @coopenomics/parser run test && pnpm --filter @coopenomics/notifications run test",
|
||||||
"test:component": "SOURCE=local NODE_ENV=test SKIP_BLOCK_FETCH=TRUE pnpm --filter @coopenomics/factory run test",
|
"test:component": "SOURCE=local NODE_ENV=test SKIP_BLOCK_FETCH=TRUE pnpm --filter @coopenomics/factory run test",
|
||||||
|
|||||||
@@ -0,0 +1,64 @@
|
|||||||
|
# Релизный флоу: FF-промоушн dev → testnet → main
|
||||||
|
|
||||||
|
Линейная модель без merge и без конфликтов. Версию бампает `lerna` один раз на
|
||||||
|
`dev`, и тот же коммит едет вверх по **fast-forward**. `testnet` и `main` не
|
||||||
|
несут собственных коммитов — это указатели на протестированный коммит `dev`,
|
||||||
|
поэтому ветки никогда не диверджатся и merge-конфликтов не бывает.
|
||||||
|
|
||||||
|
## Как релизить
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# 1. Нарезать релиз на dev (бамп версии lerna, БЕЗ деплоя)
|
||||||
|
scripts/cut-release.sh
|
||||||
|
|
||||||
|
# 2. Промоут на staging (деплой в testnet)
|
||||||
|
scripts/promote.sh testnet
|
||||||
|
|
||||||
|
# 3. Промоут в production (деплой main + npm publish + доки)
|
||||||
|
scripts/promote.sh main
|
||||||
|
```
|
||||||
|
|
||||||
|
- `cut-release.sh` — строго на `dev`. Считает версию `vYYYY.M.D` (повторный
|
||||||
|
релиз того же дня → суффикс `-N`), вызывает `lerna version` (бамп всех
|
||||||
|
package.json + lerna.json, коммит `chore(release): publish`, тег, push `dev`).
|
||||||
|
**Деплой не запускает** — CI не слушает `dev`.
|
||||||
|
- `promote.sh testnet|main` — server-side **fast-forward** push
|
||||||
|
`origin/<source>` → `origin/<target>`, рабочее дерево не трогается.
|
||||||
|
|
||||||
|
## Что триггерит деплой
|
||||||
|
|
||||||
|
`.github/workflows/release.yaml` слушает **push в `testnet`/`main` с изменением
|
||||||
|
`lerna.json`**. lerna.json меняется только при релизном бампе (на `dev`), и этот
|
||||||
|
коммит входит в диапазон FF-промоушна — поэтому деплоятся именно релизы, а не
|
||||||
|
обычные feature-пуши.
|
||||||
|
|
||||||
|
Окружение определяет **ветка**, не суффикс версии:
|
||||||
|
|
||||||
|
| Ветка | Окружение | Деплой-webhook | npm publish / доки |
|
||||||
|
|-----------|----------------------|------------------------|--------------------|
|
||||||
|
| `testnet` | staging | `TESTNET_WEBHOOK_URL` | нет |
|
||||||
|
| `main` | production | `PRODUCTION_WEBHOOK_URL` | да (`main`) |
|
||||||
|
|
||||||
|
Версия для образов (`dicoop/*:vX`), webhook-payload и `lerna publish
|
||||||
|
from-package` читается из закоммиченного `lerna.json` — едет с коммитом по FF.
|
||||||
|
Для приёмника деплоя (playbooks) ничего не меняется: webhook по-прежнему
|
||||||
|
получает версию-строку, образы тегаются версией.
|
||||||
|
|
||||||
|
## Инварианты (не нарушать)
|
||||||
|
|
||||||
|
- **В `testnet`/`main` — только FF-промоушн, никаких прямых коммитов.** Прямой
|
||||||
|
коммит сделает ветку не-FF; `promote.sh` тогда отвергнется push'ем (это гард,
|
||||||
|
а не баг — выровняй ветку).
|
||||||
|
- **Версия бампается только на `dev`** (`cut-release.sh`). Никаких повторных
|
||||||
|
бампов на `testnet`/`main` — иначе вернутся расхождение и конфликты.
|
||||||
|
- Тег (`vX`), который вешает `lerna version`, — маркер версии и основа для
|
||||||
|
GitHub Release (`scripts/publish-release.sh`); триггером деплоя он **не**
|
||||||
|
является.
|
||||||
|
|
||||||
|
## Откуда ушли (история)
|
||||||
|
|
||||||
|
Старые `publish-alpha.sh`/`publish-prod.sh` бампали версию **на каждой ветке**
|
||||||
|
(`-alpha-N` на testnet, чистую на main) через `git merge -X theirs` +
|
||||||
|
back-merge. Два независимых бампа за цикл + merge'и плодили расхождение → 20
|
||||||
|
package.json регулярно конфликтовали (особенно при гонке push/pull). FF-модель
|
||||||
|
это устраняет конструктивно.
|
||||||
Executable
+47
@@ -0,0 +1,47 @@
|
|||||||
|
#!/bin/bash
|
||||||
|
set -e
|
||||||
|
|
||||||
|
# Режет релиз СТРОГО на dev. Версия бампается ОДИН раз (lerna) и дальше едет
|
||||||
|
# вверх по fast-forward dev → testnet → main без повторных бампов и merge —
|
||||||
|
# отсюда ноль конфликтов (см. scripts/RELEASE.md).
|
||||||
|
#
|
||||||
|
# Сам по себе cut НЕ деплоит: CI триггерится push'ем в testnet/main, не в dev.
|
||||||
|
# Деплой запускается промоушном: scripts/promote.sh testnet → scripts/promote.sh main.
|
||||||
|
|
||||||
|
git fetch origin
|
||||||
|
|
||||||
|
# Режем от свежего origin/dev.
|
||||||
|
git checkout dev
|
||||||
|
git pull --ff-only origin dev
|
||||||
|
|
||||||
|
# Версия: vYYYY.M.D (без лидирующих нулей), для повторных релизов того же дня — суффикс -N.
|
||||||
|
BASE_VERSION="v$(date +%Y).$(date +%-m).$(date +%-d)"
|
||||||
|
|
||||||
|
MAX_SUFFIX=0
|
||||||
|
HAS_BASE=0
|
||||||
|
for TAG in $(git tag --list | grep "^$BASE_VERSION" | sort -V); do
|
||||||
|
if [[ "$TAG" == "$BASE_VERSION" ]]; then
|
||||||
|
HAS_BASE=1
|
||||||
|
elif [[ "$TAG" =~ ^${BASE_VERSION}-([0-9]+)$ ]]; then
|
||||||
|
SUFFIX="${BASH_REMATCH[1]}"
|
||||||
|
(( SUFFIX > MAX_SUFFIX )) && MAX_SUFFIX=$SUFFIX
|
||||||
|
fi
|
||||||
|
done
|
||||||
|
|
||||||
|
if (( HAS_BASE == 0 )) && (( MAX_SUFFIX == 0 )); then
|
||||||
|
VERSION="$BASE_VERSION"
|
||||||
|
else
|
||||||
|
VERSION="$BASE_VERSION-$((MAX_SUFFIX + 1))"
|
||||||
|
fi
|
||||||
|
|
||||||
|
echo "Режем релиз на dev: $VERSION"
|
||||||
|
|
||||||
|
# lerna сам бампит все package.json + lerna.json, коммитит chore(release): publish,
|
||||||
|
# вешает тег и пушит dev. Тег нужен как маркер версии и для GitHub Release —
|
||||||
|
# триггером деплоя он больше НЕ является (деплой по push в ветку).
|
||||||
|
lerna version "$VERSION" --yes --no-push=false --no-git-tag-version=false --force-publish
|
||||||
|
|
||||||
|
echo
|
||||||
|
echo "Готово. Версия $VERSION закоммичена на dev."
|
||||||
|
echo "Дальше: scripts/promote.sh testnet (staging-деплой)"
|
||||||
|
echo "потом: scripts/promote.sh main (production-деплой + npm publish + доки)"
|
||||||
Executable
+42
@@ -0,0 +1,42 @@
|
|||||||
|
#!/bin/bash
|
||||||
|
set -e
|
||||||
|
|
||||||
|
# Fast-forward промоушн БЕЗ merge: target-ветка просто двигается на коммит
|
||||||
|
# source-ветки. testnet и main не несут собственных коммитов — это указатели на
|
||||||
|
# протестированный коммит dev. Ветки не диверджатся → конфликты невозможны.
|
||||||
|
#
|
||||||
|
# scripts/promote.sh testnet # dev → testnet (staging-деплой)
|
||||||
|
# scripts/promote.sh main # testnet → main (production-деплой)
|
||||||
|
#
|
||||||
|
# Если push отвергнут как non-fast-forward — это ГАРД, не баг: значит в target
|
||||||
|
# попал прямой коммит, и target обогнал source. Выровняй вручную (в testnet/main
|
||||||
|
# прямых коммитов быть не должно — только FF-промоушн).
|
||||||
|
|
||||||
|
TARGET="$1"
|
||||||
|
case "$TARGET" in
|
||||||
|
testnet) SOURCE="dev" ;;
|
||||||
|
main) SOURCE="testnet" ;;
|
||||||
|
*)
|
||||||
|
echo "Использование: $0 <testnet|main>"
|
||||||
|
exit 1
|
||||||
|
;;
|
||||||
|
esac
|
||||||
|
|
||||||
|
git fetch origin
|
||||||
|
|
||||||
|
echo "FF-промоушн: origin/$SOURCE → origin/$TARGET"
|
||||||
|
|
||||||
|
# Server-side fast-forward: двигаем удалённый $TARGET на коммит удалённого $SOURCE.
|
||||||
|
# Рабочее дерево не трогаем (никаких checkout/merge). Без --force: сервер
|
||||||
|
# отвергнет, если это не fast-forward.
|
||||||
|
git push origin "origin/${SOURCE}:refs/heads/${TARGET}"
|
||||||
|
|
||||||
|
if [ "$TARGET" = "main" ]; then
|
||||||
|
ENV_LABEL="PRODUCTION (+ npm publish + доки)"
|
||||||
|
else
|
||||||
|
ENV_LABEL="staging/testnet"
|
||||||
|
fi
|
||||||
|
|
||||||
|
echo
|
||||||
|
echo "Готово. origin/$TARGET = origin/$SOURCE."
|
||||||
|
echo "CI задеплоит: $ENV_LABEL."
|
||||||
@@ -1,41 +0,0 @@
|
|||||||
#!/bin/bash
|
|
||||||
set -e
|
|
||||||
|
|
||||||
# Получаем дату в формате vYYYY.M.D без лидирующих нулей
|
|
||||||
BASE_VERSION="v$(date +%Y).$(date +%-m).$(date +%-d)"
|
|
||||||
|
|
||||||
# Получаем все теги с сегодняшней датой
|
|
||||||
TAGS=($(git tag --list | grep "^$BASE_VERSION" | sort -V))
|
|
||||||
|
|
||||||
MAX_SUFFIX=0
|
|
||||||
HAS_BASE=0
|
|
||||||
|
|
||||||
for TAG in "${TAGS[@]}"; do
|
|
||||||
if [[ "$TAG" == "$BASE_VERSION" ]]; then
|
|
||||||
HAS_BASE=1
|
|
||||||
elif [[ "$TAG" =~ ^${BASE_VERSION}-(.*)$ ]]; then
|
|
||||||
SUFFIX=${TAG##*-}
|
|
||||||
if [[ $SUFFIX =~ ^[0-9]+$ ]] && (( SUFFIX > MAX_SUFFIX )); then
|
|
||||||
MAX_SUFFIX=$SUFFIX
|
|
||||||
fi
|
|
||||||
fi
|
|
||||||
done
|
|
||||||
|
|
||||||
# Для тестнета всегда добавляем суффикс alpha-N
|
|
||||||
ALPHA_SUFFIX="alpha-$((MAX_SUFFIX+1))"
|
|
||||||
VERSION="$BASE_VERSION-$ALPHA_SUFFIX"
|
|
||||||
|
|
||||||
echo "Версионируем тестнет: $VERSION"
|
|
||||||
|
|
||||||
# Переходим на testnet и мержим dev
|
|
||||||
git checkout -f testnet
|
|
||||||
git merge -X theirs dev
|
|
||||||
|
|
||||||
# Версионируем с alpha суффиксом
|
|
||||||
lerna version "$VERSION" --yes --no-push=false --no-git-tag-version=false --force-publish
|
|
||||||
|
|
||||||
git push --follow-tags
|
|
||||||
|
|
||||||
# Возвращаемся на dev и мержим testnet
|
|
||||||
git checkout dev
|
|
||||||
git merge testnet
|
|
||||||
@@ -1,43 +0,0 @@
|
|||||||
#!/bin/bash
|
|
||||||
set -e
|
|
||||||
|
|
||||||
# Получаем дату в формате vYYYY.M.D без лидирующих нулей
|
|
||||||
BASE_VERSION="v$(date +%Y).$(date +%-m).$(date +%-d)"
|
|
||||||
|
|
||||||
# Получаем все теги с сегодняшней датой
|
|
||||||
TAGS=($(git tag --list | grep "^$BASE_VERSION" | sort -V))
|
|
||||||
|
|
||||||
MAX_SUFFIX=0
|
|
||||||
HAS_BASE=0
|
|
||||||
|
|
||||||
for TAG in "${TAGS[@]}"; do
|
|
||||||
if [[ "$TAG" == "$BASE_VERSION" ]]; then
|
|
||||||
HAS_BASE=1
|
|
||||||
elif [[ "$TAG" =~ ^${BASE_VERSION}-(.*)$ ]]; then
|
|
||||||
SUFFIX=${TAG##*-}
|
|
||||||
if [[ $SUFFIX =~ ^[0-9]+$ ]] && (( SUFFIX > MAX_SUFFIX )); then
|
|
||||||
MAX_SUFFIX=$SUFFIX
|
|
||||||
fi
|
|
||||||
fi
|
|
||||||
done
|
|
||||||
|
|
||||||
if (( HAS_BASE == 0 )) && (( MAX_SUFFIX == 0 )); then
|
|
||||||
VERSION="$BASE_VERSION"
|
|
||||||
else
|
|
||||||
VERSION="$BASE_VERSION-$((MAX_SUFFIX+1))"
|
|
||||||
fi
|
|
||||||
|
|
||||||
echo "Версионируем: $VERSION"
|
|
||||||
|
|
||||||
# Переходим на main и мержим testnet
|
|
||||||
git checkout -f main
|
|
||||||
git merge -X theirs testnet
|
|
||||||
|
|
||||||
# Только версия и тег, без публикации
|
|
||||||
lerna version "$VERSION" --yes --no-push=false --no-git-tag-version=false --force-publish
|
|
||||||
|
|
||||||
git push --follow-tags
|
|
||||||
|
|
||||||
# Возвращаемся на dev и мержим main
|
|
||||||
git checkout dev
|
|
||||||
git merge main
|
|
||||||
Reference in New Issue
Block a user