Files
gusadmin 12f974571c
build / build (push) Successful in 7s
CI: починить гейт сегментации, он молча пропускался
Шаг был зелёным, но не проверял ничего: распознавание на раннере работает,
а шаблон искал число сразу после стрелки, тогда как в выводе между ними
стоит русское слово («-> блоков 29»). Совпадения не было, и шаг выходил
с предупреждением вместо проверки.

RenderTest теперь печатает итог машиночитаемой строкой RESULT — так же, как
selftest. Отсутствие этой строки считается провалом: значит конвейер не
доработал до конца. Пропуск остался только для случая, когда на машине нет
языкового пакета распознавания.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-07-27 16:02:31 +03:00

185 lines
11 KiB
YAML
Raw Permalink 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 ($LASTEXITCODE -eq 2) {
Write-Host '::warning::no OCR language pack on the runner - frame check skipped'
exit 0
}
if ($out -notmatch 'RESULT lines=(\d+) blocks=(\d+)') {
throw "RenderTest produced no RESULT line - the pipeline did not finish"
}
$blocks = [int]$Matches[2]
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"