@@ -1,6 +1,6 @@
# CI: автоматическая сборка прошивок на своём Gitea
Схема: **Gitea (systemd) → act_r unner на том же сервере → PlatformIO → артефакты `out/` ** .
Схема: **Gitea (systemd) → Gitea R unner на том же сервере → PlatformIO → артефакты `out/` ** .
Сборка идёт через `build.sh` из репозитория, то есть тем же путём, что и локально
(версия прошивки, `merge-bin` для ESP32, `.uf2` для nRF52), — логика не дублируется.
@@ -8,158 +8,161 @@
| Файл | Назначение |
|---|---|
| `.gitea/workflows/firmware.yml` | workflow: клон, кэш PlatformIO , сборка, артефакты, черновик релиза по тегу |
| `.gitea/workflows/firmware.yml` | workflow: клон, кэш, сборка, артефакты, черновик релиза по тегу |
| `ci/build-firmwares.sh` | сборка выбранного набора env (годится и для запуска руками) |
| `ci/act_runner/act -runner.service` | systemd-юнит раннера |
| `ci/act_runner/gitea -runner.service` | systemd-юнит раннера |
| `ci/act_runner/config.yaml` | конфиг раннера: ёмкость, таймаут, кэш |
| `ci/act_runner/docker-compose.yml` | альтернатива: раннер в Docker |
## 1. Что должно быть на VPS
## 1. Что должно быть на сервере
``` bash
apt-get update
apt-get install -y git python3 python3-venv python3-pip build-essential curl ca-certificates
apt-get install -y git python3 python3-venv python3-pip curl ca-certificates build-essential
```
- `python3-venv ` обязателен: workflow ставит PlatformIO в отдельное окружение
(`~/.platformio-venv` ), потому что на свежих Debian/Ubuntu pip в систему запрещён.
- Свободного места нужно **5 ГБ ** : `~/.platformio` с тулчейнами (~2–3 ГБ),
venv и промежуточные файлы сборки.
- RAM: 2 ГБ достаточно, при 1 ГБ возможны падения линковки ESP32 .
- ** `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. Установка раннера (systemd)
## 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/act_runner/releases
# (в свежих релизах проект называется Gitea Runner, см. https://docs.gitea.com/ runner/)
curl -fsSL -o /usr/local/bin/act_runner \
https://gitea.com/gitea/act_runner/releases/download/v0.2.11/act_runner-0.2.11-linux-amd64
chmod +x /usr/local/bin/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
install -d /etc /act_ runner
cp ci/ act_ runner/config.yaml /etc /act_ runner/config.yaml
# токен: Gitea → репозиторий → Settings → Actions → Runners → Create new Runner
su - act-runner -c 'act_runner register --no-interactive \
--instance https://git.meshinfo.ru \
--token ВСТАВИТЬ_ТОКЕН \
--name meshcore-builder \
--labels ubuntu-latest:host'
# сервис
cp ci/act_runner/act-runner.service /etc/systemd/system/
systemctl daemon-reload
systemctl enable --now act-runner
systemctl status act-runner
journalctl -u act-runner -f # в логах: "runner registered" / "polling tasks"
cp ci/act_runner/config.yaml /var/lib /act- runner/config.yaml
chown act-runner: act- runner /var/lib /act- runner/config.yaml
` ``
Метка `ubuntu-latest:host` означает, что шаги выполняются **прямо на сервере ** — быстро
и без Docker, но зависимости ставятся в систему. Метка должна совпадать с `runs-on`
в `.gitea/workflows/firmware.yml` .
**Токен — обязательно со scope репозитория.** Без ` --scope` раннер становится глобальным
и начинает брать задачи **всех** репозиториев инстанса (в том числе чужие stale-bot):
Раннер виден в Gitea: репозиторий → Settings → Actions → Runners (должен быть зелёным).
` ``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
Если на VPS есть Docker, из `ci/act_runner` :
` ``bash
cd ci/act_runner
cat > .env <<'EOF'
GITEA_INSTANCE_URL=https://git.meshinfo.ru
GITEA_RUNNER_REGISTRATION_TOKEN=ВСТАВИТЬ_ТОКЕН
GITEA_RUNNER_NAME=meshcore-builder
EOF
docker compose up -d && docker compose logs -f
cd ci/act_runner && cp .env.example .env && nano .env && docker compose up -d
` ``
В этом варианте job'ы идут в контейнере `catthehacker/ubuntu:act-22.04` , а docker.sock
смонтирован внутрь раннера. В свежих версиях образ может называться `gitea/runner` .
## 3. Отключить лишние workflow
Gitea читает workflow и из `.gitea/workflows` , и из `.github/workflows` , а там лежат
upstream-файлы с триггером `push` : `build-companion-firmwares.yml` ,
`build-repeater-firmwares.yml` , `build-room-server-firmwares.yml` , `github-pages.yml` ,
`run-unit-tests.yml` . На первом же пуше они попытаются собрать сотни env (в проекте их
около 600) или задеплоить Pages.
Отключите их в настройках репозитория (раздел Actions) либо через API:
``` bash
curl -X POST -H " Authorization: token $GITEA_TOKEN " \
https://git.meshinfo.ru/api/v1/repos/shade/MeshCore/actions/workflows/run-unit-tests.yml/disable
```
Оставьте включённым только `firmware.yml` . Если ваша версия Gitea поддерживает
настройку `actions.WORKFLOW_DIRS` , можно ограничить список каталогов одним
`.gitea/workflows` — тогда upstream-файлы не подхватятся вовсе.
В свежих версиях образ называется ` gitea/runner ` (раньше ` gitea/act_runner `).
## 4. Запуск сборки
- **Автоматически** — push в ` main` (кроме изменений в ` *.md`, ` ci/**`, ` .preview/**`).
- **Вручную** — Actions → Firmware build → Run workflow, поля:
- `targets` — env через запятую, например `Heltec_t096_companion_radio_ble,ProMicro_companion_radio_ble` ;
- `match` — подстрока имени env, например `heltec_t096` ;
- `version` — значение `FIRMWARE_VERSION` (по умолчанию `ci` ).
- **Локально** — тот же скрипт, что запускает 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
bash build.sh list | grep -i t096 # доступные env
```
- **Вручную** — 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:
` ` `ba sh
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`) и прикрепляются к запуску
workflow как артефакт ` firmware`.
## 5. Релизы по тегу
Пуш тега вида `v1.0.0` или `companion-v1.0.0` запуск ает с борку и ** создаёт черновик
релиза** с прикреплёнными `.bin` / `.uf2` / `.zip` . Версия прошивки берётся из тега:
` companion-v1.2.3` → `v1.2.3` , и попадает в имена файлов
(`<env>-v1.2.3-<sha>.uf2` ).
Черновик — чтобы можно было посмотреть состав и нажать Publish руками:
Пуш тега ` v1.0.0` или ` companion-v1.0.0` собир ает на бор и создаёт ** черновик релиза**
с прикреплёнными ` .bin` / ` .uf2` / ` .zip`. Версия берётся из тега
(` companion-v1.2.3` → ` v1.2.3`) и попадает в имена файлов (` <env>-v1.2.3-<sha>.uf2 `).
` ``bash
git tag companion-v1.0.0
git push origin companion-v1.0.0
git tag companion-v1.0.0 && git push origin companion-v1.0.0
# затем: репозиторий → Releases → черновик → Publish
` ``
Для п убликации используется `secrets.GITHUB_TOKEN` , который Gitea выдаёт workflow
автоматически (в workflow стоит `permissions: contents: write` ). Если по вашему
токену релизы не создаются, заведите секрет репозитория `RELEASE_TOKEN` с правом
`write:repository` и подставьте его в шаге «Черновик релиза».
П убликация идёт автоматическим ` secrets.GITHUB_TOKEN ` (в workflow стоит
` permissions: contents: write`). Если релизы не создаются — заведите секрет
` RELEASE_TOKEN` с правом ` write:repository ` и подставьте его в шаге «Черновик релиза».
## Набор прошивок по умолчанию
## 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.
` 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. Время и ресурсы
- Первая (холодная) сборка набора — примерно 30–60 минут: скачиваются тулчейны.
- Последующие запуски заметно быстрее: `~/.platformio` и venv кэшируются между
запусками через `actions/cache` .
- `capacity: 1` в `config.yaml` — одна сборка за раз; поднимайте, если CPU позволяет.
- Сервер с **1 CPU / 2 ГБ RAM** полный набор собирает долго (первый прогон — часы),
и на время сборки Gitea с Postgres на той же машине заметно тормозят.
Разумно запускать полный набор вручную, а на push держать 2–3 env .
- Первая сборка качает тулчейны (nRF52: framework + CMSIS + gcc-arm ≈ 660 МБ;
ESP32 — ещё столько же), дальше работает кэш ` ~/.platformio`.
- ` capacity: 1` в ` config.yaml` — одна сборка за раз.
## Ограничения
## 8. Грабли, на которые уже наступали
- Шаги используют `actions/checkout` , `actions/cache` , `actions/upload-artifact` ,
`actions/download-artifact` . Раннер скачивает их с GitHub (`DEFAULT_ACTIONS_URL` ),
поэтому серверу нужен доступ в интернет; в закрытом контуре укажите свой миррор
экшенов в `config.yaml` .
- Кэш `~/.platformio` растёт со временем: если диск кончается, почистите
`~/.platformio/.cache` у пользователя `act-runner` .
| Симптом | Причина и решение |
|---|---|
| ` .../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` |