<# 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-, что и CI, с теми же companion-файлами (.version/.sha256), чтобы автообновление (U4 checksum, ResolveLatest) работало без изменений. Требования: - Go с доступным C-компилятором (CGO_ENABLED=1). WinLibs gcc: см. память `mem:binator/cgo-winlibs-gcc`. Каталог с gcc.exe можно задать через GCC_PATH (ниже); если не задан — gcc должен быть уже в PATH. - Файл scripts/.gitea-creds (git-ignored, см. ниже) с токенами. Формат (по одной `ключ=значение` на строку): GITEA_TOKEN=write:packages-токен UPDATE_TOKEN=read:package-токен GIT_MAIN_URL=http://gitea.hal9000.home PROJECT_PATH=..\..\foo (опционально) корень проекта (где лежат cmd/ и go.mod); относительно папки scripts/ или абсолютный; без него — папка рядом с скриптом GCC_PATH=D:\MinGW\bin (опционально) каталог, где лежит gcc.exe; дописывается в PATH перед проверкой и сборкой; абсолютный или относительно scripts/; без него — gcc из текущего PATH - Ветка уже запушена в `main` (VERSION = commit- от текущего HEAD). Пример: powershell -ExecutionPolicy Bypass -File scripts/build-publish-ui.ps1 #> $ErrorActionPreference = "Stop" $ScriptDir = Split-Path -Parent $MyInvocation.MyCommand.Path $CredsFile = Join-Path $ScriptDir ".gitea-creds" $Package = "ratatoskr" $Owner = "kamelion" $Filename = "ratatoskr-windows-amd64.exe" # --- 0. Проверка флага CGO_ENABLED (не зависит от creds) --- if ($env:CGO_ENABLED -eq "0") { Write-Error "CGO_ENABLED=0 — нужен C-компилятор (MinGW). Уберите его из env." } # --- 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." } # --- 1a. C-тулчейн (gcc): GCC_PATH из creds дописывается в PATH --- # Путь абсолютный либо относительно папки scripts/; без GCC_PATH — gcc # берётся из уже установленного PATH. $gccPath = $creds["GCC_PATH"] if ($gccPath -ne "" -and $gccPath) { $gccAbs = Join-Path $ScriptDir $gccPath $gccBin = (Resolve-Path -LiteralPath $gccAbs -ErrorAction SilentlyContinue).Path if (-not $gccBin) { $gccAbs = $gccPath # не нашлось относительно scripts/ — пробуем как абсолютный $gccBin = (Resolve-Path -LiteralPath $gccAbs -ErrorAction SilentlyContinue).Path } if (-not $gccBin) { Write-Error "GCC_PATH не найден: $gccPath (или относительно $ScriptDir)" } $env:Path = "$gccBin;$env:Path" Write-Host "[gcc] PATH += $gccBin" } if (-not (Get-Command gcc -ErrorAction SilentlyContinue)) { Write-Host "gcc не найден в PATH. Пример (WinLibs):" Write-Host ' D:\MinGW\bin, затем: GCC_PATH=D:\MinGW\bin — в scripts/.gitea-creds' Write-Error "C-компилятор gcc не найден." } Write-Host "[ok] gcc: $((gcc --version | Select-Object -First 1))" # --- 1b. Корень целевого проекта --- # PROJECT_PATH из .gitea-creds (относительно папки scripts/) либо, если не задан, # папка рядом со скриптом (прежнее поведение). $projPath = $creds["PROJECT_PATH"] if ($projPath -ne "" -and $projPath) { $RepoRoot = (Resolve-Path -LiteralPath (Join-Path $ScriptDir $projPath)).Path if (-not $RepoRoot) { Write-Error "PROJECT_PATH не найден: $projPath (относительно $ScriptDir)" } } else { $RepoRoot = Split-Path -Parent $ScriptDir } Set-Location $RepoRoot Write-Host "[root] $RepoRoot" # --- 2. Версия = commit- текущего 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" $goArgs = @("build", "-ldflags=$ldflags", "-o", $out, "./cmd/ratatoskr/") go $goArgs 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)"