Создана структура документации, описывающая функциональность, установку, использование CLI, архитектуру и интеграции с TorrAPI и OpenRouter. Добавлены примеры конфигурации и метаданных, а также описание структуры выходных данных.
363 lines
20 KiB
YAML
363 lines
20 KiB
YAML
# ══════════════════════════════════════════════════════════════════════════════
|
||
# CI/CD Pipeline: Сборка и публикация релиза GenAudioBookInfo
|
||
# ══════════════════════════════════════════════════════════════════════════════
|
||
#
|
||
# ЗАПУСК ТОЛЬКО ЧЕРЕЗ make — два способа:
|
||
#
|
||
# 1. make release-tag VERSION=2.1.0
|
||
# Создаёт аннотированный git-тег v2.1.0 и пушит его в origin.
|
||
# Workflow запускается автоматически по событию push.tags.
|
||
#
|
||
# 2. make ci-release VERSION=2.1.0 GITEA_TOKEN=<токен>
|
||
# Вызывает Gitea API (workflow_dispatch) — запускает вручную
|
||
# без создания тега. Удобно для тестирования пайплайна.
|
||
#
|
||
# ──────────────────────────────────────────────────────────────────────────────
|
||
# ЭТАПЫ ПАЙПЛАЙНА:
|
||
#
|
||
# [Этап 1] quality — Статический анализ (go vet) + Unit-тесты
|
||
# Блокирует следующие этапы при ошибке.
|
||
#
|
||
# [Этап 2] build — Определение версии, кросс-компиляция 15 бинарников
|
||
# для Linux / macOS / Windows / FreeBSD / OpenBSD /
|
||
# NetBSD / MIPS / RISC-V, упаковка в архивы,
|
||
# генерация SHA256 контрольных сумм.
|
||
#
|
||
# [Этап 3] publish — Создание Gitea Release через API,
|
||
# загрузка всех архивов и checksums-sha256.txt.
|
||
#
|
||
# ──────────────────────────────────────────────────────────────────────────────
|
||
# НЕОБХОДИМАЯ НАСТРОЙКА:
|
||
#
|
||
# В настройках репозитория Gitea → Settings → Secrets добавить:
|
||
# GITEA_TOKEN — токен с правами write:repository и write:release
|
||
# (обычно автоматически инжектируется runner'ом)
|
||
#
|
||
# ══════════════════════════════════════════════════════════════════════════════
|
||
|
||
name: "Release CI/CD"
|
||
|
||
on:
|
||
# ── Способ 1: запуск через make ci-release ────────────────────────────────
|
||
workflow_dispatch:
|
||
inputs:
|
||
version:
|
||
description: "Версия релиза (например: 2.1.0). Без префикса 'v'."
|
||
required: false
|
||
default: ""
|
||
prerelease:
|
||
description: "Отметить как пре-релиз (alpha/beta)?"
|
||
required: false
|
||
default: "false"
|
||
|
||
# ── Способ 2: запуск через make release-tag ───────────────────────────────
|
||
push:
|
||
tags:
|
||
- "v[0-9]+.[0-9]+.[0-9]*"
|
||
|
||
# ──────────────────────────────────────────────────────────────────────────────
|
||
# Глобальные переменные окружения (доступны во всех jobs)
|
||
# ──────────────────────────────────────────────────────────────────────────────
|
||
env:
|
||
GO_VERSION: "1.24" # Версия Go для сборки
|
||
APP_NAME: genaudiobookinfo
|
||
BUILD_DIR: build
|
||
|
||
# ══════════════════════════════════════════════════════════════════════════════
|
||
jobs:
|
||
|
||
# ────────────────────────────────────────────────────────────────────────────
|
||
# ЭТАП 1: Проверка качества кода
|
||
# ────────────────────────────────────────────────────────────────────────────
|
||
quality:
|
||
name: "Этап 1 — Качество кода"
|
||
runs-on: ubuntu-latest
|
||
|
||
steps:
|
||
- name: "Получение исходного кода"
|
||
uses: actions/checkout@v4
|
||
|
||
- name: "Установка Go ${{ env.GO_VERSION }}"
|
||
uses: actions/setup-go@v5
|
||
with:
|
||
go-version: ${{ env.GO_VERSION }}
|
||
cache: true
|
||
|
||
- name: "Загрузка зависимостей (go mod download)"
|
||
run: make deps
|
||
|
||
- name: "Статический анализ (go vet)"
|
||
# Проверяет корректность кода без запуска: подозрительные
|
||
# конструкции, неправильные форматные строки, ошибки типов.
|
||
run: make vet
|
||
|
||
- name: "Unit-тесты (go test)"
|
||
# -count=1 — отключает кеш результатов (всегда свежий прогон)
|
||
# -timeout 5m — предел времени выполнения тестов
|
||
run: go test ./... -count=1 -timeout 5m
|
||
|
||
# ────────────────────────────────────────────────────────────────────────────
|
||
# ЭТАП 2: Кросс-компиляция и подготовка артефактов
|
||
# ────────────────────────────────────────────────────────────────────────────
|
||
build:
|
||
name: "Этап 2 — Кросс-компиляция"
|
||
runs-on: ubuntu-latest
|
||
needs: quality # Запускается только после успешного прохождения Этапа 1
|
||
|
||
outputs:
|
||
# Версия и тег передаются в Этап 3 через outputs
|
||
version: ${{ steps.ver.outputs.version }}
|
||
tag: ${{ steps.ver.outputs.tag }}
|
||
|
||
steps:
|
||
- name: "Получение исходного кода (полная история для тегов)"
|
||
uses: actions/checkout@v4
|
||
with:
|
||
fetch-depth: 0 # Нужна полная история для git describe
|
||
|
||
- name: "Установка Go ${{ env.GO_VERSION }}"
|
||
uses: actions/setup-go@v5
|
||
with:
|
||
go-version: ${{ env.GO_VERSION }}
|
||
cache: true
|
||
|
||
# ── Определение версии ────────────────────────────────────────────────
|
||
# Приоритет: git-тег > ручной ввод (inputs.version) > значение в Makefile
|
||
- name: "Определение версии релиза"
|
||
id: ver
|
||
run: |
|
||
if [ "${{ github.event_name }}" = "push" ]; then
|
||
# Запуск через make release-tag: версия берётся из имени тега
|
||
TAG="${{ github.ref_name }}" # например: v2.0.0
|
||
VER="${TAG#v}" # например: 2.0.0
|
||
elif [ -n "${{ inputs.version }}" ]; then
|
||
# Запуск через make ci-release с явной версией
|
||
VER="${{ inputs.version }}"
|
||
TAG="v${VER}"
|
||
else
|
||
# Версия не задана — берём из Makefile (строка VERSION := X.Y.Z)
|
||
VER=$(grep '^VERSION' Makefile | head -1 | sed 's/.*:=\s*//' | tr -d ' ')
|
||
TAG="v${VER}"
|
||
fi
|
||
|
||
echo "version=${VER}" >> "$GITHUB_OUTPUT"
|
||
echo "tag=${TAG}" >> "$GITHUB_OUTPUT"
|
||
echo "──────────────────────────────────"
|
||
echo " Версия: ${VER}"
|
||
echo " Git-тег: ${TAG}"
|
||
echo " Коммит: ${{ github.sha }}"
|
||
echo "──────────────────────────────────"
|
||
|
||
- name: "Загрузка зависимостей"
|
||
run: make deps
|
||
|
||
# ── Кросс-компиляция ─────────────────────────────────────────────────
|
||
# make build-all собирает 15 бинарников через GOOS/GOARCH
|
||
# VERSION перезаписывает значение из Makefile (инжектируется в ldflags)
|
||
- name: "Кросс-компиляция для всех платформ"
|
||
run: |
|
||
make build-all VERSION=${{ steps.ver.outputs.version }}
|
||
echo ""
|
||
echo "Собранные бинарники:"
|
||
ls -lh ${{ env.BUILD_DIR }}/
|
||
|
||
# ── Упаковка в архивы ─────────────────────────────────────────────────
|
||
# Windows → ZIP (стандарт для Windows-пользователей)
|
||
# Всё остальное → tar.gz (стандарт для Unix-систем)
|
||
- name: "Создание архивов (zip / tar.gz)"
|
||
run: |
|
||
cd ${{ env.BUILD_DIR }}
|
||
mkdir -p archives
|
||
|
||
echo "=== Windows → ZIP ==="
|
||
for f in *-windows-*.exe; do
|
||
[ -f "$f" ] || continue
|
||
ARCNAME="${f%.exe}.zip"
|
||
echo " ${f} → archives/${ARCNAME}"
|
||
zip "archives/${ARCNAME}" "$f"
|
||
done
|
||
|
||
echo "=== Unix → TAR.GZ ==="
|
||
for f in \
|
||
*-linux-* \
|
||
*-darwin-* \
|
||
*-freebsd-* \
|
||
*-openbsd-* \
|
||
*-netbsd-*; do
|
||
[[ "$f" == *.exe ]] && continue
|
||
[ -f "$f" ] || continue
|
||
ARCNAME="${f}.tar.gz"
|
||
echo " ${f} → archives/${ARCNAME}"
|
||
tar -czf "archives/${ARCNAME}" "$f"
|
||
done
|
||
|
||
echo ""
|
||
echo "Готовые архивы:"
|
||
ls -lh archives/
|
||
|
||
# ── Контрольные суммы ─────────────────────────────────────────────────
|
||
# SHA256 для каждого архива — пользователи могут проверить целостность
|
||
- name: "Генерация контрольных сумм SHA256"
|
||
run: |
|
||
cd ${{ env.BUILD_DIR }}/archives
|
||
sha256sum * | tee checksums-sha256.txt
|
||
echo ""
|
||
echo "checksums-sha256.txt создан ($(wc -l < checksums-sha256.txt) файлов)"
|
||
|
||
# ── Передача артефактов в Этап 3 ─────────────────────────────────────
|
||
- name: "Сохранение артефактов"
|
||
uses: actions/upload-artifact@v4
|
||
with:
|
||
name: release-assets-${{ steps.ver.outputs.tag }}
|
||
path: ${{ env.BUILD_DIR }}/archives/
|
||
retention-days: 5
|
||
if-no-files-found: error # Ошибка если архивы не созданы
|
||
|
||
# ────────────────────────────────────────────────────────────────────────────
|
||
# ЭТАП 3: Публикация релиза в Gitea
|
||
# ────────────────────────────────────────────────────────────────────────────
|
||
publish:
|
||
name: "Этап 3 — Публикация релиза"
|
||
runs-on: ubuntu-latest
|
||
needs: build # Запускается только после успешного Этапа 2
|
||
|
||
env:
|
||
VERSION: ${{ needs.build.outputs.version }}
|
||
TAG: ${{ needs.build.outputs.tag }}
|
||
PRERELEASE: ${{ inputs.prerelease || 'false' }}
|
||
|
||
steps:
|
||
- name: "Скачивание артефактов из Этапа 2"
|
||
uses: actions/download-artifact@v4
|
||
with:
|
||
name: release-assets-${{ needs.build.outputs.tag }}
|
||
path: ./artifacts
|
||
|
||
- name: "Список артефактов для публикации"
|
||
run: |
|
||
echo "Артефакты ($(ls ./artifacts | wc -l) файлов):"
|
||
ls -lh ./artifacts/
|
||
|
||
# ── Создание Gitea Release и загрузка файлов ─────────────────────────
|
||
# Используется Gitea REST API v1:
|
||
# POST /api/v1/repos/{owner}/{repo}/releases — создать релиз
|
||
# POST /api/v1/repos/{owner}/{repo}/releases/{id}/assets — загрузить файл
|
||
- name: "Создание релиза в Gitea и загрузка файлов"
|
||
env:
|
||
GITEA_TOKEN: ${{ secrets.GITEA_TOKEN }}
|
||
run: |
|
||
GITEA_URL="${{ github.server_url }}"
|
||
REPO="${{ github.repository }}"
|
||
COMMIT="${{ github.sha }}"
|
||
DATE=$(date -u '+%Y-%m-%d %H:%M UTC')
|
||
|
||
echo "──────────────────────────────────────────────"
|
||
echo " Сервер: ${GITEA_URL}"
|
||
echo " Репо: ${REPO}"
|
||
echo " Версия: ${TAG}"
|
||
echo " Коммит: ${COMMIT}"
|
||
echo "──────────────────────────────────────────────"
|
||
|
||
# ── Тело описания релиза (Markdown) ────────────────────────────
|
||
RELEASE_BODY="## GenAudioBookInfo ${TAG}
|
||
|
||
### Платформы
|
||
|
||
| ОС | Архитектура | Файл |
|
||
|---|---|---|
|
||
| Windows | amd64 (64-бит) | \`genaudiobookinfo-windows-amd64.zip\` |
|
||
| Windows | 386 (32-бит) | \`genaudiobookinfo-windows-386.zip\` |
|
||
| Windows | arm64 | \`genaudiobookinfo-windows-arm64.zip\` |
|
||
| Linux | amd64 | \`genaudiobookinfo-linux-amd64.tar.gz\` |
|
||
| Linux | arm64 | \`genaudiobookinfo-linux-arm64.tar.gz\` |
|
||
| Linux | armv7 | \`genaudiobookinfo-linux-armv7.tar.gz\` |
|
||
| Linux | 386 | \`genaudiobookinfo-linux-386.tar.gz\` |
|
||
| Linux | MIPS (big-endian) | \`genaudiobookinfo-linux-mips.tar.gz\` |
|
||
| Linux | MIPS (little-endian) | \`genaudiobookinfo-linux-mipsle.tar.gz\` |
|
||
| Linux | RISC-V 64 | \`genaudiobookinfo-linux-riscv64.tar.gz\` |
|
||
| macOS | amd64 (Intel) | \`genaudiobookinfo-darwin-amd64.tar.gz\` |
|
||
| macOS | arm64 (Apple Silicon) | \`genaudiobookinfo-darwin-arm64.tar.gz\` |
|
||
| FreeBSD | amd64 | \`genaudiobookinfo-freebsd-amd64.tar.gz\` |
|
||
| FreeBSD | arm64 | \`genaudiobookinfo-freebsd-arm64.tar.gz\` |
|
||
| OpenBSD | amd64 | \`genaudiobookinfo-openbsd-amd64.tar.gz\` |
|
||
| NetBSD | amd64 | \`genaudiobookinfo-netbsd-amd64.tar.gz\` |
|
||
|
||
> Контрольные суммы файлов: \`checksums-sha256.txt\`
|
||
|
||
### Сборка
|
||
|
||
- **Коммит:** \`${COMMIT}\`
|
||
- **Дата:** ${DATE}
|
||
- **Go:** ${{ env.GO_VERSION }}"
|
||
|
||
# ── Создать релиз через API ─────────────────────────────────────
|
||
echo ""
|
||
echo "Создаём релиз ${TAG}..."
|
||
|
||
RELEASE_JSON=$(curl -sf -X POST \
|
||
-H "Authorization: token ${GITEA_TOKEN}" \
|
||
-H "Content-Type: application/json" \
|
||
"${GITEA_URL}/api/v1/repos/${REPO}/releases" \
|
||
-d "{
|
||
\"tag_name\": \"${TAG}\",
|
||
\"target_commitish\": \"${COMMIT}\",
|
||
\"name\": \"${{ env.APP_NAME }} ${TAG}\",
|
||
\"body\": $(echo "$RELEASE_BODY" | python3 -c 'import sys,json; print(json.dumps(sys.stdin.read()))'),
|
||
\"draft\": false,
|
||
\"prerelease\": ${PRERELEASE}
|
||
}")
|
||
|
||
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 ./artifacts/*; 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 ${GITEA_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 "OK (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
|