#!/usr/bin/env bash # Release script для 222a-seo-audit plugin. # Использование: ./scripts/release.sh [--message "changelog text"] [--dry-run] set -euo pipefail VERSION="${1:-}" DRY_RUN=0 MESSAGE="" shift || true while [[ $# -gt 0 ]]; do case "$1" in --dry-run) DRY_RUN=1 ;; --message) if [[ $# -lt 2 || -z "${2:-}" ]]; then echo "ERROR: --message требует непустой аргумент" exit 1 fi MESSAGE="$2" shift ;; *) echo "Unknown arg: $1"; exit 1 ;; esac shift done # 1. Валидация semver if ! [[ "$VERSION" =~ ^[0-9]+\.[0-9]+\.[0-9]+$ ]]; then echo "ERROR: версия должна быть в формате MAJOR.MINOR.PATCH (например, 1.0.0)" echo "Получено: '$VERSION'" exit 1 fi TAG="v${VERSION}" REPO_ROOT="$(cd "$(dirname "$0")/.." && pwd)" cd "$REPO_ROOT" # 2. Проверка чистоты и ветки if [[ "$DRY_RUN" -eq 0 ]]; then if [[ -n "$(git status --porcelain)" ]]; then echo "ERROR: рабочая копия не чистая. Закоммитьте или стэшните изменения." exit 1 fi CURRENT_BRANCH="$(git rev-parse --abbrev-ref HEAD)" if [[ "$CURRENT_BRANCH" != "main" ]]; then echo "ERROR: релиз только из ветки main. Текущая: $CURRENT_BRANCH" exit 1 fi fi # 3. Проверка, что тег ещё не существует if git rev-parse "$TAG" >/dev/null 2>&1; then echo "ERROR: тег $TAG уже существует" exit 1 fi if [[ "$DRY_RUN" -eq 1 ]]; then echo "Would release $VERSION (dry-run)" exit 0 fi # 4. Подменить версию в 3 json-файлах (sed inline) PLUGIN_JSON="plugins/222a-seo-audit/.claude-plugin/plugin.json" MCP_JSON="plugins/222a-seo-audit/.mcp.json" MARKETPLACE_JSON=".claude-plugin/marketplace.json" # Используем python для безопасной правки JSON python3 - "$VERSION" "$PLUGIN_JSON" "$MCP_JSON" "$MARKETPLACE_JSON" <<'PY' import json, sys version = sys.argv[1] plugin_json, mcp_json, marketplace_json = sys.argv[2], sys.argv[3], sys.argv[4] # Phase 1: load all files (fail fast if anything is broken) with open(plugin_json) as f: p = json.load(f) with open(mcp_json) as f: m = json.load(f) with open(marketplace_json) as f: mk = json.load(f) # Phase 2: mutate in memory p["version"] = version m["mcpServers"]["222a-seo-audit"]["headers"]["X-Plugin-Version"] = version mk["plugins"][0]["version"] = version # Phase 3: write all files (still not fully atomic across files, but all loads succeeded # so each individual write is a clean replace of a parsed structure) for path, data in [(plugin_json, p), (mcp_json, m), (marketplace_json, mk)]: with open(path, "w") as f: json.dump(data, f, indent=2, ensure_ascii=False) f.write("\n") print(f"Updated version to {version} in 3 files") PY # 5. Обновить CHANGELOG.md CHANGELOG="plugins/222a-seo-audit/CHANGELOG.md" TODAY="$(date +%Y-%m-%d)" # Проверка, что в CHANGELOG есть якорь [Unreleased] if ! grep -q '^## \[Unreleased\]' "$CHANGELOG"; then echo "ERROR: в $CHANGELOG нет секции '## [Unreleased]' — релиз остановлен" echo "Восстановите якорь и запустите снова." exit 1 fi if [[ -n "$MESSAGE" ]]; then # Вставить новую секцию после "## [Unreleased]" TMP="$(mktemp)" awk -v v="$VERSION" -v d="$TODAY" -v m="$MESSAGE" ' /^## \[Unreleased\]/ { print; print ""; print "## [" v "] - " d; print ""; print m; print ""; next } { print } ' "$CHANGELOG" > "$TMP" mv "$TMP" "$CHANGELOG" else # Открыть редактор для ручного ввода echo "Откройте $CHANGELOG, добавьте секцию для $VERSION в начало, сохраните и закройте." ${EDITOR:-nano} "$CHANGELOG" fi # 6. Коммит, тег, push git add "$PLUGIN_JSON" "$MCP_JSON" "$MARKETPLACE_JSON" "$CHANGELOG" git commit -m "release: v$VERSION" git tag -a "$TAG" -m "Release $VERSION" git push origin main git push origin "$TAG" echo "" echo "Released $VERSION (tag $TAG)." echo "Клиенты обновятся через:" echo " /plugin marketplace update" echo " /plugin update 222a-seo-audit"