food-market/deploy/generate-release-notes.sh
nns 843fc4bd03
Some checks are pending
Auto-tag / Create date-tag (push) Waiting to run
CI / Backend (.NET 8) (push) Waiting to run
CI / Web (React + Vite) (push) Waiting to run
CI / POS (WPF, Windows) (push) Waiting to run
feat(s21): stage→prod migration toolchain (7 скриптов + workflow)
1. deploy/check-prod-readiness.sh — pre-deploy gating: backup<60min,
   disk≥5GB на /opt+/var/lib/docker, /health/ready=Healthy, .env
   required-keys без placeholder'ов. --ssh-host для удалённой проверки.

2. deploy/prod-deploy.sh <api-tag> <web-tag> — blue-green release:
   pull → green-контейнер на :8088 → migrations (auto) → smoke
   (/health/ready + /api/me с тест-токеном) → nginx upstream switch
   → swap → docker compose up -d с обновлённым тэгом. Failure →
   удаление green, blue остаётся. --skip-web флаг.

3. deploy/prod-rollback.sh <to-tag> — docker pull (если нужно) →
   docker compose up -d --force-recreate с указанным tag'ом → wait
   /health/ready до 120с. --dry-run + --skip-web.

4. deploy/post-deploy-smoke.sh — 10 шагов (signup → login →
   /api/me → list products/counterparties/stores/stock → create+delete
   product → logout-via-session). JSON парсится через python3
   (не grep — споткнулись на пробеле перед `:` в access_token).
   Telegram-alert через FM_TG_TOKEN/CHAT при провале. Stage-тест: 10/10 ✓.

5. deploy/db-schema-diff.sh — pg_dump --schema-only с обоих хостов
   через ssh+docker exec, нормализация (sed), diff -u. Exit:
   0=идентичны, 1=разница, 2=ошибка.

6. deploy/generate-release-notes.sh <from-tag> <to-tag> — git log
   group by prefix через awk: feat→, fix→🐛, perf→, docs→📚,
   test/refactor/chore→<details>. Сохраняет docs/release-notes/<tag>.md.

7. .forgejo/workflows/auto-tag.yml — на push в main: если HEAD не
   помечен → создаёт v<YYYYMMDD>.<N> annotated tag, push в origin,
   генерирует release-notes для будущего деплоя.

Все скрипты идемпотентные, поддерживают --dry-run, не трогают прод.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-06-07 22:31:10 +05:00

130 lines
4 KiB
Bash
Executable file
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

#!/usr/bin/env bash
#
# Sprint 21: генератор release-notes между двумя тэгами.
#
# Парсит `git log <from>..<to>`, группирует коммиты по prefix:
# feat: → ## Новые возможности
# fix: → ## Исправления
# perf: → ## Производительность
# docs: → ## Документация
# test: → ## Тесты (свёрнуто)
# chore/refactor/build: → ## Внутренние изменения (свёрнуто)
#
# Вывод — markdown, дополнительно сохраняет в:
# docs/release-notes/<to-tag>.md
# Используется при создании git-тега и в /whats-new.
#
# Usage:
# deploy/generate-release-notes.sh <from-tag> <to-tag> [--dry-run]
# deploy/generate-release-notes.sh v20260606.1 v20260607.3 > release.md
set -uo pipefail
FROM="${1:-}"
TO="${2:-HEAD}"
DRY_RUN=0
while [[ $# -gt 0 ]]; do
case "$1" in
--dry-run) DRY_RUN=1; shift ;;
--help|-h) grep -E '^#( |$)' "$0" | sed 's/^# \?//'; exit 0 ;;
-*) echo "Unknown: $1" >&2; exit 2 ;;
*) shift ;;
esac
done
if [[ -z "$FROM" ]]; then
echo "Usage: $0 <from-tag> <to-tag> [--dry-run]" >&2
exit 2
fi
cd "$(dirname "$0")/.."
REPO_ROOT="$(pwd)"
# Валидация тэгов: должны существовать в git.
git rev-parse --verify "$FROM" >/dev/null 2>&1 || { echo "FAIL: тэг $FROM не найден"; exit 1; }
git rev-parse --verify "$TO" >/dev/null 2>&1 || { echo "FAIL: тэг $TO не найден"; exit 1; }
# Собираем коммиты в формате `prefix|subject|short-sha`.
# `grep -v Merge` исключает merge-коммиты.
COMMITS=$(git log "$FROM..$TO" --pretty=format:'%s|%h' --no-merges)
if [[ -z "$COMMITS" ]]; then
echo "Нет коммитов между $FROM и $TO"
exit 0
fi
# Группируем через awk. Префикс: feat/fix/perf/docs/test/chore/refactor/build/style.
RENDERED=$(echo "$COMMITS" | awk -F'|' '
function head(label) {
if (!printed[label]) {
print ""
print label
print ""
printed[label] = 1
}
}
{
subject = $1
sha = $2
type = "other"
text = subject
if (match(subject, /^(feat|fix|perf|docs|test|chore|refactor|build|style)(\([^)]+\))?:[[:space:]]*/, m)) {
type = m[1]
scope = m[2]
text = substr(subject, RLENGTH + 1)
}
line = "- " text " (`" sha "`)"
bucket[type] = bucket[type] line "\n"
}
END {
if (bucket["feat"]) { head("## ✨ Новые возможности"); printf "%s", bucket["feat"] }
if (bucket["fix"]) { head("## 🐛 Исправления"); printf "%s", bucket["fix"] }
if (bucket["perf"]) { head("## ⚡ Производительность"); printf "%s", bucket["perf"] }
if (bucket["docs"]) { head("## 📚 Документация"); printf "%s", bucket["docs"] }
if (bucket["test"]) {
print ""
print "<details><summary>🧪 Тесты</summary>"
print ""
printf "%s", bucket["test"]
print ""
print "</details>"
}
if (bucket["refactor"] || bucket["chore"] || bucket["build"] || bucket["style"]) {
print ""
print "<details><summary>🔧 Внутренние изменения</summary>"
print ""
for (k in bucket) if (k == "refactor" || k == "chore" || k == "build" || k == "style") printf "%s", bucket[k]
print ""
print "</details>"
}
if (bucket["other"]) {
print ""
print "<details><summary>📦 Прочее</summary>"
print ""
printf "%s", bucket["other"]
print ""
print "</details>"
}
}
')
DATE=$(date -u +%Y-%m-%d)
COUNT=$(echo "$COMMITS" | wc -l)
HEADER="# Release $TO
Дата: $DATE · Коммитов: $COUNT · С: $FROM"
OUTPUT="$HEADER
$RENDERED"
echo "$OUTPUT"
if [[ $DRY_RUN -eq 0 ]]; then
TARGET="$REPO_ROOT/docs/release-notes/$TO.md"
mkdir -p "$(dirname "$TARGET")"
echo "$OUTPUT" > "$TARGET"
echo "" >&2
echo "[saved] $TARGET" >&2
fi