deployment

Разворачивание и обновление прибора — docker-образ, compose для прибора, install.sh, клонирование данных, сборка образа на GitHub. Использовать при правке Dockerfile, compose-файлов, deploy/, workflow

SKILL.md

GitHub

Разворачивание

Прибор получает один docker-образ с бэкендом и собранным фронтом; на самом приборе живут только docker, chromium с cage и каталог /opt/s2l из трёх вещей: compose.yml, .env, data/. Инструкция для того, кто ставит, — docs/deploy.md; здесь — почему устроено так.

Образ

  • Сборка не на приборе. J6412 ставил бы зависимости минут десять на каждое обновление. Образ собирает GitHub Actions (.github/workflows/docker.yml) и кладёт в ghcr.io/webdev1881/scale-s2l: latest с main, v* с тегов, хеш коммита — всегда, чтобы откатиться на точный образ, когда latest подвёл.
  • Две стадии. Фронт собирается на node и в готовый образ попадает только dist; бэкенд отдаёт его сам, nginx не нужен. Раскладка путей внутри повторяет репозиторий (/opt/s2l/backend, /opt/s2l/frontend/dist, /opt/s2l/docs/prod.xlsx): бэкенд ищет фронт и выгрузку относительно себя, и менять это ради образа незачем.
  • fonts-dejavu-core обязателен: растр этикетки берёт шрифт с кириллицей из /usr/share/fonts/truetype/dejavu, без него Pillow упадёт на встроенный шрифт без кириллицы (см. скилл label-printing).
  • Не от root. Внутри работает пользователь s2l; доступ к весовой плате и принтеру выдаётся группами dialout (20) и lp (7) через group_add — те же номера, что в Ubuntu на приборе, поэтому проброшенные устройства читаются без privileged.
  • .dockerignore режет всё лишнее — данные прибора, .env, окружения, сборки. Данные в образ попасть не должны: у каждого прибора они свои.

Два compose-файла

  • compose.yml в корне — локальная сборка и запуск на симуляторе (fake), порт 8000, безымянный том. Для проверки образа на машине разработчика.
  • deploy/docker/compose.yml — прибор: образ из реестра, network_mode: host (киоск ходит на 127.0.0.1:8000, как и systemd-вариант), ./data томом, устройства из .env. Устройства должны существовать до запуска — иначе compose откажется стартовать; на симуляторе этот файл не используют.

Данные прибора

  • data/ — единственное, что отличает один прибор от другого: settings.json, s2l.db, labels/. Обновление образа их не трогает.
  • Клон — основной путь на новые весы: скопировать data/ с готового прибора и отдать его install.sh аргументом. Тот же каталог, те же цены, те же настройки.
  • Демо-каталог на железе не заводится: seed_if_empty работает только при S2L_HAL_BACKEND=fake. Демо-товары ссылаются на снимки, которых в сборке давно нет, и на приборе они выглядели бы битыми карточками. Пустой прибор заполняют клоном или tools/import_products.py изнутри контейнера — выгрузка лежит в образе.

install.sh

deploy/docker/install.sh — идемпотентный: ставит пакеты, заводит s2l (UID 1000, согласован с XDG_RUNTIME_DIR в юните киоска), кладёт файлы в /opt/s2l, ставит udev-правила и юнит киоска, тянет образ и поднимает контейнер. Повторный запуск только обновляет образ. Юнит s2l-kiosk.service ждёт docker.service, а не отдельный юнит бэкенда: контейнер поднимает сам docker (restart: unless-stopped), а пока он не готов, chromium показывает экран «Оновлення» и переподключается.

Старый deploy/systemd/s2l-backend.service (venv без docker) оставлен как запасной путь для прибора, где docker поставить нельзя.

Ловушки

  • Открытая вкладка киоска после обновления образа держит прежнюю сборку фронта до перезагрузки (systemctl restart s2l-kiosk). Автоперезагрузка по смене сборки обсуждалась и отложена.
  • Сеть хоста означает, что админка доступна с любого адреса прибора без пароля — как и до docker. Закрывать её — задача сети магазина, не образа.
  • docker compose exec работает от пользователя s2l внутри контейнера, поэтому скрипты из tools/ пишут в data/ без правки прав.

Инструкции

docs/new-device.md — пошаговая, для того, кто ставит прибор в магазине: имя прибора, Ubuntu, install.sh, .env, железо, туннель Cloudflare, элемент в 1С, настройка, чистка, контрольный список, таблица «симптом → куда смотреть». docs/deploy.md — справочник: клон данных, .env, обновление, проверка, без реестра, что где лежит. Меняя процесс постановки, править обе.

Доступ 1С к прибору

Сервер 1С доступен только по RDP, а приборы стоят за NAT провайдера без белого IP (проверено на машине разработчика: WAN роутера — приватный адрес, проброс снаружи не отвечает). Поэтому прибор сам держит исходящий туннель: сервис tunnel (cloudflared) в deploy/docker/compose.yml, профиль tunnel, токен в CLOUDFLARE_TUNNEL_TOKEN. В 1С — Сервер = <имя прибора в домене>, SSL = Истина, ресурс /api/catalog/1c-import, заголовок X-API-Key. Не Tailscale, потому что на сервер 1С ставить клиента нельзя; Cloudflare Tunnel требует только имя и HTTPS. Через туннель наружу смотрит и админка, поэтому в .env прибора обязателен S2L_ADMIN_PASSWORD (скилл admin-ui). Подробности — integrations/1c/README.md и docs/deploy.md.