# 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, и шаг установки PlatformIO падает. Готовое окружение 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 # значение — github или gitea (ссылка не поддерживается) DEFAULT_ACTIONS_URL = github # workflow ищем только здесь, чтобы не подхватывались upstream-файлы 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 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` раннер становится глобальным и начинает брать задачи всех репозиториев инстанса: ```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 ``` ## 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`, чистит `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 шага публикации нет: сервер только собирает прошивки по ручному запуску и отдаёт их артефактом. ### Прошивальщик на сайте Страница берёт список прошивок из своего `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: - **companion (15):** T096, ProMicro (SSD1306 и SH1106), Heltec v3, heltec_v4, heltec_v4 TFT, tracker_v2, T-Deck, T-Echo Card, T114, T190, M9, Meshadventurer (sx1262, sx1262 + e22p 868M30S, e22p 433M30S); - **repeater (8):** Heltec v3, heltec_v4, heltec_v4 TFT, T096, ProMicro, Meshadventurer (sx1262, sx1262 + e22p 868M30S, sx1262 + e22p 433M30S); - **room server / sensor (3):** T096 room server, T096 sensor, ProMicro room server. ## 7. Время и ресурсы - Сервер с **1 CPU / 2 ГБ RAM** полный набор собирает часами, поэтому рабочий вариант — собирать полный набор локально (`ci/build-local.ps1`), а в Gitea запускать вручную только те env, которые нужно проверить. - Первая сборка качает тулчейны (nRF52: framework + CMSIS + gcc-arm ≈ 660 МБ; ESP32 — ещё столько же), дальше работает кэш `~/.platformio`. - `capacity: 1` в `config.yaml` — одна сборка за раз.