Files
Button/docs/ota.md
T
GrigoandClaude Opus 5.5 56d635f051 docs: what OTA changes in the system (docs/ota.md)
bt-update runs as root and installs helpers, sudoers, systemd units and
config; none of it is rolled back. Documents the order of stage steps, the
'old bt-update runs the OTA' pitfall and how to add a system change.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-11-10 10:58:05 +00:00

64 lines
7.0 KiB
Markdown
Raw 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.
# OTA: что обновление может менять в системе
Изначально OTA задумывалась как замена только приложения (бинарник, веб, коллектор). На практике `bt-update` выполняется **от root** и уже меняет системные файлы: хелперы, sudoers, юниты systemd, конфиги systemd. Здесь описано, что именно он делает, в каком порядке и как добавить новую системную правку.
## Как запускается
1. Веб (пользователь `buttontask`) принимает пакет и вызывает `sudo -n /opt/buttontask/scripts/bt-update stage <pkg>`. Это разрешено правилом в `/etc/sudoers.d/buttontask`.
2. `bt-update` перезапускает сам себя через `systemd-run` в отдельном временном юните `buttontask-update-<ts>`. Поэтому он переживает перезапуск `buttontask-web` посреди обновления.
3. Прогресс пишется в `run/update-status.json`, подробный лог в `run/update.log`.
## Порядок `stage`
| Шаг | Что делается | Куда пишет |
|---|---|---|
| 1. распаковка | пакет → `versions/<ver>`, проверка `manifest.json` | `/opt/buttontask/versions/` |
| 2. `sync_helpers` | все `webconfig/scripts/bt-*` → root-owned хелперы; sudoers ставится только если прошёл `visudo -c` | `/opt/buttontask/scripts/`, `/etc/sudoers.d/buttontask` |
| 3. коллектор | `buttontask-collector.service`, enable/disable по `collectorEnabled` | `/etc/systemd/system/` |
| 4. bluetooth | drop-in `Restart=always` для `bluetooth.service` | `/etc/systemd/system/bluetooth.service.d/override.conf` |
| 5. `sync_system` | `buttontask.service`, `buttontask-displaywatch.service`, аппаратный вотчдог; `daemon-reload` или `daemon-reexec`, enable/restart displaywatch | `/etc/systemd/system/`, `/etc/systemd/system.conf.d/` |
| 6. переключение | `current` → новая версия, перезапуск сервисов | |
| 7. health-check | `/healthz`, свежий `run/qt.alive`, `buttontask` active (30 с) | |
| 8. откат | при провале `current` → предыдущая версия, перезапуск | |
Подкоманды для ручного запуска:
```sh
sudo /opt/buttontask/scripts/bt-update sync-helpers # шаг 2 из текущей версии
sudo /opt/buttontask/scripts/bt-update sync-system # шаг 5 из текущей версии
```
## Важные ограничения
### 1. OTA выполняет **старый** `bt-update`
Обновление запускает `bt-update`, который уже установлен на устройстве, а не тот, что лежит в пакете. Новый `bt-update` приезжает на шаге 2 и работает только **со следующей** OTA. Значит, системную правку, добавленную в `bt-update`, первая OTA не применит.
Как это обойти (сделано для вотчдога): веб при каждом старте вызывает `sudo bt-update sync-system` (`sync_system_units()` в `webconfig/system_info.py`). На шаге 6 веб перезапускается уже из новой версии, вызывает новый `bt-update` (его установил шаг 2), и правка применяется в ту же OTA. Поэтому `sync-system` должен быть **идемпотентным**: без изменений он ничего не делает (`install_if_changed` + `cmp`).
### 2. Системные правки **не откатываются**
Шаги 2–5 выполняются до health-check, и откат возвращает только симлинк `current`. Юниты, sudoers и конфиги остаются от новой версии. Отсюда правило: **любая системная правка должна работать и со старыми версиями приложения.** Пример: `buttontask.service` не задаёт `WatchdogSec=`, таймаут включает сам бинарник через `WATCHDOG_USEC`. Иначе старые версии без пингов убивались бы каждые 30 секунд.
### 3. Изменённый юнит не применяется к уже запущенному процессу
`daemon-reload` обновляет юнит, но переменные окружения (например, `NOTIFY_SOCKET`) процесс получает только при старте. Если юнит поменялся после перезапуска на шаге 6 (это случай из пункта 1), новые настройки заработают со следующего рестарта сервиса или перезагрузки.
### 4. Чего OTA не умеет
- **apt/deb-пакеты.** Для них нужен полный deploy-бандл (`winDeployScripts/`, `device-install.sh`).
- **Произвольные одноразовые скрипты-миграции.** Отдельного механизма нет: каждая правка вписывается в `bt-update` руками (см. ниже).
- **Перезагрузку** после установки. Если она нужна, это ручной шаг.
## Как добавить новую системную правку
1. Положите файл в пакет, в `webconfig/…` (всё из `webconfig/` попадает в OTA через `make-package.sh`).
2. В `bt-update` в `sync_system` добавьте `install_if_changed <src> <dst>` и нужный `reload`/`reexec`/`enable`. Не добавляйте новый шаг прямо в `cmd_stage`: тогда правка не применится при первой OTA (пункт 1).
3. Проверьте, что правка совместима со старыми версиями приложения (пункт 2).
4. Повторите то же в `winDeployScripts/device-install.sh` для чистой установки. Если добавили новый хелпер `bt-*`, впишите его в списки в `device-install.sh` и `make-adb-bundle.ps1`.
5. Новые типы файлов (`*.conf` и т.п.) пропишите в `.gitattributes` с `eol=lf`.
## Безопасность
Пакет OTA фактически исполняется от root: хелперы, sudoers и юниты из него ставятся без подписи. От подмены защищают только пароль веб-конфигуратора и (необязательная) сверка sha256 при загрузке. Доступ к вкладке «Обновление» равен root-доступу к устройству.