201 lines
12 KiB
Markdown
201 lines
12 KiB
Markdown
# 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` попадает в имена файлов
|
||
(`<env>-v1.0.0-<sha>.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 <тег>` |
|