fix(ci): headless в CI, UI-бинарь собирается локально

CI (.gitea/workflows/ci.yaml):
- go 1.23 -> 1.25 (modernc.org/sqlite v1.56.0 требует go 1.25; старый CI падал
  на сборке ещё до тестов).
- CGO_ENABLED=0 на уровне workflow: CI собирает/тестирует только headless
  (ui_noui), не тянет Fyne/glfw и C-компилятор.
- timeout 120s -> 300s (worker один ~30-60s на быстрой машине).
- build-and-package: только linux/amd64 (headless). Windows-бинарь с UI — локально.

Локальная сборка UI + публикация:
- scripts/build-publish-ui.ps1: собирает ratatoskr-windows-amd64.exe (CGO_ENABLED=1,
  WinLibs gcc), вшивает commit-<sha7> + updateToken, пишет .version/.sha256 и
  публикует в Gitea Packages в ту же версию, что и CI; самопроверка (U4).
- scripts/.gitea-creds.example + .gitignore (scripts/.gitea-creds) — секреты не
  попадают в репозиторий.

README: CI=headless + публикация Windows-UI локальным скриптом; обновлён intro
(бинарь по умолчанию headless, окно — опционально с cgo).
This commit is contained in:
ki.sagidullin
2026-08-20 17:51:25 +05:00
parent c2272137b3
commit 5e7fb58049
6 changed files with 201 additions and 21 deletions

View File

@@ -0,0 +1,6 @@
# Секреты для scripts/build-publish-ui.ps1 — НЕ коммитить (git-ignored).
# Скопируйте этот файл в scripts/.gitea-creds и заполните реальные значения.
# Формат: по одной `ключ=значение` на строку, строки с # — комментарии.
GITEA_TOKEN=write:packages-токен
UPDATE_TOKEN=read:package-токен
GIT_MAIN_URL=http://gitea.hal9000.home

View File

