[@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
|
||||
|
||||
# Единый релизный 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
|
||||
|
||||
Reference in New Issue
Block a user