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>
This commit is contained in:
@@ -260,6 +260,11 @@ restarts both services, then runs a health-check: the web `/healthz` must answer
|
|||||||
switches `current` back to the previous version and restarts. Progress is shown
|
switches `current` back to the previous version and restarts. Progress is shown
|
||||||
live and is also readable in the app's **Обновление** page.
|
live and is also readable in the app's **Обновление** page.
|
||||||
|
|
||||||
|
OTA also changes system files as root (helpers, sudoers, systemd units and
|
||||||
|
config) and those are **not** rolled back. What it touches, the "old
|
||||||
|
`bt-update` runs the OTA" pitfall and how to add a new system change:
|
||||||
|
[docs/ota.md](docs/ota.md).
|
||||||
|
|
||||||
## Display / GUI watchdog
|
## Display / GUI watchdog
|
||||||
|
|
||||||
Targets "backlight on, no picture until power re-plug" (seen on PoE sites:
|
Targets "backlight on, no picture until power re-plug" (seen on PoE sites:
|
||||||
|
|||||||
+63
@@ -0,0 +1,63 @@
|
|||||||
|
# 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-доступу к устройству.
|
||||||
@@ -8,7 +8,9 @@
|
|||||||
# collector/ (BLE GATT collector, if present in the repo)
|
# collector/ (BLE GATT collector, if present in the repo)
|
||||||
# manifest.json ({"version": ..., "created": ...})
|
# manifest.json ({"version": ..., "created": ...})
|
||||||
# and prints the package's sha256 (paste it into the web UI for integrity).
|
# and prints the package's sha256 (paste it into the web UI for integrity).
|
||||||
# Note: new systemd units / apt packages still require a full deploy .tgz.
|
# System files (helpers, sudoers, systemd units/config) are installed by
|
||||||
|
# bt-update from webconfig/ — see docs/ota.md. apt packages still need a full
|
||||||
|
# deploy .tgz.
|
||||||
#
|
#
|
||||||
# Usage:
|
# Usage:
|
||||||
# make-package.sh <version> [build-dir] [out-dir]
|
# make-package.sh <version> [build-dir] [out-dir]
|
||||||
|
|||||||
Reference in New Issue
Block a user