diff --git a/README.md b/README.md index 1fc870d4..ccbdad16 100644 --- a/README.md +++ b/README.md @@ -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 всего одно ядро. ### Варианты плат diff --git a/ci/README.md b/ci/README.md index 2140a14f..9589ea06 100644 --- a/ci/README.md +++ b/ci/README.md @@ -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`) | diff --git a/ci/build-firmwares.sh b/ci/build-firmwares.sh index 3bbd7e35..e1faa127 100644 --- a/ci/build-firmwares.sh +++ b/ci/build-firmwares.sh @@ -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 diff --git a/ci/build-local.ps1 b/ci/build-local.ps1 new file mode 100644 index 00000000..1adf6e9b --- /dev/null +++ b/ci/build-local.ps1 @@ -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 +}