- 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 и доки очищены от внутренних хостов/адресов (публичный репо)
133 lines
7.8 KiB
Bash
Executable File
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)"
|