CI: автообновление прошивальщика после публикации релиза

This commit is contained in:
shade committed 2026-10-12 08:49:13 +03:00
1 parent e110fd87a8
commit 24e2e48e1d
4 files changed
+212 -1

No files matched your search

+6
View File
@@ -86,6 +86,12 @@ git merge upstream/main # правки для upstream отправляю
набор прошивок собирается на своей машине (тот же `build.sh` через Git Bash), а готовые
файлы прикрепляются к релизу Gitea — сервер только принимает их и не тратит CPU на сборку.
Это основной способ выпустить прошивки: на VPS всего одно ядро.
- **Прошивальщик на сайте** (<https://meshinfo.ru/flasher/>): список прошивок страница берёт
из своего `index.json`, где имена файлов разобраны на плату, роль, чип и формат. После
публикации релиза манифест пересобирается скриптом
[`ci/update-flasher.ps1`](./ci/update-flasher.ps1): он выкладывает его на сервер, делает
бэкап предыдущего и проверяет, что страница и файлы доступны. При `-Publish` это делается
автоматически.
### Варианты плат
+20
View File
@@ -167,6 +167,26 @@ $env:GITEA_TOKEN = '<токен с правом write:repository>'
В CI шага публикации нет: сервер только собирает прошивки по ручному запуску и отдаёт их
артефактом.
### Прошивальщик на сайте
Страница <https://meshinfo.ru/flasher/> берёт список прошивок из своего `index.json`
(статика в `/var/www/flasher`, а сами файлы браузер тянет через nginx напрямую из релизов
Gitea). Имена файлов в манифесте разобраны на плату, роль, чип и формат, поэтому после
каждой публикации релиза его нужно пересобрать — иначе на странице останутся старые ссылки.
```powershell
.\ci\update-flasher.ps1 # пересобрать и выложить на сервер
.\ci\update-flasher.ps1 -SkipUpload # только пересобрать, без заливки
```
Скрипт запускает генератор `build-flasher-index.py` из соседнего репозитория
(`..\MeshCore-posadmesh-meshcoretel\posadmesh-tools\`; путь задаётся через `-Generator`
или `FLASHER_GENERATOR`), делает на сервере бэкап старого манифеста, заливает новый и
проверяет, что страница отдаёт его, а файлы доступны через `/flasher/fw/...`.
При публикации релиза (`ci/build-local.ps1 -Publish`) обновление выполняется автоматически,
отключить можно флагом `-SkipFlasher`.
## 6. Набор прошивок по умолчанию
`ci/build-firmwares.sh`, массив `DEFAULT_TARGETS` — 26 env:
+23 -1
View File
@@ -21,7 +21,11 @@
FIRMWARE_VERSION — попадает в имена файлов (по умолчанию local).
.PARAMETER Publish
Создать релиз в Gitea и прикрепить к нему файлы из out/.
Создать релиз в Gitea и прикрепить к нему файлы из out/. После публикации
автоматически обновляется манифест прошивальщика (ci/update-flasher.ps1).
.PARAMETER SkipFlasher
Не обновлять манифест прошивальщика после публикации релиза.
.PARAMETER Tag
Тег для релиза (обязателен с -Publish). Если тега в репозитории нет — он будет создан
@@ -47,6 +51,7 @@ param(
[string]$Tag = '',
[switch]$Public,
[switch]$SkipBuild,
[switch]$SkipFlasher,
[string]$Token = $env:GITEA_TOKEN,
[string]$GiteaUrl = 'https://git.meshinfo.ru',
[string]$Repo = 'shade/MeshCore'
@@ -170,6 +175,23 @@ try {
Write-Host ''
Write-Host "Релиз: $GiteaUrl/$Repo/releases" -ForegroundColor Green
# Прошивальщик на сайте читает имена файлов из своего index.json: после публикации
# релиза манифест нужно пересобрать, иначе на странице останутся старые ссылки.
if (-not $SkipFlasher) {
$updater = Join-Path $root 'ci\update-flasher.ps1'
Write-Host ''
Write-Host '=== обновление прошивальщика ===' -ForegroundColor Cyan
if (Test-Path $updater) {
& powershell.exe -NoProfile -ExecutionPolicy Bypass -File $updater -Token $Token
if ($LASTEXITCODE -ne 0) {
Write-Warning 'прошивальщик не обновился — запустите ci/update-flasher.ps1 вручную'
}
}
else {
Write-Warning "не найден $updater — прошивальщик не обновлён"
}
}
}
else {
Write-Host ''
+163
View File
@@ -0,0 +1,163 @@
#Requires -Version 5.1
<#
.SYNOPSIS
Обновление страницы-прошивальщика (https://meshinfo.ru/flasher/) после публикации релиза.
.DESCRIPTION
Прошивальщик — статика в /var/www/flasher: страница читает index.json, где перечислены
релизы и файлы прошивок (имена разбираются на плату, роль, чип и формат). Файлы прошивок
на сервер не копируются: nginx проксирует их из релизов Gitea.
Скрипт пересобирает index.json генератором из posadmesh-tools и выкладывает его на сервер.
Запускать после того, как релиз в Gitea создан (иначе новых имён в манифесте не будет).
Из ci/build-local.ps1 вызывается автоматически при -Publish.
.PARAMETER Generator
Путь к build-flasher-index.py. По умолчанию берётся из FLASHER_GENERATOR или ищется
в соседнем репозитории ..\MeshCore-posadmesh-meshcoretel\posadmesh-tools\.
.PARAMETER Python
Интерпретатор Python. По умолчанию FLASHER_PYTHON, затем python из PATH, затем
из окружения PlatformIO.
.PARAMETER Token
Токен Gitea (нужен, если репозитории приватные). По умолчанию GITEA_TOKEN.
.PARAMETER SshKey
Ключ для доступа к серверу. По умолчанию ~/.ssh/meshcore_ci_ed25519.
.PARAMETER Server
Адрес сервера, по умолчанию root@meshinfo.ru.
.PARAMETER RemotePath
Куда положить манифест, по умолчанию /var/www/flasher/index.json.
.PARAMETER PublicUrl
Базовый адрес страницы для проверки, по умолчанию https://meshinfo.ru/flasher.
.PARAMETER SkipUpload
Только пересобрать index.json, ничего не заливать (для локальной проверки).
.EXAMPLE
.\ci\update-flasher.ps1
.EXAMPLE
.\ci\update-flasher.ps1 -SkipUpload
#>
param(
[string]$Generator = '',
[string]$Python = '',
[string]$Token = $env:GITEA_TOKEN,
[string]$SshKey = "$env:USERPROFILE\.ssh\meshcore_ci_ed25519",
[string]$Server = 'root@meshinfo.ru',
[string]$RemotePath = '/var/www/flasher/index.json',
[string]$PublicUrl = 'https://meshinfo.ru/flasher',
[switch]$SkipUpload
)
$ErrorActionPreference = 'Stop'
$repoRoot = Split-Path -Parent (Split-Path -Parent $MyInvocation.MyCommand.Path)
function Fail($msg) { Write-Host "ошибка: $msg" -ForegroundColor Red; exit 1 }
# --- генератор манифеста ----------------------------------------------------
if (-not $Generator) { $Generator = $env:FLASHER_GENERATOR }
if (-not $Generator) {
$candidates = @(
(Join-Path (Split-Path -Parent $repoRoot) 'MeshCore-posadmesh-meshcoretel\posadmesh-tools\build-flasher-index.py'),
(Join-Path $repoRoot 'posadmesh-tools\build-flasher-index.py')
)
$Generator = $candidates | Where-Object { Test-Path $_ } | Select-Object -First 1
}
if (-not $Generator -or -not (Test-Path $Generator)) {
Fail "не найден build-flasher-index.py. Укажите путь: -Generator <путь> или переменную FLASHER_GENERATOR"
}
$Generator = (Resolve-Path $Generator).Path
Write-Host "генератор: $Generator"
# --- python -----------------------------------------------------------------
if (-not $Python) { $Python = $env:FLASHER_PYTHON }
if (-not $Python) {
$candidates = @(
'C:\Espressif\tools\python\python.exe',
(Join-Path $env:USERPROFILE '.platformio\penv\Scripts\python.exe')
)
$Python = $candidates | Where-Object { Test-Path $_ } | Select-Object -First 1
if (-not $Python) {
$cmd = Get-Command python -ErrorAction SilentlyContinue
if ($cmd) { $Python = $cmd.Source }
}
}
if (-not $Python) { Fail 'не найден python.exe (укажите -Python <путь>)' }
Write-Host "python: $Python"
# --- пересборка манифеста ---------------------------------------------------
$env:PYTHONIOENCODING = 'utf-8'
if ($Token) { $env:GITEA_TOKEN = $Token } else { Write-Warning 'GITEA_TOKEN не задан — приватные репозитории не прочитаются' }
Write-Host ''
Write-Host '=== пересборка index.json ===' -ForegroundColor Cyan
& $Python $Generator
if ($LASTEXITCODE -ne 0) { Fail "генератор завершился с кодом $LASTEXITCODE" }
$manifest = Join-Path (Split-Path -Parent $Generator) 'flasher\index.json'
if (-not (Test-Path $manifest)) { Fail "генератор не создал $manifest" }
$data = Get-Content -Raw -Encoding UTF8 $manifest | ConvertFrom-Json
$meshcore = $data.sections | Where-Object { $_.id -eq 'meshcore' } | Select-Object -First 1
$shaList = @($meshcore.releases | ForEach-Object { $_.files } | ForEach-Object { $_.sha } | Sort-Object -Unique)
Write-Host ''
Write-Host "манифест: $manifest"
Write-Host " собран: $($data.generated_at)"
Write-Host " всего файлов: $($data.totals.files), плат: $($data.totals.boards)"
Write-Host " MeshCore: плат $($meshcore.board_count), файлов $($meshcore.file_count)"
Write-Host " версии кода: $($shaList -join ', ')"
if ($meshcore.file_count -eq 0) { Fail 'в секции MeshCore нет файлов — релиз не найден или имена не разбираются' }
# --- выкладка на сервер -----------------------------------------------------
if ($SkipUpload) {
Write-Host ''
Write-Host 'заливка пропущена (-SkipUpload)' -ForegroundColor Yellow
exit 0
}
$sshOpts = @('-i', $SshKey, '-o', 'IdentitiesOnly=yes', '-o', 'BatchMode=yes', '-o', 'StrictHostKeyChecking=accept-new')
Write-Host ''
Write-Host '=== выкладка на сервер ===' -ForegroundColor Cyan
$stamp = Get-Date -Format 'yyyyMMdd-HHmmss'
& ssh @sshOpts $Server "cp $RemotePath $RemotePath.bak-$stamp" 2>&1 | Out-Null
if ($LASTEXITCODE -ne 0) { Fail "не удалось сделать бэкап на сервере ($Server)" }
Write-Host " бэкап: $RemotePath.bak-$stamp"
& scp @sshOpts $manifest "${Server}:$RemotePath" 2>&1 | Out-Null
if ($LASTEXITCODE -ne 0) { Fail 'scp не смог залить манифест' }
Write-Host " залито: $RemotePath"
# --- проверка ---------------------------------------------------------------
Write-Host ''
Write-Host '=== проверка ===' -ForegroundColor Cyan
$httpCode = & curl.exe -s -o NUL -w '%{http_code}' "$PublicUrl/index.json"
if ($httpCode -ne '200') { Fail "страница отдаёт index.json с кодом $httpCode" }
$remote = & curl.exe -s "$PublicUrl/index.json"
$remoteData = $remote | ConvertFrom-Json
Write-Host " index.json: HTTP 200, собран $($remoteData.generated_at)"
if ($remoteData.generated_at -ne $data.generated_at) {
Fail "на сервере старый манифест ($($remoteData.generated_at) вместо $($data.generated_at))"
}
# пара ссылок на файлы: первый по списку и первый merged, если есть
$sample = @($meshcore.releases[0].files | Select-Object -First 1)
$merged = @($meshcore.releases[0].files | Where-Object { $_.format -eq 'merged' } | Select-Object -First 1)
if ($merged) { $sample += $merged }
$bad = 0
foreach ($f in $sample) {
$code = & curl.exe -s -o NUL -w '%{http_code}' "$PublicUrl/fw/$($f.repo)/$($f.tag)/$($f.name)"
if ($code -eq '200') { Write-Host " $($f.name): HTTP 200" }
else { Write-Host " $($f.name): HTTP $code" -ForegroundColor Red; $bad++ }
}
if ($bad -gt 0) { Fail "файлы прошивок недоступны через $PublicUrl/fw/ — проверьте, что релиз в Gitea опубликован" }
Write-Host ''
Write-Host "готово: $PublicUrl/ — $($meshcore.file_count) файлов MeshCore, версия кода $($shaList -join ', ')" -ForegroundColor Green
exit 0