Полная автоматизация релизного цикла. == Auto-update (electron-updater) == - src/main/updater.ts — обёртка над autoUpdater с дискриминированным UpdaterStatus union и broadcast через IPC. autoDownload=false, пользователь сам жмёт «Скачать». allowDowngrade=false. Проверка каждые 6 часов, первая через 5с после старта. - В dev-режиме (app.isPackaged=false) статус сразу становится 'unsupported' с пояснением — никаких exceptions из updater'а. - build.publish в package.json: provider=generic, url указывает на Gitea release assets конкретной версии. - src/main/ipc.ts: 4 новых канала — status/check/download/install. - src/preload: API window.api.updater* + onUpdaterStatus. - src/renderer/src/components/UpdaterCard.tsx: HUD-карточка в Settings с состояниями idle/checking/available/downloading/downloaded/error, прогресс-бар с скоростью в МБ/с. == Тесты (vitest) == - vitest.config.ts с алиасами @shared / @renderer - 23 теста, все зелёные: * format.test.ts — formatCountdown, formatInterval (8 cases) * vdf.test.ts — parseVdf / stringifyVdf / round-trip (11 cases) * types.test.ts — DEFAULT_SETTINGS, SAMPLE_EXERCISES sanity (4) - npm scripts: test (watch), test:run (CI) == CI/CD (Gitea Actions) == - .gitea/workflows/ci.yml — на push/PR: typecheck + тесты + smoke-сборка - .gitea/workflows/release.yml — на тег v*.*.*: сборка NSIS + Gitea release == Локальный релизный скрипт == - scripts/release.ps1 — один скрипт от бампа версии до публикации через Gitea API (params: -Bump patch/minor/major, -Version, -DryRun) - npm run release — обёртка - RELEASING.md — полная инструкция Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
197 lines
6.4 KiB
PowerShell
197 lines
6.4 KiB
PowerShell
<#
|
||
.SYNOPSIS
|
||
Локальный релиз: бамп версии → коммит → тег → push → сборка → upload в Gitea release.
|
||
|
||
.DESCRIPTION
|
||
Один скрипт от и до. Если Gitea Actions не настроено, это рабочая альтернатива.
|
||
|
||
.PARAMETER Bump
|
||
Какую часть semver инкрементировать: patch (по умолчанию), minor, major.
|
||
Альтернатива — указать -Version явно.
|
||
|
||
.PARAMETER Version
|
||
Точная версия (напр. "0.3.0"). Если задана, -Bump игнорируется.
|
||
|
||
.PARAMETER SkipBuild
|
||
Пропустить сборку (если уже собрано вручную, .exe лежит в release/).
|
||
|
||
.PARAMETER DryRun
|
||
Показать что произойдёт, но ничего не делать.
|
||
|
||
.EXAMPLE
|
||
pwsh scripts/release.ps1 -Bump minor
|
||
pwsh scripts/release.ps1 -Version 0.3.0
|
||
pwsh scripts/release.ps1 -Bump patch -DryRun
|
||
|
||
.NOTES
|
||
Требует переменную окружения GITEA_TOKEN с правом write:repository
|
||
(создаётся в Gitea: Settings → Applications → Generate New Token).
|
||
#>
|
||
param(
|
||
[ValidateSet('patch', 'minor', 'major')]
|
||
[string]$Bump = 'patch',
|
||
[string]$Version,
|
||
[switch]$SkipBuild,
|
||
[switch]$DryRun
|
||
)
|
||
|
||
$ErrorActionPreference = 'Stop'
|
||
|
||
# --- Config ---------------------------------------------------------------
|
||
$repoOwner = 'AnRil'
|
||
$repoName = 'laude'
|
||
$giteaHost = 'xn--90adajar8af4h.xn--p1ai/git'
|
||
$apiBase = "https://$giteaHost/api/v1"
|
||
|
||
# --- Pre-flight checks ---------------------------------------------------
|
||
$root = Resolve-Path (Join-Path $PSScriptRoot '..')
|
||
Set-Location $root
|
||
|
||
if (-not $env:GITEA_TOKEN -and -not $DryRun) {
|
||
Write-Error 'GITEA_TOKEN не задан. Создай в Gitea Settings → Applications и export GITEA_TOKEN=...'
|
||
exit 1
|
||
}
|
||
|
||
$status = git status --porcelain
|
||
if ($status) {
|
||
Write-Error "Есть незакоммиченные изменения. Сначала закоммить или stash."
|
||
exit 1
|
||
}
|
||
|
||
$branch = git rev-parse --abbrev-ref HEAD
|
||
if ($branch -ne 'main') {
|
||
Write-Warning "Текущая ветка не main, а $branch. Продолжить? (Ctrl+C для отмены)"
|
||
Read-Host 'Press Enter'
|
||
}
|
||
|
||
# --- Compute next version ------------------------------------------------
|
||
$pkg = Get-Content package.json | ConvertFrom-Json
|
||
$current = $pkg.version
|
||
|
||
if ($Version) {
|
||
$next = $Version
|
||
} else {
|
||
$parts = $current.Split('.')
|
||
$major = [int]$parts[0]; $minor = [int]$parts[1]; $patch = [int]$parts[2]
|
||
switch ($Bump) {
|
||
'major' { $major++; $minor = 0; $patch = 0 }
|
||
'minor' { $minor++; $patch = 0 }
|
||
'patch' { $patch++ }
|
||
}
|
||
$next = "$major.$minor.$patch"
|
||
}
|
||
|
||
$tag = "v$next"
|
||
Write-Host ""
|
||
Write-Host "→ Release plan" -ForegroundColor Cyan
|
||
Write-Host " current : v$current"
|
||
Write-Host " next : $tag"
|
||
Write-Host " bump : $Bump"
|
||
Write-Host ""
|
||
|
||
if ($DryRun) {
|
||
Write-Host '(dry run — exiting)' -ForegroundColor Yellow
|
||
exit 0
|
||
}
|
||
|
||
# --- Bump version in package.json ---------------------------------------
|
||
Write-Host "→ Bumping package.json to $next…" -ForegroundColor Cyan
|
||
$pkgJson = (Get-Content package.json -Raw) -replace "`"version`":\s*`"$current`"", "`"version`": `"$next`""
|
||
Set-Content -Path package.json -Value $pkgJson -NoNewline -Encoding utf8
|
||
|
||
git add package.json
|
||
git commit -m "chore(release): $tag"
|
||
|
||
# --- Build (typecheck + tests + dist) ------------------------------------
|
||
if (-not $SkipBuild) {
|
||
Write-Host "→ Running typecheck…" -ForegroundColor Cyan
|
||
npm run typecheck
|
||
if ($LASTEXITCODE -ne 0) { exit $LASTEXITCODE }
|
||
|
||
Write-Host "→ Running tests…" -ForegroundColor Cyan
|
||
npm run test:run
|
||
if ($LASTEXITCODE -ne 0) { exit $LASTEXITCODE }
|
||
|
||
Write-Host "→ Building installer (npm run dist)…" -ForegroundColor Cyan
|
||
npm run dist
|
||
if ($LASTEXITCODE -ne 0) { exit $LASTEXITCODE }
|
||
}
|
||
|
||
# --- Verify artifacts exist ---------------------------------------------
|
||
$installer = Join-Path 'release' "Exercise-Reminder-Setup-$next.exe"
|
||
$blockmap = "$installer.blockmap"
|
||
$manifest = Join-Path 'release' 'latest.yml'
|
||
foreach ($f in @($installer, $blockmap, $manifest)) {
|
||
if (-not (Test-Path $f)) {
|
||
Write-Error "Не найден артефакт: $f"
|
||
exit 1
|
||
}
|
||
}
|
||
|
||
# --- Tag + push ----------------------------------------------------------
|
||
Write-Host "→ Tagging $tag and pushing…" -ForegroundColor Cyan
|
||
git tag -a $tag -m "Release $tag"
|
||
git push origin main
|
||
git push origin $tag
|
||
|
||
# --- Create release via Gitea API ----------------------------------------
|
||
Write-Host "→ Creating Gitea release $tag…" -ForegroundColor Cyan
|
||
$headers = @{
|
||
Authorization = "token $env:GITEA_TOKEN"
|
||
Accept = 'application/json'
|
||
}
|
||
|
||
# Release notes from commits since previous tag
|
||
$prev = git describe --tags --abbrev=0 "$tag^" 2>$null
|
||
if ($prev) {
|
||
$log = git log --pretty=format:"- %s" "$prev..$tag" | Out-String
|
||
} else {
|
||
$log = git log --pretty=format:"- %s" "$tag" | Out-String
|
||
}
|
||
$body = @"
|
||
### Изменения
|
||
|
||
$log
|
||
|
||
---
|
||
|
||
**Установщик ниже** — запустить и следовать мастеру. Если приложение уже стояло — обновится поверх, настройки сохранятся.
|
||
"@
|
||
|
||
$releaseBody = @{
|
||
tag_name = $tag
|
||
name = "Exercise Reminder $tag"
|
||
body = $body
|
||
draft = $false
|
||
prerelease = $false
|
||
} | ConvertTo-Json -Depth 5
|
||
|
||
$release = Invoke-RestMethod `
|
||
-Uri "$apiBase/repos/$repoOwner/$repoName/releases" `
|
||
-Method Post `
|
||
-Headers $headers `
|
||
-Body $releaseBody `
|
||
-ContentType 'application/json'
|
||
|
||
Write-Host " Release id: $($release.id)" -ForegroundColor DarkGray
|
||
|
||
# --- Upload assets -------------------------------------------------------
|
||
foreach ($asset in @($installer, $blockmap, $manifest)) {
|
||
$name = Split-Path $asset -Leaf
|
||
Write-Host "→ Uploading $name…" -ForegroundColor Cyan
|
||
$uri = "$apiBase/repos/$repoOwner/$repoName/releases/$($release.id)/assets?name=$([uri]::EscapeDataString($name))"
|
||
Invoke-RestMethod `
|
||
-Uri $uri `
|
||
-Method Post `
|
||
-Headers $headers `
|
||
-InFile $asset `
|
||
-ContentType 'application/octet-stream' | Out-Null
|
||
}
|
||
|
||
$releaseUrl = "https://$giteaHost/$repoOwner/$repoName/releases/tag/$tag"
|
||
Write-Host ""
|
||
Write-Host "Release published" -ForegroundColor Green
|
||
Write-Host " $releaseUrl"
|
||
Write-Host ""
|
||
Write-Host "Auto-updater подхватит обновление на установленных копиях в течение ~6 часов."
|