@@ -0,0 +1,108 @@
<#
build-publish-ui.ps1 — локальная сборка Windows-бинаря с UI (Fyne) и публикация в Gitea Packages.
Почему локально: Fyne-окно требует cgo + C-компилятор (MinGW/gcc), которых нет в CI.
CI собирает ТОЛЬКО headless-linux (см. .gitea/workflows/ci.yaml). Windows-бинарь
с графическим окном собирается на Windows-машине Камиля и публикуется скриптом ниже
в ту же версию commit-<sha7>, что и CI, с теми же companion-файлами (.version/.sha256),
чтобы автообновление (U4 checksum, ResolveLatest) работало без изменений.
Требования:
- Go с доступным C-компилятором (CGO_ENABLED=1). WinLibs gcc: см. память
`mem:toolchain/cgo-winlibs-gcc` — bin нужно добавить в PATH перед запуском.
- Файл scripts/.gitea-creds (git-ignored, см. ниже) с токенами.
Формат (по одной `ключ=значение` на строку):
GITEA_TOKEN=write:packages-токен
UPDATE_TOKEN=read:package-токен
GIT_MAIN_URL=http://gitea.hal9000.home
- Ветка уже запушена в `main` (VERSION = commit-<sha7> от текущего HEAD).
Пример:
powershell -ExecutionPolicy Bypass -File scripts/build-publish-ui.ps1
#>
$ErrorActionPreference = "Stop"
$ScriptDir = Split-Path -Parent $MyInvocation.MyCommand.Path
$RepoRoot = Split-Path -Parent $ScriptDir
$CredsFile = Join-Path $ScriptDir ".gitea-creds"
$Package = "ratatoskr"
$Owner = "kamelion"
$Filename = "ratatoskr-windows-amd64.exe"
Set-Location $RepoRoot
# --- 0. Проверка C-тулчейна ---
if ($env:CGO_ENABLED -eq "0") { Write-Error "CGO_ENABLED=0 — нужен C-компилятор (MinGW). Уберите его из env." }
if (-not (Get-Command gcc -ErrorAction SilentlyContinue)) {
Write-Host "gcc не найден в PATH. Пример (WinLibs):"
Write-Host ' $p = "<...>\mingw64\bin"; $env:Path = "$p;$env:Path"'
Write-Error "C-компилятор gcc не найден."
}
Write-Host "[ok] gcc: $((gcc --version | Select-Object -First 1))"
# --- 1. Чтение секретов ---
if (-not (Test-Path $CredsFile)) {
Write-Error "Нет файла секретов: $CredsFile`nФормат: GITEA_TOKEN=... / UPDATE_TOKEN=... / GIT_MAIN_URL=..."
}
$creds = @{}
Get-Content $CredsFile | ForEach-Object {
$line = $_.Trim()
if ($line -and -not $line.StartsWith("#")) {
$kv = $line.Split("=", 2)
if ($kv.Count -eq 2) { $creds[$kv[0].Trim()] = $kv[1].Trim() }
}
}
$giteaToken = $creds["GITEA_TOKEN"]
$updateToken = $creds["UPDATE_TOKEN"]
$giteaUrl = $creds["GIT_MAIN_URL"]
if (-not $giteaToken) { Write-Error "В $CredsFile нет GITEA_TOKEN (нужен write:packages)." }
if (-not $giteaUrl) { Write-Error "В $CredsFile нет GIT_MAIN_URL." }
# --- 2. Версия = commit-<sha7> текущего HEAD (совпадает с именованием CI) ---
$sha = (git rev-parse --short HEAD).Trim()
if (-not $sha) { Write-Error "git rev-parse --short HEAD не дал хэш." }
$Version = "commit-$sha"
Write-Host "[build] VERSION=$Version FILENAME=$Filename"
# --- 3. Сборка (cgo + UI) ---
$env:CGO_ENABLED = "1"
$out = Join-Path $RepoRoot $Filename
$ldflags = "-s -w -X main.version=${Version} -X main.updateToken=${updateToken}"
Write-Host "[build] go build -ldflags=... -o $Filename"
go build -ldflags=$ldflags -o $out ./cmd/ratatoskr/
if ($LASTEXITCODE -ne 0) { Write-Error "go build завершился с кодом $LASTEXITCODE" }
# --- 4. Companion-файлы (.version, .sha256) ---
$sha256Hex = (Get-FileHash -Algorithm SHA256 $out).Hash.ToLower()
$verFile = "$out.version"
$sumFile = "$out.sha256"
Set-Content -Path $verFile -Value $Version -NoNewline -Encoding ascii
Set-Content -Path $sumFile -Value $sha256Hex -NoNewline -Encoding ascii
Write-Host "[ok] bin: $out ($([math]::Round((Get-Item $out).Length/1MB,1)) MB)"
Write-Host "[ok] sha256: $sha256Hex"
# --- 5. Публикация в Gitea Packages ---
$api = "$giteaUrl/api/packages/$Owner/generic/$Package/$Version"
$files = @($Filename, "$Filename.version", "$Filename.sha256")
foreach ($f in $files) {
Write-Host "[put] $api/$f"
curl.exe -sS -X PUT `
-H "Authorization: token $giteaToken" `
-H "Content-Type: application/octet-stream" `
"$api/$f" `
--data-binary "@$f"
if ($LASTEXITCODE -ne 0) { Write-Error "curl PUT $f завершился с кодом $LASTEXITCODE" }
}
# --- 6. Самопроверка: читаем .sha256 обратно и сверяем (контракт U4) ---
Write-Host "[verify] перечитываю $Filename.sha256 из Gitea..."
$remoteSum = (curl.exe -sS -H "Authorization: token $giteaToken" "$api/$Filename.sha256").Trim()
if ($remoteSum -ne $sha256Hex) {
Write-Error "Проверка не сошлась: remote=$remoteSum local=$sha256Hex"
}
$remoteVer = (curl.exe -sS -H "Authorization: token $giteaToken" "$api/$Filename.version").Trim()
if ($remoteVer -ne $Version) {
Write-Error "Версия не сошлась: remote=$remoteVer local=$Version"
}
Write-Host "[ok] опубликовано и проверено: $Version/$Filename (+.version, +.sha256)"