Files
screen-translator/.gitea/workflows/build.yaml
T
gusadmin 0e515604db
build / build (push) Successful in 6s
CI: убрать кириллицу из тела шагов сборки
Раннер сохраняет каждый шаг во временный .ps1 в кодировке, которая портит
кириллицу: условие с русским текстом превратилось в мусор и уронило сборку,
хотя сама проверка отработала верно. Теперь внутри run: только латиница.

Чтобы описание релиза осталось русским, оно вынесено в .gitea/release-body.md
и читается как UTF-8. Проверка модели сверяется с машиночитаемой строкой
RESULT, а не с человеческой сводкой. Архив назван латиницей по той же причине.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-07-27 15:59:47 +03:00

186 lines
11 KiB
YAML
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# Сборка и публикация «Переводчика экрана».
#
# Идёт на self-hosted Windows-раннере (метка `windows`, host mode) — иначе никак:
# распознавание держится на Windows.Media.Ocr, а сборка — на Roslyn из VS Build Tools
# и на Windows.winmd из Windows SDK. В контейнере ничего этого нет.
#
# * push в master -> сборка + прогон проверок + перезапись релиза `latest`
# * push тега v* -> то же, но отдельный постоянный релиз с этой версией
#
# Требований к раннеру всего два, и оба уже выполнены (тот же хост собирает GusVoice):
# Visual Studio Build Tools (нужен Roslyn csc) и Windows SDK (Windows.winmd).
# Если Roslyn вдруг отсутствует, build.ps1 сам откатывается на компилятор из состава
# Windows — код специально написан без возможностей C# 6 и выше, чтобы это работало.
name: build
on:
push:
branches: [master]
tags: ['v*']
workflow_dispatch:
permissions:
contents: write
env:
GITEA_HOST: gitea.gusik.tech
jobs:
build:
runs-on: windows
steps:
# Обычный actions/checkout тянется с github.com, а до него из этой сети TLS не доходит
# (`net/http: TLS handshake timeout`). Репозиторий публичный и лежит рядом, поэтому
# проще и надёжнее склонировать его напрямую, без внешних действий.
- name: Checkout
shell: pwsh
env:
REPO: ${{ github.repository }}
SHA: ${{ github.sha }}
run: |
$ErrorActionPreference = 'Stop'
git init -q .
git remote add origin "https://$($env:GITEA_HOST)/$($env:REPO).git"
git -c protocol.version=2 fetch -q --depth 1 origin $env:SHA
git checkout -q FETCH_HEAD
git log --oneline -1
- name: Сборка exe и диагностической утилиты
shell: pwsh
run: .\build.ps1 -Test
# Модель правдоподобия английского — единственная часть, которую можно проверить
# без экрана и без языковых пакетов. Проверяет, что таблица биграмм на месте и
# всё ещё отделяет настоящие надписи от каши распознавания.
# ВНИМАНИЕ: внутри run: только латиница. Раннер сохраняет шаг во временный .ps1
# в кодировке, которая портит кириллицу, и условие с русским текстом не сработает
# (проверено: сравнение превращалось в мусор и валило сборку).
- name: Проверка биграммной модели
shell: pwsh
run: |
$out = & .\dist\RenderTest.exe --selftest 2>&1 | Out-String
Write-Host $out
if ($LASTEXITCODE -ne 0) { throw "selftest exit code $LASTEXITCODE" }
if ($out -notmatch 'RESULT worst_good=(-?[\d.]+) best_bad=(-?[\d.]+)') { throw "selftest produced no summary" }
$good = [double]$Matches[1]; $bad = [double]$Matches[2]
Write-Host "worst good = $good, best bad = $bad"
# Настоящие надписи не должны опускаться до уровня каши, иначе фильтр начнёт
# выбрасывать нормальный текст.
if ($good -lt -3.0) { throw "real phrases scored below the garbage threshold" }
# Гейт качества на эталонном кадре. Работает, только если на раннере установлен
# английский пакет распознавания; если его нет — не валим сборку, а предупреждаем:
# это среда, а не регрессия кода.
- name: Проверка распознавания на эталоне
shell: pwsh
run: |
.\tools\make-sample.ps1
$out = & .\dist\RenderTest.exe --in .\dist\sample.png --out .\dist\ci --no-translate 2>&1 | Out-String
Write-Host $out
# Латиница в шаблонах намеренно — см. примечание о кодировке выше.
if ($out -match 'OCR\s+\S*\s*=?\s*$' -or $out -match 'exit code 2') {
Write-Host '::warning::no OCR language pack on the runner - frame check skipped'
exit 0
}
if ($out -notmatch '->\s*(\d+)') {
Write-Host '::warning::no block count in output - frame check skipped'
exit 0
}
$blocks = [int]$Matches[1]
Write-Host "blocks detected: $blocks"
# На этом кадре стабильно выходит 27-29 блоков. Резкое расхождение означает, что
# правки в Blocks.cs поехали — либо всё слиплось, либо рассыпалось.
if ($blocks -lt 18 -or $blocks -gt 40) { throw "unexpected block count ($blocks) - segmentation broke" }
- name: Сборка пакета
shell: pwsh
run: |
$ErrorActionPreference = 'Stop'
$rel = 'release'
New-Item -ItemType Directory -Force -Path $rel | Out-Null
Copy-Item .\dist\ScreenTranslator.exe $rel -Force
# Инструкция названа по-русски, поэтому берём её по маске, а не по имени:
# писать кириллицу в этот скрипт нельзя (кодировка раннера).
Get-ChildItem -Path . -Filter '*.txt' -File | Copy-Item -Destination $rel -Force
# config.json программа делает сама из вшитого умолчания
& .\dist\ScreenTranslator.exe --write-config
Start-Sleep -Seconds 2
if (Test-Path .\dist\config.json) { Copy-Item .\dist\config.json $rel -Force }
else { Write-Host '::warning::config.json was not generated' }
Get-ChildItem $rel | Select-Object Name, Length | Format-Table -AutoSize
Compress-Archive -Path "$rel\*" -DestinationPath 'ScreenTranslator.zip' -Force
# Тег v* -> постоянный релиз. Любой push в master -> перезаписываемый `latest`,
# чтобы у тех, кому программу отдали, всегда была ссылка на свежую сборку.
- name: Публикация релиза
shell: pwsh
env:
GITEA_TOKEN: ${{ secrets.GITEA_TOKEN }}
REPO: ${{ github.repository }}
REF_NAME: ${{ github.ref_name }}
REF: ${{ github.ref }}
SHA: ${{ github.sha }}
run: |
$ErrorActionPreference = 'Stop'
$host_ = $env:GITEA_HOST
$base = "https://${host_}/api/v1/repos/$($env:REPO)"
$hdr = @{ Authorization = "token $($env:GITEA_TOKEN)" }
$isTag = $env:REF -like 'refs/tags/*'
$tag = if ($isTag) { $env:REF_NAME } else { 'latest' }
if (-not $isTag) {
# Скользящий релиз: сносим прошлый вместе с тегом, иначе он останется
# висеть на старом коммите и «свежая сборка» перестанет быть свежей.
try {
$old = Invoke-RestMethod -Uri "$base/releases/tags/$tag" -Headers $hdr
if ($old.id) {
Invoke-RestMethod -Method Delete -Uri "$base/releases/$($old.id)" -Headers $hdr | Out-Null
Write-Host "previous $tag release removed"
}
} catch { }
try { Invoke-RestMethod -Method Delete -Uri "$base/tags/$tag" -Headers $hdr | Out-Null } catch { }
}
$short = $env:SHA.Substring(0, 8)
$stamp = (Get-Date).ToUniversalTime().ToString('yyyy-MM-dd HH:mm') + ' UTC'
# Русский текст описания лежит в файле и читается как UTF-8 — в самом скрипте
# кириллицу писать нельзя (её ломает кодировка временного .ps1 у раннера).
$notes = (Get-Content '.gitea/release-body.md' -Raw -Encoding UTF8).
Replace('{COMMIT}', $short).Replace('{DATE}', $stamp)
$body = @{
tag_name = $tag
target_commitish = $env:SHA
name = if ($isTag) { $tag } else { "latest ($short)" }
body = $notes
draft = $false
prerelease = (-not $isTag)
} | ConvertTo-Json -Compress
try {
$rel = Invoke-RestMethod -Method Post -Uri "$base/releases" -Headers $hdr -ContentType 'application/json' -Body $body
} catch {
# Релиз на этот тег уже есть (перезапуск задания) — берём его и обновляем описание
$rel = Invoke-RestMethod -Uri "$base/releases/tags/$tag" -Headers $hdr
$patch = @{ body = $notes } | ConvertTo-Json -Compress
Invoke-RestMethod -Method Patch -Uri "$base/releases/$($rel.id)" -Headers $hdr -ContentType 'application/json' -Body $patch | Out-Null
}
if (-not $rel.id) { throw "could not resolve release for tag $tag" }
# Старые файлы с тем же именем API не заменяет, поэтому сначала чистим
try {
foreach ($a in (Invoke-RestMethod -Uri "$base/releases/$($rel.id)/assets" -Headers $hdr)) {
Invoke-RestMethod -Method Delete -Uri "$base/releases/$($rel.id)/assets/$($a.id)" -Headers $hdr | Out-Null
}
} catch { }
foreach ($f in @('ScreenTranslator.zip', '.\dist\ScreenTranslator.exe')) {
$item = Get-Item $f
Write-Host "uploading $($item.Name) ..."
& curl.exe -sf -X POST "$base/releases/$($rel.id)/assets?name=$($item.Name)" `
-H "Authorization: token $($env:GITEA_TOKEN)" -F "attachment=@$($item.FullName)"
if ($LASTEXITCODE -ne 0) { throw "upload failed for $($item.Name)" }
}
Write-Host "release $tag published: https://${host_}/$($env:REPO)/releases"