<# .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 $ErrorActionPreference = "Continue" & make build-all VERSION=$Version $buildExit = $LASTEXITCODE $ErrorActionPreference = "Stop" if ($buildExit -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" } $ErrorActionPreference = "Continue" git push origin $tag 2>&1 | Out-Null git push origin master 2>&1 | Out-Null $ErrorActionPreference = "Stop" # ── Проверка токена ─────────────────────────────────────────────────────────── 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 = '' $ErrorActionPreference = "Continue" try { $prevTag = git describe --tags --abbrev=0 "$tag^" 2>&1 | Where-Object { $_ -is [string] } } catch {} $ErrorActionPreference = "Stop" $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 }