Релиз pipeline¶
End-to-end процесс выкатки новой версии Miami Graphics. От git commit до того момента, когда юзеры получают auto-update notification.
Шаги в нормальном порядке¶
flowchart TD
Commit[git commit + push] --> Test[Smoke test локально]
Test --> Bump[Bump версии в csproj и Inno]
Bump --> Publish[scripts/publish.ps1]
Publish --> Sign[опционально: signtool sign на .exe]
Sign --> Installer[scripts/build_installer.ps1]
Installer --> Upload[Upload в R2 через rclone]
Upload --> Supa[INSERT в app_versions]
Supa --> Smoke[Юзер-flow смоук-тест]
Smoke --> Done[Юзеры через 1 час получат update notification]
Полный скрипт¶
param(
[Parameter(Mandatory)] [string]$Version,
[string]$ReleaseNotes = "Bug fixes and improvements",
[switch]$DryRun
)
$ErrorActionPreference = "Stop"
Write-Host "=== Releasing Miami Graphics $Version ===" -ForegroundColor Cyan
# 1. Build
.\scripts\publish.ps1 -Version $Version
.\scripts\build_installer.ps1 -Version $Version
$installerExe = "installer/output/MiamiGraphicsSetup_$Version.exe"
if (-not (Test-Path $installerExe)) { throw "Installer не собрался" }
if ($DryRun) {
Write-Host "DryRun — выйдем тут. Installer: $installerExe"
return
}
# 2. Upload в R2
$r2Path = "r2:huntergraphics/releases/MiamiGraphicsSetup_$Version.exe"
rclone copy $installerExe r2:huntergraphics/releases/
Write-Host "Uploaded to $r2Path"
# 3. INSERT в Supabase
$supabaseUrl = $env:SUPABASE_URL
$serviceKey = $env:SUPABASE_SERVICE_KEY
$installerUrl = "https://cdn.miamigraphicsstorage.uk/releases/MiamiGraphicsSetup_$Version.exe"
# Сбрасываем is_latest у всех старых
Invoke-RestMethod -Method Patch `
-Uri "$supabaseUrl/rest/v1/app_versions?is_latest=eq.true" `
-Headers @{ "apikey" = $serviceKey; "Authorization" = "Bearer $serviceKey"; "Content-Type" = "application/json" } `
-Body '{"is_latest": false}'
# Ставим новый
Invoke-RestMethod -Method Post `
-Uri "$supabaseUrl/rest/v1/app_versions" `
-Headers @{ "apikey" = $serviceKey; "Authorization" = "Bearer $serviceKey"; "Content-Type" = "application/json" } `
-Body (@{
version = $Version
installer_url = $installerUrl
release_notes = $ReleaseNotes
is_latest = $true
} | ConvertTo-Json)
Write-Host "Supabase app_versions updated to $Version" -ForegroundColor Green
Write-Host "Юзеры получат update notification при следующем запуске лаунчера"
Smoke-tests перед release'ом¶
Прогоняем вручную:
-
npm run buildбез warning'ов. -
dotnet publishбез warning'ов. - Запуск .exe из publish — лаунчер открывается, UI грузится.
- Login flow работает.
- Open Browse → видим redux'ы (Supabase connection ok).
- Open one redux → preview грузится (R2 connection ok).
- Install какого-нибудь test-redux'а на тестовый GTA → success.
- Запуск GTA → визуально применилось.
- Restore Clean → откатилось.
- Settings → Test bypass → проходит.
Если что-то падает — НЕ release'им, фиксим, начинаем сначала.
Code signing (TODO)¶
Сейчас наш .exe не подписан — Windows показывает SmartScreen warning «Unknown publisher» при первом запуске. Юзеры жмут «More info → Run anyway».
В roadmap'е — Authenticode сертификат от DigiCert/Sectigo (~$200/год standard, $400/год EV). EV убирает SmartScreen warning сразу, standard — после ~3 месяцев reputation building.
Не сделали пока потому что: - $200-400/год — реальный money cost для solo-dev project'а. - SmartScreen warning ловят 99% юзеров «running anyway» — не блокер. - Russian users не верят EV signs всё равно (TLS-frag не вяжется с «trusted publisher» concept).
Rollback процедура¶
Если после release обнаружили critical bug:
- В Supabase ставим
is_latest = falseдля проблемной версии: - Старая версия (
1.0.1) снова becomes «latest». - Юзеры на
1.0.2НЕ откатываются автоматически (auto-update только forward). Но новые юзеры качают1.0.1. - Фиксим bug, выкатываем
1.0.3. Юзеры с1.0.2обновятся в1.0.3напрямую.
R2 файл 1.0.2.exe оставляем на R2 — на случай дебага потом.
Версионирование¶
Major.Minor.Patch (SemVer-ish):
- Major (
2.0.0) — breaking changes в data model или workflow. Никогда не делали. - Minor (
1.1.0) — новые features (новый component type, новая admin-секция). - Patch (
1.0.X) — bug fixes, мелкие UI improvements.
Сейчас на 1.0.X — мы в active development, не bumped в 1.1 ещё.
CI/CD (TODO)¶
Сейчас release полностью manual — я (Hunter) запускаю release.ps1 со своей машины. Это работает на маленькой команде (1 dev), но плохо scale'ится.
В roadmap'е — GitHub Actions:
name: Release
on:
push:
tags: ['v*']
jobs:
build:
runs-on: windows-latest
steps:
- uses: actions/checkout@v4
- uses: actions/setup-dotnet@v4
- uses: actions/setup-node@v4
- name: Build UI
run: cd HunterGraphics.Shell/ui && npm ci && npm run build
- name: Publish
run: ./scripts/publish.ps1 -Version ${{ github.ref_name }}
- name: Build Installer
run: ./scripts/build_installer.ps1 -Version ${{ github.ref_name }}
- name: Upload to R2
env:
AWS_ACCESS_KEY_ID: ${{ secrets.R2_KEY }}
AWS_SECRET_ACCESS_KEY: ${{ secrets.R2_SECRET }}
run: aws s3 cp installer/output/*.exe s3://huntergraphics/releases/ --endpoint-url=https://<id>.r2.cloudflarestorage.com
- name: Update Supabase
env:
SUPABASE_URL: ${{ secrets.SUPABASE_URL }}
SUPABASE_KEY: ${{ secrets.SUPABASE_SERVICE_KEY }}
run: pwsh ./scripts/update_supabase_version.ps1 -Version ${{ github.ref_name }}
Это автоматизирует release полностью — git tag v1.0.3 && git push --tags и через 10 минут юзеры получают update. Не сделали пока потому что local release работает, secrets management в GitHub Actions — отдельная задача.