From 532e34c53badc707a62149e99191e5ec5f71af22 Mon Sep 17 00:00:00 2001 From: coopops Date: Tue, 16 Jun 2026 07:45:04 +0000 Subject: [PATCH] =?UTF-8?q?ci(release):=20promote=20=D0=BF=D0=BE=20=D1=83?= =?UTF-8?q?=D0=BC=D0=BE=D0=BB=D1=87=D0=B0=D0=BD=D0=B8=D1=8E=20=D1=88=D0=BB?= =?UTF-8?q?=D1=91=D1=82=20=D1=82=D0=B5=D0=BA=D1=83=D1=89=D0=B8=D0=B9=20HEA?= =?UTF-8?q?D=20(=D0=BD=D0=B5=20=D0=BF=D0=BE=D1=81=D0=BB=D0=B5=D0=B4=D0=BD?= =?UTF-8?q?=D0=B8=D0=B9=20=D1=82=D0=B5=D0=B3)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Дефолт promote.sh — текущий HEAD чекаута; второй аргумент по-прежнему задаёт явный ref (ветка/тег/sha). Типичный путь: cut на dev → promote testnet → promote main, все три от того же HEAD. testnet/main остаются независимыми FF-указателями. Co-Authored-By: Claude Opus 4.8 --- scripts/RELEASE.md | 44 ++++++++++++++++++++++--------------------- scripts/promote.sh | 47 ++++++++++++++++++++-------------------------- 2 files changed, 43 insertions(+), 48 deletions(-) diff --git a/scripts/RELEASE.md b/scripts/RELEASE.md index 8592bd8d9b4..44dd3e8494f 100644 --- a/scripts/RELEASE.md +++ b/scripts/RELEASE.md @@ -8,32 +8,34 @@ fast-forward-указатель** на выбранный релизный те **и при этом прод и тест можно катить разным темпом и на разные версии одновременно**. -## Как релизить +## Как релизить (типичный путь) ```bash -# 1. Нарезать релиз на dev (бамп версии lerna + тег, БЕЗ деплоя) -pnpm run cut # = scripts/cut-release.sh - -# 2. Выкатить в окружения — НЕЗАВИСИМО, в любом порядке и темпе -pnpm run testnet # последний тег → testnet (staging) -pnpm run production # последний тег → main (production) - -# Точечно: конкретную версию в конкретный контур -scripts/promote.sh main v2026.6.16 # эту версию в прод -scripts/promote.sh main testnet # ровно то, что сейчас на testnet, в прод +# стоишь на dev +pnpm run cut # бамп версии lerna + тег + push dev (деплой НЕ запускает) +pnpm run testnet # текущий HEAD (= свежий cut) → testnet (staging), деплой пошёл +pnpm run production # тот же HEAD → main (production), деплой пошёл ``` -Рекомендуемая практика (не обязательная): сначала `promote.sh testnet`, проверить -на staging, затем `promote.sh main <та же версия>`. Это уже не топологический -гард (как в старом паровозе), а ваш процесс — контуры независимы. +`promote` по умолчанию шлёт **текущий HEAD** в указанный контур, поэтому после +`cut` оба промоута идут «оттуда же» (с dev). Контуры независимы — можно гнать +прод и тест разным темпом: -- `cut-release.sh` — строго на `dev`. Считает версию `vYYYY.M.D` (повторный релиз - того же дня → суффикс `-N`), вызывает `lerna version` (бамп всех package.json + - lerna.json, коммит `chore(release): publish`, тег, push `dev`). **Деплой не - запускает** — CI не слушает `dev`. -- `promote.sh [<версия|ref>]` — server-side **fast-forward** push - выбранного релизного тега на ветку окружения. `REF` по умолчанию — последний тег - на линии `origin/dev`. Рабочее дерево не трогается. Только вперёд (FF). +```bash +scripts/promote.sh main v2026.6.16 # конкретную версию (тег) в прод +scripts/promote.sh main testnet # ровно то, что сейчас на testnet, в прод +# на другой ветке: promote main → улетит HEAD этой ветки (FF-гард не пустит, если +# main не её предок) +``` + +- `cut-release.sh` — строго на `dev`. Версия `vYYYY.M.D` (повторный релиз того же + дня → суффикс `-N`), `lerna version` (бамп всех package.json + lerna.json, коммит + `chore(release): publish`, тег, push `dev`). **Деплой не запускает** — CI не + слушает `dev`. +- `promote.sh []` — server-side **fast-forward** push: по + умолчанию `HEAD`, иначе указанный ветка/тег/sha. Рабочее дерево не трогается. + Только вперёд (FF). **Деплой стартует только если в улетающем коммите есть бамп + `lerna.json`, которого ещё нет в целевой ветке** — то есть нужен свежий `cut`. ## Что триггерит деплой diff --git a/scripts/promote.sh b/scripts/promote.sh index 0c02f89cea5..81bd05bbe6f 100755 --- a/scripts/promote.sh +++ b/scripts/promote.sh @@ -1,27 +1,25 @@ #!/bin/bash set -e -# Независимый деплой по окружениям. testnet и main — НЕ паровоз: каждый из них -# самостоятельный fast-forward-указатель на выбранный релизный тег dev. Можно -# катить в прод и в тест разным темпом и на разные версии одновременно — -# расхождений и конфликтов нет, потому что оба указателя живут на ЛИНЕЙНОЙ -# истории dev (двигаются только вперёд по FF). +# Деплой в окружение. По умолчанию летит ТЕКУЩИЙ HEAD (твой чекаут) в указанную +# ветку окружения. testnet и main независимы — каждый двигается своим темпом; +# только вперёд по fast-forward (живут на линейной истории dev → нет диверджа и +# конфликтов). # -# scripts/promote.sh testnet # последний релиз (тег) → testnet -# scripts/promote.sh main # последний релиз (тег) → main -# scripts/promote.sh main v2026.6.16 # конкретную версию → main +# scripts/promote.sh testnet # текущий HEAD → testnet (staging) +# scripts/promote.sh main # текущий HEAD → main (production) +# scripts/promote.sh main v2026.6.16 # конкретную версию (тег) → main # scripts/promote.sh main testnet # ровно то, что сейчас на testnet → main # -# REF по умолчанию — последний релизный тег, достижимый из origin/dev (его вешает -# scripts/cut-release.sh). Деплой стартует push'ем в ветку с изменением lerna.json -# (бамп-коммит входит в диапазон FF) — слушает .github/workflows/release.yaml. +# Деплой (release.yaml) стартует, только если в улетающем коммите есть изменение +# lerna.json (релизный бамп от scripts/cut-release.sh), которого ещё нет в целевой +# ветке. Без свежего cut ветка передвинется, но пересборки не будет. # # Только FF (без --force): сервер отвергнет non-fast-forward — это ГАРД, не баг -# (в target попал прямой коммит, либо ты пытаешься откатить ветку назад). -# Откат на более старую версию или редеплой без движения ветки — через -# workflow_dispatch в release.yaml (inputs: environment + ref), НЕ через этот скрипт. +# (цель не является предком ref). Откат на старую версию / редеплой без движения +# ветки — через workflow_dispatch в release.yaml (inputs: environment + ref). -usage() { echo "Использование: $0 [<версия|ref>]"; } +usage() { echo "Использование: $0 []"; } TARGET="$1" case "$TARGET" in @@ -34,14 +32,9 @@ esac git fetch --tags --force origin -REF="$2" -if [ -z "$REF" ]; then - # Последний релизный тег на линии origin/dev (vYYYY.M.D[-N] от cut-release.sh). - REF="$(git describe --tags --abbrev=0 origin/dev)" -fi - -# REF может быть именем ветки (testnet/main/dev), тегом или sha — разрешаем единообразно. -if git show-ref --verify --quiet "refs/remotes/origin/$REF"; then +# По умолчанию — текущий HEAD. Второй аргумент: ветка (testnet/main/dev), тег или sha. +REF="${2:-HEAD}" +if [ "$REF" != "HEAD" ] && git show-ref --verify --quiet "refs/remotes/origin/$REF"; then SRC="origin/$REF" else SRC="$REF" @@ -51,10 +44,10 @@ REF_SHA="$(git rev-parse --verify "${SRC}^{commit}")" REF_VERSION="$(git show "${REF_SHA}:lerna.json" \ | sed -nE 's/.*"version"[[:space:]]*:[[:space:]]*"([^"]+)".*/\1/p' | head -1)" -echo "Деплой: $REF (v${REF_VERSION}) → $TARGET" +echo "Деплой: $REF → $TARGET (версия из lerna.json: v${REF_VERSION}, коммит ${REF_SHA:0:9})" -# Server-side fast-forward: двигаем удалённый TARGET на REF. Рабочее дерево не трогаем -# (никаких checkout/merge). Без --force: сервер отвергнет, если это не fast-forward. +# Server-side fast-forward: двигаем удалённый TARGET на REF. Рабочее дерево не трогаем. +# Без --force: сервер отвергнет, если это не fast-forward. git push origin "${REF_SHA}:refs/heads/${TARGET}" if [ "$TARGET" = "main" ]; then @@ -65,4 +58,4 @@ fi echo echo "Готово. origin/$TARGET = $REF (v${REF_VERSION})." -echo "CI задеплоит: $ENV_LABEL." +echo "CI задеплоит: $ENV_LABEL (если в диапазоне есть релизный бамп lerna.json)."