Files
GenAudioBookInfo/.github/workflows/release.yml
Dmitriy Fofanov 91f5c42859
Wiki Sync / Синхронизация Wiki (push) Failing after 0s
Release CI/CD / Сборка и публикация релиза (push) Failing after 1s
Fix: убраны uses actions, заменены на run (совместимость с dfgit)
2026-02-23 22:46:25 +03:00

390 lines
18 KiB
YAML
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.
# ══════════════════════════════════════════════════════════════════════════════
# CI/CD Pipeline: Сборка и публикация релиза GenAudioBookInfo
# ══════════════════════════════════════════════════════════════════════════════
#
# ЗАПУСК:
# make release VERSION=2.1.0
# Создаёт git-тег v2.1.0, пушит его → запускается этот workflow.
#
# ──────────────────────────────────────────────────────────────────────────────
# ЕДИНЫЙ JOB (без передачи артефактов между jobs):
#
# 1. Качество кода: go vet + go test
# 2. Кросс-компиляция: 16 бинарников для всех платформ
# 3. Архивирование: Windows → ZIP, Unix → tar.gz, SHA256
# 4. Описание релиза: авто-changelog из git-коммитов на русском языке
# 5. Публикация: Gitea Release + загрузка всех файлов
#
# ──────────────────────────────────────────────────────────────────────────────
# НЕОБХОДИМАЯ НАСТРОЙКА:
#
# В настройках репозитория Gitea → Settings → Secrets:
# GIT_TOKEN (или GITEA_TOKEN) — токен с правами write:repository
#
# ══════════════════════════════════════════════════════════════════════════════
name: "Release CI/CD"
on:
push:
tags:
- "v*"
env:
GO_VERSION: "1.24"
APP_NAME: genaudiobookinfo
BUILD_DIR: build
# ══════════════════════════════════════════════════════════════════════════════
jobs:
release:
name: "Сборка и публикация релиза"
runs-on: ubuntu-latest
steps:
# ── Исходный код (полная история для changelog) ──────────────────────
- name: "Получение исходного кода"
shell: bash
env:
GIT_TOKEN: ${{ secrets.GIT_TOKEN }}
GITEA_TOKEN: ${{ secrets.GITEA_TOKEN }}
run: |
TOKEN="${GIT_TOKEN:-$GITEA_TOKEN}"
SERVER="${{ github.server_url }}"
REPO="${{ github.repository }}"
TAG="${GITHUB_REF_NAME}"
# Убираем https:// для вставки токена
HOST="${SERVER#https://}"
HOST="${HOST#http://}"
CLONE_URL="https://${TOKEN}@${HOST}/${REPO}.git"
echo ">>> Клонирование ${REPO} (тег ${TAG})..."
git clone --branch "${TAG}" "${CLONE_URL}" . 2>&1 | grep -v "${TOKEN}" || true
git fetch --tags --force 2>&1 | grep -v "${TOKEN}" || true
echo ">>> Исходный код получен: $(git log --oneline -1)"
- name: "Проверка Go"
shell: bash
run: |
if ! command -v go &>/dev/null; then
echo "ОШИБКА: Go не установлен на раннере."
echo "Установите Go ${{ env.GO_VERSION }} на сервер раннера."
exit 1
fi
echo ">>> Go: $(go version)"
# ── Определение версии ──────────────────────────────────────────────
- name: "Определение версии и предыдущего тега"
id: ver
shell: bash
run: |
TAG="${GITHUB_REF_NAME}"
VER="${TAG#v}"
echo "version=${VER}" >> "$GITHUB_OUTPUT"
echo "tag=${TAG}" >> "$GITHUB_OUTPUT"
PREV_TAG=$(git describe --tags --abbrev=0 "${TAG}^" 2>/dev/null || echo "")
echo "prev_tag=${PREV_TAG}" >> "$GITHUB_OUTPUT"
echo "══════════════════════════════════════"
echo " Версия: ${VER}"
echo " Git-тег: ${TAG}"
echo " Пред. тег: ${PREV_TAG:-<первый релиз>}"
echo " Коммит: ${GITHUB_SHA:0:8}"
echo "══════════════════════════════════════"
# ── Качество кода ───────────────────────────────────────────────────
- name: "Проверка качества кода (vet + tests)"
shell: bash
run: |
echo ">>> Загрузка зависимостей..."
go mod download
echo ">>> Статический анализ (go vet)..."
go vet ./...
echo ">>> Unit-тесты..."
go test ./... -count=1 -timeout 5m
echo ">>> Качество кода — ОК"
# ── Кросс-компиляция ────────────────────────────────────────────────
- name: "Кросс-компиляция для всех платформ (16 бинарников)"
shell: bash
run: |
make build-all VERSION=${{ steps.ver.outputs.version }}
echo ""
echo "Собранные бинарники:"
ls -lh ${BUILD_DIR}/
# ── Архивы + контрольные суммы ──────────────────────────────────────
- name: "Создание архивов и контрольных сумм"
shell: bash
run: |
cd "${BUILD_DIR}"
mkdir -p archives
echo "=== Создание архивов ==="
for f in *; do
[ -f "$f" ] || continue
case "$f" in
*.exe)
ARCNAME="${f%.exe}.zip"
echo " ${f} → archives/${ARCNAME}"
zip "archives/${ARCNAME}" "$f"
;;
${APP_NAME}-*)
ARCNAME="${f}.tar.gz"
echo " ${f} → archives/${ARCNAME}"
tar -czf "archives/${ARCNAME}" "$f"
;;
esac
done
echo ""
echo "=== Контрольные суммы SHA256 ==="
cd archives
sha256sum * > checksums-sha256.txt
cat checksums-sha256.txt
echo ""
echo "Готово: $(ls | wc -l) файлов"
# ── Генерация описания релиза на русском языке ──────────────────────
- name: "Генерация описания релиза"
shell: bash
run: |
TAG="${{ steps.ver.outputs.tag }}"
VER="${{ steps.ver.outputs.version }}"
PREV_TAG="${{ steps.ver.outputs.prev_tag }}"
COMMIT="${{ github.sha }}"
DATE=$(date -u '+%d.%m.%Y %H:%M UTC')
# ── Changelog из git-коммитов ──────────────────────────────────
if [ -n "${PREV_TAG}" ]; then
RAW_LOG=$(git log --pretty=format:"%s" "${PREV_TAG}..${TAG}" --no-merges 2>/dev/null || echo "")
else
RAW_LOG=$(git log --pretty=format:"%s" --no-merges 2>/dev/null | head -50 || echo "")
fi
# Категоризация коммитов
FEATURES=""
FIXES=""
REFACTOR=""
OTHER=""
while IFS= read -r line; do
[ -z "$line" ] && continue
line_lower=$(echo "$line" | tr '[:upper:]' '[:lower:]')
case "$line_lower" in
функция:*|feat:*|feature:*|добавлен*|реализован*|новое:*)
FEATURES="${FEATURES}
- ${line}"
;;
исправлен*|fix:*|bugfix:*|баг:*|ошибка:*)
FIXES="${FIXES}
- ${line}"
;;
рефакторинг:*|refactor:*|оптимизац*|улучшен*)
REFACTOR="${REFACTOR}
- ${line}"
;;
ci:*|docs:*|ci/*|build:*)
OTHER="${OTHER}
- ${line}"
;;
*)
OTHER="${OTHER}
- ${line}"
;;
esac
done <<< "$RAW_LOG"
# ── Начинаем формировать тело релиза ───────────────────────────
{
echo "## GenAudioBookInfo ${TAG}"
echo ""
# Пользовательские заметки (RELEASE_NOTES.md — приоритет)
if [ -f "RELEASE_NOTES.md" ]; then
cat RELEASE_NOTES.md
echo ""
fi
# Автоматический changelog
HAS_CHANGES=false
if [ -n "${FEATURES}" ]; then
HAS_CHANGES=true
echo "### 🚀 Новые возможности"
echo "${FEATURES}"
echo ""
fi
if [ -n "${FIXES}" ]; then
HAS_CHANGES=true
echo "### 🐛 Исправления"
echo "${FIXES}"
echo ""
fi
if [ -n "${REFACTOR}" ]; then
HAS_CHANGES=true
echo "### ♻️ Рефакторинг и оптимизация"
echo "${REFACTOR}"
echo ""
fi
if [ -n "${OTHER}" ]; then
HAS_CHANGES=true
echo "### 📝 Прочие изменения"
echo "${OTHER}"
echo ""
fi
if [ "${HAS_CHANGES}" = "false" ]; then
echo "- Первый релиз"
echo ""
fi
echo "---"
echo ""
echo "### Поддерживаемые платформы"
echo ""
echo "| ОС | Архитектура | Файл |"
echo "|---|---|---|"
echo "| Windows | amd64 (64-бит) | \`${APP_NAME}-windows-amd64.zip\` |"
echo "| Windows | 386 (32-бит) | \`${APP_NAME}-windows-386.zip\` |"
echo "| Windows | arm64 | \`${APP_NAME}-windows-arm64.zip\` |"
echo "| Linux | amd64 | \`${APP_NAME}-linux-amd64.tar.gz\` |"
echo "| Linux | arm64 | \`${APP_NAME}-linux-arm64.tar.gz\` |"
echo "| Linux | armv7 | \`${APP_NAME}-linux-armv7.tar.gz\` |"
echo "| Linux | 386 | \`${APP_NAME}-linux-386.tar.gz\` |"
echo "| Linux | MIPS | \`${APP_NAME}-linux-mips.tar.gz\` |"
echo "| Linux | MIPSle | \`${APP_NAME}-linux-mipsle.tar.gz\` |"
echo "| Linux | RISC-V 64 | \`${APP_NAME}-linux-riscv64.tar.gz\` |"
echo "| macOS | amd64 (Intel) | \`${APP_NAME}-darwin-amd64.tar.gz\` |"
echo "| macOS | arm64 (Apple Silicon) | \`${APP_NAME}-darwin-arm64.tar.gz\` |"
echo "| FreeBSD | amd64 | \`${APP_NAME}-freebsd-amd64.tar.gz\` |"
echo "| FreeBSD | arm64 | \`${APP_NAME}-freebsd-arm64.tar.gz\` |"
echo "| OpenBSD | amd64 | \`${APP_NAME}-openbsd-amd64.tar.gz\` |"
echo "| NetBSD | amd64 | \`${APP_NAME}-netbsd-amd64.tar.gz\` |"
echo ""
echo "> 🔒 Контрольные суммы: \`checksums-sha256.txt\`"
echo ""
echo "### Информация о сборке"
echo ""
echo "| Параметр | Значение |"
echo "|---|---|"
echo "| Коммит | \`${COMMIT}\` |"
echo "| Дата сборки | ${DATE} |"
echo "| Go | ${{ env.GO_VERSION }} |"
} > /tmp/release_body.md
echo ">>> Описание релиза сформировано:"
echo "──────────────────────────────────────"
cat /tmp/release_body.md
echo "──────────────────────────────────────"
# ── Публикация в Gitea ──────────────────────────────────────────────
- name: "Создание релиза в Gitea и загрузка файлов"
shell: bash
env:
GIT_TOKEN: ${{ secrets.GIT_TOKEN }}
GITEA_TOKEN: ${{ secrets.GITEA_TOKEN }}
run: |
TOKEN="${GIT_TOKEN:-$GITEA_TOKEN}"
if [ -z "${TOKEN}" ]; then
echo "ОШИБКА: не задан секрет GIT_TOKEN (или GITEA_TOKEN)."
echo "Добавьте токен в Settings → Secrets репозитория."
exit 1
fi
TAG="${{ steps.ver.outputs.tag }}"
COMMIT="${{ github.sha }}"
GITEA_URL="${{ github.server_url }}"
REPO="${{ github.repository }}"
echo "══════════════════════════════════════"
echo " Сервер: ${GITEA_URL}"
echo " Репо: ${REPO}"
echo " Тег: ${TAG}"
echo " Коммит: ${COMMIT:0:8}"
echo "══════════════════════════════════════"
# ── JSON-кодирование тела релиза ───────────────────────────────
BODY_JSON=$(python3 -c "
import json
with open('/tmp/release_body.md', 'r') as f:
print(json.dumps(f.read()))
")
# ── Создание релиза через Gitea API ────────────────────────────
echo ""
echo ">>> Создаём релиз ${TAG}..."
RELEASE_JSON=$(curl -sf -X POST \
-H "Authorization: token ${TOKEN}" \
-H "Content-Type: application/json" \
"${GITEA_URL}/api/v1/repos/${REPO}/releases" \
-d "{
\"tag_name\": \"${TAG}\",
\"target_commitish\": \"${COMMIT}\",
\"name\": \"${APP_NAME} ${TAG}\",
\"body\": ${BODY_JSON},
\"draft\": false,
\"prerelease\": false
}")
RELEASE_ID=$(echo "$RELEASE_JSON" | \
python3 -c "import sys,json; d=json.load(sys.stdin); print(d.get('id',''))" 2>/dev/null || true)
if [ -z "$RELEASE_ID" ] || [ "$RELEASE_ID" = "None" ]; then
echo "ОШИБКА: не удалось создать релиз!"
echo "Ответ API:"
echo "$RELEASE_JSON" | python3 -m json.tool 2>/dev/null || echo "$RELEASE_JSON"
exit 1
fi
echo ">>> Релиз создан: ID=${RELEASE_ID}"
# ── Загрузка артефактов ────────────────────────────────────────
echo ""
echo ">>> Загрузка артефактов..."
UPLOAD_OK=0
UPLOAD_FAIL=0
for FILE in ./${BUILD_DIR}/archives/*; do
[ -f "$FILE" ] || continue
FILENAME=$(basename "$FILE")
FILESIZE=$(du -sh "$FILE" | cut -f1)
printf " %-55s [%5s] " "${FILENAME}" "${FILESIZE}"
HTTP_CODE=$(curl -s -o /dev/null -w "%{http_code}" \
-X POST \
-H "Authorization: token ${TOKEN}" \
-H "Content-Type: application/octet-stream" \
--data-binary @"${FILE}" \
"${GITEA_URL}/api/v1/repos/${REPO}/releases/${RELEASE_ID}/assets?name=${FILENAME}")
if [ "$HTTP_CODE" -ge 200 ] && [ "$HTTP_CODE" -lt 300 ]; then
echo "✓ (HTTP ${HTTP_CODE})"
UPLOAD_OK=$((UPLOAD_OK + 1))
else
echo "✗ (HTTP ${HTTP_CODE})"
UPLOAD_FAIL=$((UPLOAD_FAIL + 1))
fi
done
# ── Итог ───────────────────────────────────────────────────────
echo ""
echo "══════════════════════════════════════════════"
echo " Загружено: ${UPLOAD_OK}"
echo " Ошибок: ${UPLOAD_FAIL}"
echo " Релиз: ${GITEA_URL}/${REPO}/releases/tag/${TAG}"
echo "══════════════════════════════════════════════"
if [ "$UPLOAD_FAIL" -gt 0 ]; then
echo "ОШИБКА: часть файлов не удалось загрузить!"
exit 1
fi
echo ">>> Релиз ${TAG} опубликован успешно."