164 lines
8.1 KiB
PowerShell
164 lines
8.1 KiB
PowerShell
#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
|