Вынос логики релиза в scripts/release.ps1

This commit is contained in:
Dmitriy Fofanov
2026-02-23 22:25:04 +03:00
parent 91130aed33
commit c98392aea0
2 changed files with 247 additions and 103 deletions
+239
View File
@@ -0,0 +1,239 @@
<#
.SYNOPSIS
Полный локальный релиз: сборка + архивы + тег + публикация в Gitea.
.DESCRIPTION
Вызывается из Makefile: make release VERSION=X.Y.Z GIT_TOKEN=xxx
.PARAMETER Version
Версия релиза (например, 2.0.0). Обязательный.
.PARAMETER Token
Gitea API-токен. Если не указан — только локальная сборка.
.PARAMETER AppName
Имя приложения (передаётся из Makefile).
.PARAMETER BuildDir
Директория сборки (передаётся из Makefile).
.PARAMETER GiteaUrl
URL Gitea-сервера.
.PARAMETER GiteaRepo
Репозиторий в формате owner/repo.
.PARAMETER GoVersion
Версия Go (для описания релиза).
#>
param(
[Parameter(Mandatory)][string]$Version,
[string]$Token,
[string]$AppName = "genaudiobookinfo",
[string]$BuildDir = "build",
[string]$GiteaUrl = "https://github.dfv24.com",
[string]$GiteaRepo = "fofanov.dmitry/GenAudioBookInfo",
[string]$GoVersion = "unknown"
)
$ErrorActionPreference = "Stop"
$tag = "v$Version"
# ── Валидация ─────────────────────────────────────────────────────────────────
if ($Version -eq 'dev' -or $Version -eq '') {
Write-Host '!!! VERSION не указана. Использование: make release VERSION=X.Y.Z' -ForegroundColor Red
exit 1
}
$dirty = git status --porcelain
if ($dirty) {
Write-Host '!!! Есть незакоммиченные изменения. Сначала сделайте git commit.' -ForegroundColor Red
git status --short
exit 1
}
# ── Начало ────────────────────────────────────────────────────────────────────
Write-Host ""
Write-Host "══════════════════════════════════════════════" -ForegroundColor Cyan
Write-Host " Создание релиза $tag" -ForegroundColor Cyan
Write-Host "══════════════════════════════════════════════" -ForegroundColor Cyan
Write-Host ""
# ── [1/6] Кросс-компиляция ───────────────────────────────────────────────────
Write-Host ">>> [1/6] Кросс-компиляция для всех платформ..." -ForegroundColor Yellow
$env:GOOS = ''
$env:GOARCH = ''
Remove-Item Env:GOARM, Env:GOMIPS -ErrorAction SilentlyContinue
& make build-all VERSION=$Version
if ($LASTEXITCODE -ne 0) {
Write-Host "!!! Ошибка компиляции." -ForegroundColor Red
exit 1
}
# ── [2/6] Создание архивов ────────────────────────────────────────────────────
Write-Host ""
Write-Host ">>> [2/6] Создание архивов..." -ForegroundColor Yellow
$archDir = Join-Path $BuildDir "archives"
New-Item -ItemType Directory -Force $archDir | Out-Null
Get-ChildItem $BuildDir -File | Where-Object { $_.Name -ne '.env' } | ForEach-Object {
if ($_.Name -match '\.exe$') {
$arcName = $_.BaseName + '.zip'
Write-Host " $($_.Name) -> $arcName"
Compress-Archive -Path $_.FullName -DestinationPath (Join-Path $archDir $arcName) -Force
}
elseif ($_.Name -match "^$([regex]::Escape($AppName))-") {
$arcName = $_.Name + '.tar.gz'
Write-Host " $($_.Name) -> $arcName"
tar -czf (Join-Path $archDir $arcName) -C $BuildDir $_.Name
}
}
# ── [3/6] SHA256 ──────────────────────────────────────────────────────────────
Write-Host ""
Write-Host ">>> [3/6] Генерация SHA256..." -ForegroundColor Yellow
$checksumFile = Join-Path $archDir "checksums-sha256.txt"
$lines = Get-ChildItem $archDir -File |
Where-Object { $_.Name -ne 'checksums-sha256.txt' } |
Get-FileHash -Algorithm SHA256 |
ForEach-Object { "$($_.Hash.ToLower()) $(Split-Path $_.Path -Leaf)" }
$lines | Out-File -Encoding UTF8 $checksumFile
Write-Host " checksums-sha256.txt создан"
# ── [4/6] Тег ─────────────────────────────────────────────────────────────────
Write-Host ""
Write-Host ">>> [4/6] Создание тега $tag..." -ForegroundColor Yellow
$existingTag = git tag -l $tag
if ($existingTag) {
Write-Host " Тег $tag уже существует, пропускаю создание" -ForegroundColor Gray
} else {
git tag -a $tag -m "Release $tag"
}
git push origin $tag 2>$null
git push origin master 2>$null
# ── Проверка токена ───────────────────────────────────────────────────────────
if (-not $Token) {
Write-Host ""
Write-Host "══════════════════════════════════════════════" -ForegroundColor Yellow
Write-Host " Локальная сборка завершена." -ForegroundColor Yellow
Write-Host " Для загрузки в Gitea укажите GIT_TOKEN:" -ForegroundColor Yellow
Write-Host " make release VERSION=$Version GIT_TOKEN=<токен>" -ForegroundColor Yellow
Write-Host " Архивы: $archDir\" -ForegroundColor Yellow
Write-Host "══════════════════════════════════════════════" -ForegroundColor Yellow
exit 0
}
# ── [5/6] Создание релиза в Gitea ────────────────────────────────────────────
Write-Host ""
Write-Host ">>> [5/6] Создание релиза в Gitea..." -ForegroundColor Yellow
$commit = git rev-parse HEAD
$date = Get-Date -Format 'dd.MM.yyyy HH:mm UTC'
$prevTag = ''
try { $prevTag = git describe --tags --abbrev=0 "$tag^" 2>$null } catch {}
$rawLog = @()
if ($prevTag) {
$rawLog = git log --pretty=format:"%s" "$prevTag..$tag" --no-merges 2>$null
} else {
$rawLog = git log --pretty=format:"%s" --no-merges 2>$null | Select-Object -First 50
}
$features = @()
$fixes = @()
$refactor = @()
$other = @()
($rawLog -split "`n") | Where-Object { $_ } | ForEach-Object {
$low = $_.ToLower()
if ($low -match '^(функция|feat|feature|добавлен|реализован|новое)') { $features += "- $_" }
elseif ($low -match '^(исправлен|fix|bugfix|баг|ошибка)') { $fixes += "- $_" }
elseif ($low -match '^(рефакторинг|refactor|оптимизац|улучшен)') { $refactor += "- $_" }
else { $other += "- $_" }
}
# Формируем тело релиза
$body = "## GenAudioBookInfo $tag`n`n"
if (Test-Path 'RELEASE_NOTES.md') {
$body += (Get-Content 'RELEASE_NOTES.md' -Raw) + "`n`n"
}
if ($features.Count -gt 0) { $body += "### Новые возможности`n" + ($features -join "`n") + "`n`n" }
if ($fixes.Count -gt 0) { $body += "### Исправления`n" + ($fixes -join "`n") + "`n`n" }
if ($refactor.Count -gt 0) { $body += "### Рефакторинг`n" + ($refactor -join "`n") + "`n`n" }
if ($other.Count -gt 0) { $body += "### Прочие изменения`n" + ($other -join "`n") + "`n`n" }
$body += "---`n`n### Информация о сборке`n`n"
$body += "| Параметр | Значение |`n|---|---|`n"
$body += "| Коммит | ``$commit`` |`n"
$body += "| Дата | $date |`n"
$body += "| Go | $GoVersion |`n"
# API: создание релиза
$releaseData = @{
tag_name = $tag
target_commitish = $commit
name = "$AppName $tag"
body = $body
draft = $false
prerelease = $false
} | ConvertTo-Json -Compress -Depth 5
$headers = @{
Authorization = "token $Token"
'Content-Type' = 'application/json'
}
$releaseResp = Invoke-RestMethod -Method Post `
-Uri "$GiteaUrl/api/v1/repos/$GiteaRepo/releases" `
-Headers $headers -Body ([System.Text.Encoding]::UTF8.GetBytes($releaseData)) `
-ErrorAction Stop
$releaseId = $releaseResp.id
Write-Host " Релиз создан: ID=$releaseId"
# ── [6/6] Загрузка файлов ────────────────────────────────────────────────────
Write-Host ""
Write-Host ">>> [6/6] Загрузка файлов..." -ForegroundColor Yellow
$ok = 0
$fail = 0
Get-ChildItem $archDir -File | ForEach-Object {
$fname = $_.Name
$fsize = '{0:N1} MB' -f ($_.Length / 1MB)
Write-Host -NoNewline (" {0,-55} [{1,6}] " -f $fname, $fsize)
try {
$bytes = [System.IO.File]::ReadAllBytes($_.FullName)
$uploadHeaders = @{
Authorization = "token $Token"
'Content-Type' = 'application/octet-stream'
}
Invoke-RestMethod -Method Post `
-Uri "$GiteaUrl/api/v1/repos/$GiteaRepo/releases/$releaseId/assets?name=$fname" `
-Headers $uploadHeaders -Body $bytes -ErrorAction Stop | Out-Null
Write-Host "OK" -ForegroundColor Green
$ok++
} catch {
Write-Host "ОШИБКА: $_" -ForegroundColor Red
$fail++
}
}
# ── Итог ──────────────────────────────────────────────────────────────────────
Write-Host ""
Write-Host "══════════════════════════════════════════════" -ForegroundColor Green
Write-Host " Загружено: $ok | Ошибок: $fail" -ForegroundColor Green
Write-Host " Релиз: $GiteaUrl/$GiteaRepo/releases/tag/$tag" -ForegroundColor Green
Write-Host "══════════════════════════════════════════════" -ForegroundColor Green
if ($fail -gt 0) { exit 1 }