CI: локальная сборка прошивок с публикацией релиза в Gitea
This commit is contained in:
@@ -71,6 +71,10 @@ git merge upstream/main # правки для upstream отправляю
|
||||
`build.sh`, поэтому версия, `merge-bin` для ESP32 и `.uf2` для nRF52 получаются такими же,
|
||||
как при локальной сборке. Готовые файлы выгружаются артефактами, пуш тега создаёт черновик
|
||||
релиза. Как поднять раннер — [`ci/README.md`](./ci/README.md).
|
||||
- **Локальная сборка с публикацией результата** ([`ci/build-local.ps1`](./ci/build-local.ps1)):
|
||||
набор прошивок собирается на своей машине (тот же `build.sh` через Git Bash), а готовые
|
||||
файлы прикрепляются к релизу Gitea — сервер только принимает их и не тратит CPU на сборку.
|
||||
Это основной способ собрать полный набор: на VPS всего одно ядро.
|
||||
|
||||
### Варианты плат
|
||||
|
||||
|
||||
@@ -126,6 +126,30 @@ cd ci/act_runner && cp .env.example .env && nano .env && docker compose up -d
|
||||
Готовые файлы попадают в `out/` (каталог в `.gitignore`) и прикрепляются к запуску
|
||||
workflow как артефакт `firmware`.
|
||||
|
||||
### Локально на Windows, без нагрузки на сервер
|
||||
|
||||
На этом VPS всего 1 CPU, поэтому полный набор прошивок собирается там часами. Разумнее
|
||||
собрать у себя, а на сервер отправить только готовые файлы — релизом Gitea:
|
||||
|
||||
```powershell
|
||||
# быстрый набор: все env, в имени которых есть подстрока
|
||||
.\ci\build-local.ps1 -Match promicro -Version v1.0.0
|
||||
|
||||
# полный набор из DEFAULT_TARGETS
|
||||
.\ci\build-local.ps1 -Version v1.0.0
|
||||
|
||||
# собрать и прикрепить к релизу (черновик; -Public опубликует сразу)
|
||||
$env:GITEA_TOKEN = '<токен с правом write:repository>'
|
||||
.\ci\build-local.ps1 -Version v1.0.0 -Publish -Tag companion-v1.0.0
|
||||
```
|
||||
|
||||
Скрипт находит Git Bash и PlatformIO, подставляет рабочий `python3` (в Git Bash это
|
||||
заглушка Microsoft Store — из-за неё `build.sh` не может определить платформу env),
|
||||
чистит `out/` перед сборкой, а с `-Publish` создаёт тег, черновик релиза и прикрепляет
|
||||
к нему файлы. Токен нужен только для публикации: сама сборка работает без него.
|
||||
|
||||
На Linux/macOS то же самое делается напрямую: `FIRMWARE_VERSION=v1.0.0 bash ci/build-firmwares.sh`.
|
||||
|
||||
## 5. Релизы по тегу
|
||||
|
||||
Пуш тега `v1.0.0` или `companion-v1.0.0` собирает набор и создаёт **черновик релиза**
|
||||
@@ -166,3 +190,5 @@ tracker_v2, T-Deck, T-Echo Card, T114, T190, M9 и три варианта Mesha
|
||||
| `registration file not found` в цикле рестартов | раннер запущен до регистрации: сначала `register`, потом сервис |
|
||||
| `E: Could not get lock /var/lib/dpkg/lock-frontend` | работает unattended-upgrades; подождать либо остановить `apt-daily.timer` |
|
||||
| на push запускается сборка сотен env | не выставлен `WORKFLOW_DIRS = .gitea/workflows` |
|
||||
| `405 Method Not Allowed` при создании тега | в Gitea тег создаётся через `POST /repos/{owner}/{repo}/tags`, у `/git/refs` метода POST нет |
|
||||
| `python3: command not found` или пустой вывод в Git Bash | это заглушка Microsoft Store; задайте `PYTHON3=/c/.../python.exe` (это делает `ci/build-local.ps1`) |
|
||||
|
||||
@@ -22,6 +22,15 @@ set -uo pipefail
|
||||
|
||||
cd "$(dirname "${BASH_SOURCE[0]}")/.."
|
||||
|
||||
# На Windows в Git Bash команда python3 — это заглушка Microsoft Store, из-за которой
|
||||
# build.sh не может определить платформу env. Если PYTHON3 задан, подставляем рабочий
|
||||
# интерпретатор (нужен и самому скрипту, и вызываемому build.sh):
|
||||
# PYTHON3=/c/Espressif/tools/python/python.exe bash ci/build-firmwares.sh
|
||||
if [[ -n "${PYTHON3:-}" ]]; then
|
||||
python3() { "$PYTHON3" "$@"; }
|
||||
export -f python3
|
||||
fi
|
||||
|
||||
# Набор по умолчанию — устройства этого форка.
|
||||
DEFAULT_TARGETS=(
|
||||
# companion
|
||||
|
||||
@@ -0,0 +1,175 @@
|
||||
#Requires -Version 5.1
|
||||
<#
|
||||
.SYNOPSIS
|
||||
Локальная сборка прошивок MeshCore под Windows с публикацией в Gitea.
|
||||
|
||||
.DESCRIPTION
|
||||
Запускает ci/build-firmwares.sh (тот же путь сборки, что и в CI: build.sh, merge-bin,
|
||||
uf2) через Git Bash, а готовые файлы из out/ при необходимости прикрепляет к релизу
|
||||
в Gitea. Сам сервер при этом не нагружается — на него уходит только загрузка файлов.
|
||||
|
||||
Зачем: на VPS всего 1 CPU, полный набор прошивок собирается там часами. Локально —
|
||||
быстрее, а результат удобно раздать через релиз Gitea.
|
||||
|
||||
.PARAMETER Targets
|
||||
Список env через запятую. Пусто — набор устройств форка из ci/build-firmwares.sh.
|
||||
|
||||
.PARAMETER Match
|
||||
Подстрока имени env: собрать все подходящие (например, promicro).
|
||||
|
||||
.PARAMETER Version
|
||||
FIRMWARE_VERSION — попадает в имена файлов (по умолчанию local).
|
||||
|
||||
.PARAMETER Publish
|
||||
Создать релиз в Gitea и прикрепить к нему файлы из out/.
|
||||
|
||||
.PARAMETER Tag
|
||||
Тег для релиза (обязателен с -Publish). Если тега в репозитории нет — он будет создан
|
||||
от текущего коммита.
|
||||
|
||||
.PARAMETER Public
|
||||
Опубликовать релиз сразу. Без этого флага создаётся черновик.
|
||||
|
||||
.PARAMETER Token
|
||||
Токен Gitea с правом write:repository. По умолчанию берётся из переменной GITEA_TOKEN.
|
||||
|
||||
.EXAMPLE
|
||||
.\ci\build-local.ps1 -Match promicro -Version v1.0.0
|
||||
|
||||
.EXAMPLE
|
||||
.\ci\build-local.ps1 -Version v1.0.0 -Publish -Tag companion-v1.0.0
|
||||
#>
|
||||
param(
|
||||
[string]$Targets = '',
|
||||
[string]$Match = '',
|
||||
[string]$Version = 'local',
|
||||
[switch]$Publish,
|
||||
[string]$Tag = '',
|
||||
[switch]$Public,
|
||||
[string]$Token = $env:GITEA_TOKEN,
|
||||
[string]$GiteaUrl = 'https://git.meshinfo.ru',
|
||||
[string]$Repo = 'shade/MeshCore'
|
||||
)
|
||||
|
||||
$ErrorActionPreference = 'Stop'
|
||||
$root = Split-Path -Parent (Split-Path -Parent $MyInvocation.MyCommand.Path)
|
||||
Push-Location $root
|
||||
|
||||
try {
|
||||
# --- ищем Git Bash -------------------------------------------------------
|
||||
$bash = @(
|
||||
"$env:ProgramFiles\Git\bin\bash.exe",
|
||||
"${env:ProgramFiles(x86)}\Git\bin\bash.exe",
|
||||
"$env:LOCALAPPDATA\Programs\Git\bin\bash.exe"
|
||||
) | Where-Object { Test-Path $_ } | Select-Object -First 1
|
||||
if (-not $bash) { $bash = (Get-Command bash -ErrorAction SilentlyContinue).Source }
|
||||
if (-not $bash) { throw 'Git Bash не найден — установите Git for Windows' }
|
||||
Write-Host "Git Bash: $bash"
|
||||
|
||||
# --- ищем рабочий python3 для build.sh ----------------------------------
|
||||
# В Git Bash путь должен быть в unix-виде: C:\Tools\python.exe -> /c/Tools/python.exe
|
||||
$pythonCandidates = @(
|
||||
"$env:USERPROFILE\.platformio\penv\Scripts\python.exe",
|
||||
'C:\Espressif\tools\python\python.exe'
|
||||
)
|
||||
$python = $pythonCandidates | 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) { throw 'Не найден python.exe (нужен для build.sh)' }
|
||||
$pythonUnix = '/' + $python.Substring(0, 1).ToLower() + $python.Substring(2).Replace('\', '/')
|
||||
Write-Host "python3 для build.sh: $pythonUnix"
|
||||
|
||||
# --- pio должен быть доступен и для bash ---------------------------------
|
||||
$pioDir = Join-Path $env:USERPROFILE '.platformio\penv\Scripts'
|
||||
if ((Test-Path (Join-Path $pioDir 'pio.exe')) -and ($env:PATH -notlike "*$pioDir*")) {
|
||||
$env:PATH = "$pioDir;$env:PATH"
|
||||
}
|
||||
$pioCheck = & $bash -c 'command -v pio' 2>$null
|
||||
if (-not $pioCheck) { throw 'PlatformIO (pio) не найден в PATH — установите PlatformIO Core' }
|
||||
Write-Host "pio: $pioCheck"
|
||||
|
||||
# --- сборка --------------------------------------------------------------
|
||||
$env:FIRMWARE_VERSION = $Version
|
||||
$env:TARGETS = $Targets
|
||||
$env:MATCH = $Match
|
||||
$env:PYTHON3 = $pythonUnix
|
||||
|
||||
# out/ чистим, иначе в релиз попадут файлы прошлых сборок
|
||||
$outDir = Join-Path $root 'out'
|
||||
if (Test-Path $outDir) { Get-ChildItem $outDir -File | Remove-Item -Force }
|
||||
|
||||
Write-Host ''
|
||||
Write-Host "=== сборка (версия $Version) ===" -ForegroundColor Cyan
|
||||
& $bash 'ci/build-firmwares.sh'
|
||||
$buildExit = $LASTEXITCODE
|
||||
|
||||
$files = @(Get-ChildItem $outDir -File -ErrorAction SilentlyContinue)
|
||||
Write-Host ''
|
||||
Write-Host "файлов в out/: $($files.Count)"
|
||||
$files | ForEach-Object { ' {0} {1:N0} байт' -f $_.Name, $_.Length }
|
||||
|
||||
if ($buildExit -ne 0) { Write-Warning "часть env не собралась (код $buildExit)" }
|
||||
|
||||
# --- публикация релиза ---------------------------------------------------
|
||||
if ($Publish) {
|
||||
if (-not $Tag) { throw 'С -Publish нужно указать -Tag, например -Tag companion-v1.0.0' }
|
||||
if (-not $Token) { throw 'Нужен токен Gitea: -Token <токен> или переменная окружения GITEA_TOKEN' }
|
||||
if ($files.Count -eq 0) { throw 'В out/ нет файлов — публиковать нечего' }
|
||||
|
||||
$api = "$GiteaUrl/api/v1/repos/$Repo"
|
||||
$headers = @{ Authorization = "token $Token"; 'Content-Type' = 'application/json' }
|
||||
|
||||
# тег создаём, если его ещё нет (на текущем коммите).
|
||||
# В Gitea тег создаётся через POST /tags (у /git/refs нет метода POST).
|
||||
$tagExists = $true
|
||||
try { Invoke-RestMethod -Method Get -Uri "$api/tags/$Tag" -Headers $headers | Out-Null }
|
||||
catch { $tagExists = $false }
|
||||
if (-not $tagExists) {
|
||||
$sha = (git -C $root rev-parse HEAD).Trim()
|
||||
$body = @{ tag_name = $Tag; target = $sha } | ConvertTo-Json -Compress
|
||||
Invoke-RestMethod -Method Post -Uri "$api/tags" -Headers $headers -Body $body | Out-Null
|
||||
Write-Host "создан тег $Tag на коммите $($sha.Substring(0,8))"
|
||||
}
|
||||
else {
|
||||
Write-Host "тег $Tag уже существует"
|
||||
}
|
||||
|
||||
$draft = if ($Public) { 'false' } else { 'true' }
|
||||
$releaseBody = @{
|
||||
tag_name = $Tag
|
||||
name = $Tag
|
||||
draft = ($draft -eq 'true')
|
||||
prerelease = $false
|
||||
target_commitish = 'main'
|
||||
} | ConvertTo-Json -Compress -Depth 3
|
||||
|
||||
try {
|
||||
$release = Invoke-RestMethod -Method Post -Uri "$api/releases" -Headers $headers -Body $releaseBody
|
||||
Write-Host "релиз #$($release.id) создан ($(if ($Public) { 'опубликован' } else { 'черновик' }))"
|
||||
}
|
||||
catch {
|
||||
throw "Не удалось создать релиз: $($_.Exception.Message)"
|
||||
}
|
||||
|
||||
foreach ($f in $files) {
|
||||
& curl.exe -sS -X POST "$api/releases/$($release.id)/assets?name=$($f.Name)" `
|
||||
-H "Authorization: token $Token" -F "attachment=@$($f.FullName)" | Out-Null
|
||||
if ($LASTEXITCODE -ne 0) { Write-Warning "не загрузился: $($f.Name)" }
|
||||
else { Write-Host " прикреплён $($f.Name)" }
|
||||
}
|
||||
|
||||
Write-Host ''
|
||||
Write-Host "Релиз: $GiteaUrl/$Repo/releases" -ForegroundColor Green
|
||||
}
|
||||
else {
|
||||
Write-Host ''
|
||||
Write-Host 'Файлы готовы в out/. Чтобы прикрепить их к релизу Gitea, добавьте -Publish -Tag <тег>' -ForegroundColor Yellow
|
||||
}
|
||||
|
||||
exit $buildExit
|
||||
}
|
||||
finally {
|
||||
Pop-Location
|
||||
}
|
||||
Reference in New Issue
Block a user