#!/bin/bash # ───────────────────────────────────────────────────────────────────────────── # atxpi — one-shot installer. Идемпотентный: повторные запуски обновляют репо # и перезапускают сервис. # # # 1) из чекаута репозитория: # sudo bash install.sh # # # 2) curl-установка (публичное репо). URL укажи через ATXPI_GIT_REPO: # curl -s /raw/branch/main/install.sh | sudo ATXPI_GIT_REPO= bash # # # 3) закрытое репо — добавь токен: # curl -s /raw/branch/main/install.sh \ # | sudo ATXPI_GIT_REPO= ATXPI_GIT_TOKEN= bash # # Хост репозитория в код не зашит — передаётся через ATXPI_GIT_REPO. # ───────────────────────────────────────────────────────────────────────────── set -euo pipefail # ── конфигурация (переопределяется через env) ─────────────────────────────── ATXPI_TARGET="${ATXPI_TARGET:-/opt/arm}" # куда кладём чекаут репо ATXPI_USER="${ATXPI_USER:-atxpi}" # системный юзер сервиса ATXPI_BRANCH="${ATXPI_BRANCH:-main}" ATXPI_GIT_REPO="${ATXPI_GIT_REPO:-}" # URL репо (для свежего клона обязателен) ATXPI_GIT_TOKEN="${ATXPI_GIT_TOKEN:-}" # токен для закрытого репо (иначе пусто) ATXPI_BIND_HOST="${ATXPI_BIND_HOST:-0.0.0.0}" # ⚠️ ограничь до VPN/LAN (см. безопасность) ATXPI_BIND_PORT="${ATXPI_BIND_PORT:-8443}" ATXPI_SERIAL="${ATXPI_SERIAL:-/dev/ttyUSB0}" ATXPI_BAUD="${ATXPI_BAUD:-115200}" ATXPI_DRIVER="${ATXPI_DRIVER:-real}" ATXPI_MODE="${ATXPI_MODE:-full}" log() { echo -e "\e[1;32m[atxpi]\e[0m $*"; } err() { echo -e "\e[1;31m[atxpi]\e[0m $*" >&2; } # ── 0. root ────────────────────────────────────────────────────────────────── if [ "$(id -u)" -ne 0 ]; then err "Запускай через sudo (или sudo -E для варианта с env-переменными)." exit 1 fi # ── 1. системные зависимости ───────────────────────────────────────────────── log "Устанавливаю системные зависимости (git, curl, python3-venv)..." export DEBIAN_FRONTEND=noninteractive apt-get update -y apt-get install -y git curl ca-certificates python3-venv python3-pip # ── 2. сервисный пользователь + доступ к железу ────────────────────────────── if ! id "$ATXPI_USER" >/dev/null 2>&1; then log "Создаю сервисного пользователя '$ATXPI_USER'..." useradd --system --create-home --shell /usr/sbin/nologin "$ATXPI_USER" fi # I2C (expander-хат в atxctl) и dialout (ttyUSB0 — консоль) usermod -aG i2c,dialout "$ATXPI_USER" 2>/dev/null || true # ── 3. клонирование / обновление репо ──────────────────────────────────────── if [ -d "$ATXPI_TARGET/.git" ]; then log "Репозиторий уже есть: обновляю $ATXPI_TARGET..." ATXPI_GIT_REPO="${ATXPI_GIT_REPO:-$(git -C "$ATXPI_TARGET" remote get-url origin 2>/dev/null || true)}" git -C "$ATXPI_TARGET" fetch origin "$ATXPI_BRANCH" || true git -C "$ATXPI_TARGET" reset --hard "origin/$ATXPI_BRANCH" || true else if [ -z "$ATXPI_GIT_REPO" ]; then err "Укажи ATXPI_GIT_REPO (URL репозитория) для первого клона." exit 1 fi log "Клонирую репозиторий в $ATXPI_TARGET..." if [ -n "$ATXPI_GIT_TOKEN" ]; then git -c http.extraHeader="Authorization: token $ATXPI_GIT_TOKEN" \ clone -q -b "$ATXPI_BRANCH" "$ATXPI_GIT_REPO" "$ATXPI_TARGET" # сохранить креды для будущих `git pull` (не оставляя токен в remote URL) host="$(printf '%s' "$ATXPI_GIT_REPO" \ | sed -E 's#^[a-zA-Z]+://##; s#.*@##; s#^git@##; s#[:/].*$##')" printf 'protocol=https\nhost=%s\nusername=oauth2\npassword=%s\n\n' \ "$host" "$ATXPI_GIT_TOKEN" | git credential approve || true else git clone -q -b "$ATXPI_BRANCH" "$ATXPI_GIT_REPO" "$ATXPI_TARGET" fi fi APP_DIR="$ATXPI_TARGET/atxpi" # ── 4. venv + зависимости ──────────────────────────────────────────────────── log "Ставлю зависимости в venv..." VENV="$APP_DIR/venv" [ -d "$VENV" ] || python3 -m venv "$VENV" "$VENV/bin/pip" install --upgrade pip >/dev/null "$VENV/bin/pip" install -q -r "$APP_DIR/requirements.txt" chown -R "$ATXPI_USER":"$ATXPI_USER" "$VENV" chown -R "$ATXPI_USER":"$ATXPI_USER" "$APP_DIR" # владелец приложения — сервис-юзер (pyc/tmp/audit) chown "$ATXPI_USER":"$ATXPI_USER" /etc/atxpi # для /api/history (audit.jsonl внутри) # ── 5. конфигурация / секреты (generated, НЕ в git) ────────────────────────── ENV_FILE=/etc/atxpi/atxpi.env if [ ! -f "$ENV_FILE" ]; then log "Создаю ${ENV_FILE}..." mkdir -p /etc/atxpi cat > "$ENV_FILE" < /etc/systemd/system/atxpi.service systemctl daemon-reload # ── 7. старт ───────────────────────────────────────────────────────────────── if [ -f "$APP_DIR/webui/server.py" ]; then log "Веб-приложение на месте: включаю и запускаю сервис." systemctl enable atxpi.service >/dev/null 2>&1 || true systemctl restart atxpi.service || true else log "Веб-приложение (atxpi/webui/server.py) ещё не готово — юнит установлен, но не запущен." log "Запуск после появления webui/server.py (шаг ③)." fi echo log "Готово. Сводка:" echo " Репозиторий : $ATXPI_TARGET" echo " Пользователь: $ATXPI_USER (группы: $(id -nG "$ATXPI_USER" 2>/dev/null | sed 's/ /,/g'))" echo " venv : $APP_DIR/venv" echo " Конфиг : $ENV_FILE (0600)" echo " Юнит : atxpi.service (bind ${ATXPI_BIND_HOST}:${ATXPI_BIND_PORT})" [ -f "$APP_DIR/webui/server.py" ] && echo " Статус : $(systemctl is-active atxpi.service 2>/dev/null || echo unknown)"