From 44e45dfb00c303e2422889bd1d24385ae1ca2858 Mon Sep 17 00:00:00 2001 From: coopops Date: Tue, 9 Jun 2026 18:29:09 +0000 Subject: [PATCH] =?UTF-8?q?[@dark-sun]=20ci(release):=20FF-=D0=BF=D1=80?= =?UTF-8?q?=D0=BE=D0=BC=D0=BE=D1=83=D1=88=D0=BD=20dev=E2=86=92testnet?= =?UTF-8?q?=E2=86=92main=20=D0=B2=D0=BC=D0=B5=D1=81=D1=82=D0=BE=20merge=20?= =?UTF-8?q?-X=20theirs?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Релизный флоу переведён на линейную 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) --- .github/workflows/release.yaml | 94 +++++++++++++++++-------------- CLAUDE.md | 2 + components/contracts/package.json | 3 - package.json | 6 +- scripts/RELEASE.md | 64 +++++++++++++++++++++ scripts/cut-release.sh | 47 ++++++++++++++++ scripts/promote.sh | 42 ++++++++++++++ scripts/publish-alpha.sh | 41 -------------- scripts/publish-prod.sh | 43 -------------- 9 files changed, 209 insertions(+), 133 deletions(-) create mode 100644 scripts/RELEASE.md create mode 100755 scripts/cut-release.sh create mode 100755 scripts/promote.sh delete mode 100644 scripts/publish-alpha.sh delete mode 100644 scripts/publish-prod.sh diff --git a/.github/workflows/release.yaml b/.github/workflows/release.yaml index 25dc145058e..f152ec5a73d 100644 --- a/.github/workflows/release.yaml +++ b/.github/workflows/release.yaml @@ -1,38 +1,39 @@ name: Release -# Единый релизный workflow по push'у тэга `v*`. +# Релизный workflow по push'у в testnet/main (FF-промоушн-модель, см. +# scripts/RELEASE.md). # # Порядок (через jobs.needs): # 1) release — контракты + контейнеры + webhook деплоя (атомарно) -# 2) publish-packages — npm publish через lerna (если не -alpha) +# 2) publish-packages — npm publish через lerna (только ветка main) # 3) trigger-coopenomics — workflow_dispatch сборки сайта C9S/coopenomics на -# Gitea (если не -alpha + ветка main); он сам тянет -# mono и пересобирает доки. +# Gitea (только ветка main); он сам тянет mono и +# пересобирает доки. # # publish-docs (gh-pages на github.com) удалён 2026-05-25 — после переезда на # Gitea Pages-публикация невалидна; доки деплоятся через C9S/coopenomics. # # Зачем последовательно: пакеты/доки/внешний триггер не должны уезжать, -# если релиз контрактов или контейнеров провалился. Раньше четыре workflow'а -# стартовали параллельно по одному тэгу — отсюда гонка из инцидента 2026-05-13 -# с ledger2 walletop sweep'ом (см. историю release.yaml до объединения). +# если релиз контрактов или контейнеров провалился. # -# Гейт `-alpha` унифицирован: пакеты + доки + триггер docs пропускают только -# не-alpha тэги. -beta/-rc/-test в проекте не используются (см. CLAUDE.md), -# поэтому проще иметь один паттерн `!contains(github.ref, '-alpha')`. -# IS_PROD в release-job остаётся на `(alpha|beta|rc|test)` — он определяет -# деплой-webhook (testnet vs production) и тэг `:latest` у образов; -# намеренно строже, чтобы случайный -rc-тэг не уехал на прод. +# Триггер — push в testnet/main с изменением lerna.json. Версию бампает +# `lerna version` ОДИН раз на dev (scripts/cut-release.sh); тот же коммит едет +# вверх по fast-forward (scripts/promote.sh). lerna.json меняется только на +# релизном бампе, поэтому обычные feature-пуши деплой не триггерят. dev в +# триггере НЕТ: cut на dev только бампает версию, деплой начинается с testnet +# (staging) и завершается main (production). # -# Резолв ветки. Тэг семантики окружения не несёт (например `v2026.5.13-alpha-2` -# может лежать на testnet или dev — определяет именно ветка, на которую сделан -# merge перед тэгом). Поэтому ищем первое совпадение SHA с remote-ветками -# main/testnet/dev. branch экспортируется как outputs.branch для downstream jobs. +# Окружение определяет ВЕТКА (main → production, testnet → staging), а не +# суффикс версии: единая версия едет во все среды, поэтому source of truth — +# ветка, на которую сделан промоушн. Версия читается из закоммиченного +# lerna.json. Гейты npm-publish/доки — `branch == main`. on: push: - tags: - - 'v*' + branches: [testnet, main] + paths: + - 'lerna.json' + workflow_dispatch: permissions: contents: write @@ -50,22 +51,22 @@ jobs: with: fetch-depth: 0 - - name: Resolve branch, build mode and tags + - name: Resolve branch, build mode and version id: resolve run: | - TAG_NAME="${{ github.ref_name }}" + # Ветка — это ветка push'а (или выбранная в workflow_dispatch). + BRANCH="${{ github.ref_name }}" SHA="${{ github.sha }}" - BRANCH="" - for candidate in main testnet dev; do - if git branch -r --contains "$SHA" 2>/dev/null | grep -qE "^[[:space:]]*origin/${candidate}$"; then - BRANCH="$candidate" - break - fi - done - if [ -z "$BRANCH" ]; then - echo "::error::Тэг $TAG_NAME ($SHA) не лежит ни на одной из dev/testnet/main" + + # Версия — из закоммиченного lerna.json. Её бампит lerna version ОДИН + # раз на dev (scripts/cut-release.sh), и тот же коммит едет по FF в + # testnet/main. Тэг как триггер больше не используется. + RAW_VERSION="$(sed -nE 's/.*"version"[[:space:]]*:[[:space:]]*"([^"]+)".*/\1/p' lerna.json | head -1)" + if [ -z "$RAW_VERSION" ]; then + echo "::error::Не удалось прочитать version из lerna.json" exit 1 fi + TAG_NAME="v${RAW_VERSION}" case "$BRANCH" in main) @@ -83,17 +84,22 @@ jobs: CONTRACTS_TAG=dev CONTRACTS_EXTRA= ;; + *) + echo "::error::Неподдерживаемая ветка $BRANCH (ожидались testnet/main/dev)" + exit 1 + ;; esac - # IS_PROD определяет webhook деплоя (PRODUCTION vs TESTNET) и тэг :latest. - # Сознательно строже гейта publish-* — случайный -rc/-beta тэг не должен - # уехать на прод-вебхук, даже если в npm/docs его опубликовали. - if [[ "$TAG_NAME" =~ -(alpha|beta|rc|test) ]]; then - IS_PROD=false - WEBHOOK_URL='${{ vars.TESTNET_WEBHOOK_URL }}' - else + # Окружение деплоя определяет ВЕТКА: main → production, иначе → testnet. + # IS_PROD управляет деплой-webhook (PRODUCTION vs TESTNET) и тэгом :latest + # у образов. Раньше критерием был суффикс -alpha у тэга; теперь единая + # версия едет во все среды, поэтому источник истины — ветка промоушна. + if [ "$BRANCH" = "main" ]; then IS_PROD=true WEBHOOK_URL='${{ vars.PRODUCTION_WEBHOOK_URL }}' + else + IS_PROD=false + WEBHOOK_URL='${{ vars.TESTNET_WEBHOOK_URL }}' fi { @@ -334,14 +340,16 @@ jobs: # ============================================================================ # 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 9/10). Иначе action-setup может подтянуть другую версию и переписать # pnpm-lock.yaml → Lerna EUNCOMMIT. # ============================================================================ publish-packages: needs: release - if: ${{ !contains(github.ref, '-alpha') }} + if: ${{ needs.release.outputs.branch == 'main' }} runs-on: ubuntu-latest steps: - uses: actions/checkout@v4 @@ -364,7 +372,7 @@ jobs: # ============================================================================ # trigger-coopenomics-docs — пересборка сайта C9S/coopenomics. - # Гейт: не-alpha + ветка main. + # Гейт: ветка main. # # coopenomics переехал на Gitea (C9S/coopenomics); его publish-docs.yaml # клонирует mono и пересобирает сайт. Gitea НЕ имеет API для @@ -376,7 +384,7 @@ jobs: # ============================================================================ trigger-coopenomics-docs: needs: release - if: ${{ !contains(github.ref, '-alpha') && needs.release.outputs.branch == 'main' }} + if: ${{ needs.release.outputs.branch == 'main' }} runs-on: ubuntu-latest steps: - name: Trigger coopenomics website rebuild (Gitea workflow_dispatch) @@ -392,11 +400,11 @@ jobs: # webhook DOCS_DEPLOY_WEBHOOK_URL. Отдельная от coopenomics публикация — # обе доки уезжают синхронно по релизу. Извлечён из бывшего publish-docs: # gh-pages на github.com выпилен как мёртвый, остался реальный deploy-webhook - # (приёмник деплоит доки на своей стороне). Гейт: не-alpha + ветка main. + # (приёмник деплоит доки на своей стороне). Гейт: ветка main. # ============================================================================ trigger-mono-docs: needs: release - if: ${{ !contains(github.ref, '-alpha') && needs.release.outputs.branch == 'main' }} + if: ${{ needs.release.outputs.branch == 'main' }} runs-on: ubuntu-latest steps: - name: Trigger docs deployment webhook diff --git a/CLAUDE.md b/CLAUDE.md index 5084cfefcf8..ad89cb7b606 100644 --- a/CLAUDE.md +++ b/CLAUDE.md @@ -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. +**Релиз/деплой — 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 и др.). ### Story-by-story для проекта «Стол заказов» (mono-ai-4 на marketplace2) diff --git a/components/contracts/package.json b/components/contracts/package.json index fccef9b0b95..3c29459cc9c 100644 --- a/components/contracts/package.json +++ b/components/contracts/package.json @@ -9,9 +9,6 @@ "build:one": "./build.sh $1", "build:one:test": "contract=$1 mode=test ./build.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:build": "pnpm --filter @coopenomics/standards-site build", "standards:preview": "pnpm --filter @coopenomics/standards-site preview" diff --git a/package.json b/package.json index 305f7239dd8..42058f5fb73 100644 --- a/package.json +++ b/package.json @@ -63,10 +63,10 @@ "dev:lib": "lerna run dev --parallel --scope @coopenomics/factory --scope cooptypes", "dev:all": "lerna run dev --parallel", "gpt-commit": "git add . && commitgpt", - "testnet": "bash scripts/publish-alpha.sh", - "production": "bash scripts/publish-prod.sh", + "cut": "bash scripts/cut-release.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", - "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: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", diff --git a/scripts/RELEASE.md b/scripts/RELEASE.md new file mode 100644 index 00000000000..ee13d9f5e49 --- /dev/null +++ b/scripts/RELEASE.md @@ -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/` → `origin/`, рабочее дерево не трогается. + +## Что триггерит деплой + +`.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-модель +это устраняет конструктивно. diff --git a/scripts/cut-release.sh b/scripts/cut-release.sh new file mode 100755 index 00000000000..3b830647482 --- /dev/null +++ b/scripts/cut-release.sh @@ -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 + доки)" diff --git a/scripts/promote.sh b/scripts/promote.sh new file mode 100755 index 00000000000..20be120ca8e --- /dev/null +++ b/scripts/promote.sh @@ -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 " + 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." diff --git a/scripts/publish-alpha.sh b/scripts/publish-alpha.sh deleted file mode 100644 index 3302918b7c5..00000000000 --- a/scripts/publish-alpha.sh +++ /dev/null @@ -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 diff --git a/scripts/publish-prod.sh b/scripts/publish-prod.sh deleted file mode 100644 index 9a439d72445..00000000000 --- a/scripts/publish-prod.sh +++ /dev/null @@ -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 \ No newline at end of file