# CI: автоматическая сборка прошивок на своём Gitea Схема: **Gitea (systemd) → Gitea Runner на том же сервере → PlatformIO → артефакты `out/`**. Сборка идёт через `build.sh` из репозитория, то есть тем же путём, что и локально (версия прошивки, `merge-bin` для ESP32, `.uf2` для nRF52), — логика не дублируется. ## Что где лежит | Файл | Назначение | |---|---| | `.gitea/workflows/firmware.yml` | workflow: клон, кэш, сборка, артефакты (запуск только вручную) | | `ci/build-firmwares.sh` | сборка выбранного набора env (годится и для запуска руками) | | `ci/build-local.ps1` | сборка на своей машине под Windows и публикация релиза в Gitea | | `ci/act_runner/gitea-runner.service` | systemd-юнит раннера | | `ci/act_runner/config.yaml` | конфиг раннера: ёмкость, таймаут, кэш | | `ci/act_runner/docker-compose.yml` | альтернатива: раннер в Docker | ## 1. Что должно быть на сервере ```bash apt-get update apt-get install -y git python3 python3-venv python3-pip curl ca-certificates build-essential ``` - **`python3-pip` обязателен.** Без него `python3 -m venv` создаёт окружение **без pip** (в `bin/` только симлинки python), и шаг установки PlatformIO падает с `No such file or directory`. Если venv уже создан без pip, workflow пересоздаст его сам. - Свободного места нужно **5 ГБ**: тулчейны PlatformIO занимают ~800 МБ на одну платформу, а при сборке и ESP32, и nRF52 — заметно больше. - **Swap желателен.** На VPS с 2 ГБ RAM линковка ESP32 упирается в память: ```bash fallocate -l 2G /swapfile && chmod 600 /swapfile && mkswap /swapfile && swapon /swapfile echo '/swapfile none swap sw 0 0' >> /etc/fstab ``` ## 2. Включение Actions в Gitea В `/etc/gitea/app.ini` (владелец `git:git`, права 600): ```ini [actions] ENABLED = true # Gitea 1.27 принимает значение github или gitea, но НЕ ссылку: # "https://github.com" даёт ошибку DEFAULT_ACTIONS_URL does not support ... DEFAULT_ACTIONS_URL = github # Искать workflow только в .gitea/workflows: иначе Gitea подхватит upstream-файлы # из .github/workflows, а у них триггер push — на каждый push запустится сборка # сотен env и деплой Pages. WORKFLOW_DIRS = .gitea/workflows ``` После правки: `systemctl restart gitea`. Перед изменением делайте копию конфига — `cp -a /etc/gitea/app.ini /etc/gitea/app.ini.bak`. ## 3. Установка раннера (systemd) ```bash useradd --create-home --home-dir /var/lib/act-runner --shell /bin/bash act-runner # актуальный релиз: https://gitea.com/gitea/runner/releases # (проект переименован: раньше act_runner, теперь Gitea Runner; на момент настройки — v5.0.0) curl -fsSL -o /usr/local/bin/gitea-runner \ https://gitea.com/gitea/runner/releases/download/v5.0.0/gitea-runner-5.0.0-linux-amd64 chmod +x /usr/local/bin/gitea-runner gitea-runner --version install -d -o act-runner -g act-runner /var/lib/act-runner cp ci/act_runner/config.yaml /var/lib/act-runner/config.yaml chown act-runner:act-runner /var/lib/act-runner/config.yaml ``` **Токен — обязательно со scope репозитория.** Без `--scope` раннер становится глобальным и начинает брать задачи **всех** репозиториев инстанса (в том числе чужие stale-bot): ```bash # от имени пользователя Gitea (CLI не работает под root) su - git -c "gitea actions generate-runner-token -c /etc/gitea/app.ini --scope shade/MeshCore" \ > /var/lib/act-runner/reg-token chown act-runner:act-runner /var/lib/act-runner/reg-token && chmod 600 /var/lib/act-runner/reg-token su - act-runner -c "/usr/local/bin/gitea-runner register --no-interactive \ --instance https://git.meshinfo.ru \ --token-file /var/lib/act-runner/reg-token \ --name meshcore-builder \ --labels ubuntu-latest:host \ -c /var/lib/act-runner/config.yaml" rm -f /var/lib/act-runner/reg-token # токен больше не нужен ``` Метка `ubuntu-latest:host` означает, что шаги выполняются **прямо на сервере** — без Docker, но зависимости ставятся в систему. Метка должна совпадать с `runs-on` в workflow. ```bash cp ci/act_runner/gitea-runner.service /etc/systemd/system/ systemctl daemon-reload systemctl enable --now gitea-runner systemctl status gitea-runner journalctl -u gitea-runner -f # ждём "declare successfully" ``` ### Альтернатива: раннер в Docker ```bash cd ci/act_runner && cp .env.example .env && nano .env && docker compose up -d ``` В свежих версиях образ называется `gitea/runner` (раньше `gitea/act_runner`). ## 4. Запуск сборки Workflow запускается **только вручную** — автоматических сборок по push и тегам нет (прошивки собираются локально и публикуются релизом, см. следующий раздел). - **Вручную в Gitea** — Actions → Firmware build → Run workflow (`targets`, `match`, `version`), либо через API: ```bash curl -X POST -H "Authorization: token $GITEA_TOKEN" -H 'Content-Type: application/json' \ -d '{"ref":"main","inputs":{"targets":"Heltec_t096_companion_radio_ble","version":"ci"}}' \ https://git.meshinfo.ru/api/v1/repos/shade/MeshCore/actions/workflows/firmware.yml/dispatches ``` - **Локально** — тем же скриптом, что запускает CI: ```bash FIRMWARE_VERSION=v1.0.0 bash ci/build-firmwares.sh MATCH=promicro bash ci/build-firmwares.sh TARGETS="Heltec_t096_companion_radio_ble" bash ci/build-firmwares.sh ``` Готовые файлы попадают в `out/` (каталог в `.gitignore`); при запуске в Gitea они выгружаются артефактом `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 # собрать и опубликовать релиз $env:GITEA_TOKEN = '<токен с правом write:repository>' .\ci\build-local.ps1 -Version v1.0.0 -Publish -Public -Tag v1.0.0 # опубликовать уже собранное, без пересборки .\ci\build-local.ps1 -Version v1.0.0 -SkipBuild -Publish -Public -Tag v1.0.0 ``` Скрипт находит Git Bash и PlatformIO, подставляет рабочий `python3` (в Git Bash это заглушка Microsoft Store — из-за неё `build.sh` не может определить платформу env), чистит `out/` перед сборкой, а с `-Publish` создаёт тег и релиз, прикрепляя к нему файлы (`-Public` публикует сразу, без флага создаётся черновик). Токен нужен только для публикации: сама сборка работает без него. На Linux/macOS то же самое делается напрямую: `FIRMWARE_VERSION=v1.0.0 bash ci/build-firmwares.sh`. ## 5. Релизы Релиз собирается и публикуется **локально**: `ci/build-local.ps1 -Publish -Public -Tag <тег>` (см. выше). Скрипт создаёт тег на текущем коммите, релиз в Gitea и прикрепляет к нему `.bin` / `.uf2` / `.zip`; версия `-Version` попадает в имена файлов (`-v1.0.0-.uf2`). Если тег уже существует, он переиспользуется. В CI шага публикации нет: сервер только собирает прошивки по ручному запуску и отдаёт их артефактом. ## 6. Набор прошивок по умолчанию `ci/build-firmwares.sh`, массив `DEFAULT_TARGETS`: T096 (companion, repeater, room server, sensor), ProMicro (SSD1306, SH1106, repeater, room server), Heltec v3, heltec_v4 TFT, tracker_v2, T-Deck, T-Echo Card, T114, T190, M9 и три варианта Meshadventurer. ## 7. Время и ресурсы - Сервер с **1 CPU / 2 ГБ RAM** полный набор собирает долго (первый прогон — часы), и на время сборки Gitea с Postgres на той же машине заметно тормозят. Поэтому рабочий вариант — собирать полный набор локально (`ci/build-local.ps1`), а в Gitea запускать вручную только те env, которые нужно проверить. - Первая сборка качает тулчейны (nRF52: framework + CMSIS + gcc-arm ≈ 660 МБ; ESP32 — ещё столько же), дальше работает кэш `~/.platformio`. - `capacity: 1` в `config.yaml` — одна сборка за раз. ## 8. Грабли, на которые уже наступали | Симптом | Причина и решение | |---|---| | `.../bin/pip: No such file or directory` | venv создан без pip — нет `python3-pip`; поставить пакет, venv пересоздать | | `DEFAULT_ACTIONS_URL does not support "https://github.com"` | в Gitea 1.27 значение — `github`, не ссылка | | раннер берёт задачи чужих репозиториев | токен выдан без `--scope owner/repo` | | `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` | | сборка запускается на каждый push, хотя в workflow стоит `paths-ignore` | Gitea этот фильтр не применяет; автосборок нет — workflow запускается только вручную | | `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`) | | в `out/` оказались файлы только последней платы | `build.sh` при каждом запуске делает `rm -rf out`; `ci/build-firmwares.sh` копит результат в `dist/` и в конце складывает всё в `out/` | | нужно опубликовать уже собранное, без пересборки | `ci/build-local.ps1 -SkipBuild -Publish -Tag <тег>` |