Files
arm/install.sh
T
toros 12e6a453de feat: atxpi — server power & console manager (core, CLI, deploy)
- core/hardware: драйверы real (atxctl/pyserial) + mock, ATXPI_DRIVER
- core/power, console, mock: операции питания и RS232-консоли
- cli/atxpi: status/on/off/reset/cycle/console (stdlib, без deps)
- install.sh: one-shot curl|bash, идемпотентный, хост-агностичный
- deploy: systemd-юнит, шаблон env, update.sh
- README, AGENTS.md: документация и проектные решения
- AGENTS.md и доки очищены от внутренних хостов/адресов (публичный репо)
2026-09-08 21:54:11 +05:00

133 lines
7.8 KiB
Bash
Executable File

#!/bin/bash
# ─────────────────────────────────────────────────────────────────────────────
# atxpi — one-shot installer. Идемпотентный: повторные запуски обновляют репо
# и перезапускают сервис.
#
# # 1) из чекаута репозитория:
# sudo bash install.sh
#
# # 2) curl-установка (публичное репо). URL укажи через ATXPI_GIT_REPO:
# curl -s <repo>/raw/branch/main/install.sh | sudo ATXPI_GIT_REPO=<repo> bash
#
# # 3) закрытое репо — добавь токен:
# curl -s <repo>/raw/branch/main/install.sh \
# | sudo ATXPI_GIT_REPO=<repo> ATXPI_GIT_TOKEN=<tok> 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}"
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"
# ── 5. конфигурация / секреты (generated, НЕ в git) ──────────────────────────
ENV_FILE=/etc/atxpi/atxpi.env
if [ ! -f "$ENV_FILE" ]; then
log "Создаю ${ENV_FILE}..."
mkdir -p /etc/atxpi
cat > "$ENV_FILE" <<EOF
# atxpi — локальная конфигурация (создаётся install.sh, НЕ хранится в git)
# ATXPI_DRIVER: real | mock
ATXPI_DRIVER=$ATXPI_DRIVER
SERIAL_PORT=$ATXPI_SERIAL
SERIAL_BAUD=$ATXPI_BAUD
# токен для REST-API и браузера (генерируется при установке)
ATXPI_WEB_TOKEN=$(python3 -c "import secrets;print(secrets.token_hex(32))")
EOF
chmod 600 "$ENV_FILE"
chown "$ATXPI_USER":"$ATXPI_USER" "$ENV_FILE"
fi
# ── 6. systemd-юнит ──────────────────────────────────────────────────────────
log "Разворачиваю systemd-юнит..."
sed "s|@USER@|$ATXPI_USER|g; s|@TARGET@|$ATXPI_TARGET|g; s|@BIND_HOST@|$ATXPI_BIND_HOST|g; s|@BIND_PORT@|$ATXPI_BIND_PORT|g" \
"$APP_DIR/deploy/systemd/atxpi.service" > /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)"