Files

201 lines
12 KiB
Markdown
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# 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 <тег>` |