Загрузка данных
#!/usr/bin/env bash
set -uo pipefail
# ОСнова (ОСОн, НППКТ) 2.14 — ФАЙЛ 2/2: локальное окружение на СИСТЕМНОМ python3.9 (3.9.23).
# Запускать ПОСЛЕ osnova_base_disk.sh (сеть + apt с дисков + python3.9 уже стоят).
#
# ПОЧЕМУ БЕЗ СБОРКИ (важно):
# Собрать ровно 3.9.6 через pyenv на 2.14 НЕВОЗМОЖНО без devel-диска: у ОСновы libc
# пересобрана как 2.28…osnova1, и дебиановский libc6-dev к ней не встаёт (нет совпадающей
# версии) → компилятора нет → CPython не собрать. Поэтому берём ГОТОВЫЙ системный
# python3.9 = 3.9.23 (та же ветка 3.9, API/ABI совместимы, requirements ставятся один-в-один).
#
# ПОЧЕМУ БЕЗ КОМПИЛЯТОРА ВООБЩЕ:
# Из requirements.txt компилируемых всего 4 — cffi, cryptography, psutil, pydantic_core —
# и все они ставятся ГОТОВЫМИ wheel'ами (cp39 manylinux) с PyPI. Собирать нечего →
# gcc/-dev не нужны, база ОСновы не трогается.
#
# ЧТО ДЕЛАЕМ:
# sudo NOPASSWD → проверка ЗПС → nproc → права $HOME → venv(3.9) + pip через get-pip →
# проект (git) → requirements с PyPI → SDK (download_deps) → продукт .deb → Allure → готово.
#
# Запуск ПОД ОБЫЧНЫМ пользователем:
# bash osnova_local_setup.sh
# если PyPI недоступен, а есть внутренний индекс:
# PIP_INDEX=https://nexus.aktivco.ru/repository/pypi-group/simple bash osnova_local_setup.sh
TESTER="${TESTER:-$(id -un)}"
PY="${PY:-python3.9}" # системный интерпретатор с дисков (3.9.23)
PROJECT_GIT="${PROJECT_GIT:-https://scm.aktivco.ru/rutoken/dev/qa-dev/rtlogon_pytests.git}"
PROJECT_DIR="${PROJECT_DIR:-$HOME/rtlogon_pytests}"
PROJECT_REF="${PROJECT_REF:-}"
GIT_USER="${GIT_USER:-}"
GIT_PASS="${GIT_PASS:-}"
PIP_INDEX="${PIP_INDEX:-}" # пусто = pypi.org; или внутренний nexus (…/simple)
GETPIP_URL="${GETPIP_URL:-https://bootstrap.pypa.io/pip/get-pip.py}"
PRODUCT_URL="${PRODUCT_URL:-https://builds.aktivco.ru/new/unilogon-production/unilogon-production-143a6b8d5cc6b3527afe3ba0fcbeaa464f4e60bf.zip}"
PRODUCT_ZIP="${PRODUCT_ZIP:-}"
PRODUCT_DEB="${PRODUCT_DEB:-}"
ALLURE_VER="${ALLURE_VER:-2.29.0}"
NPROC_MIN="${NPROC_MIN:-16384}"
SCRIPT_DIR="$(cd "$(dirname "$0")" 2>/dev/null && pwd || echo "$PWD")"
export DEBIAN_FRONTEND=noninteractive
ok(){ echo " [OK] $*"; }
act(){ echo " [DO] $*"; }
warn(){ echo " [WARN] $*"; }
die(){ echo "ОШИБКА: $*" >&2; exit 1; }
have(){ command -v "$1" >/dev/null 2>&1; }
apt_get(){ sudo DEBIAN_FRONTEND=noninteractive apt-get "$@" </dev/null; }
urlenc(){ local s="$1" o="" c i; for ((i=0;i<${#s};i++)); do c="${s:$i:1}"; case "$c" in [a-zA-Z0-9.~_-]) o+="$c";; *) printf -v c '%%%02X' "'$c"; o+="$c";; esac; done; printf '%s' "$o"; }
PIPIDX(){ [ -n "$PIP_INDEX" ] && printf -- '--index-url %s' "$PIP_INDEX"; }
. /etc/os-release 2>/dev/null || true
echo "=== ОСнова local-setup (системный python3.9, без pyenv/компилятора) ==="
echo " система: ${PRETTY_NAME:-?} (VERSION_ID=${VERSION_ID:-?}), ядро $(uname -r)"
[ "$(id -u)" -ne 0 ] || die "Запусти под обычным пользователем ($TESTER), НЕ root — venv в \$HOME"
sudo -v || die "нужен sudo"
echo "== 0. предпосылки (что даёт osnova_base_disk.sh) =="
have "$PY" || die "$PY не найден — сначала отработай osnova_base_disk.sh (он ставит python3.9 с дисков)"
have git || die "git не найден — сначала отработай osnova_base_disk.sh"
ok "интерпретатор: $("$PY" --version 2>&1)"
# venv-модуль в базовом python3.9 есть (ensurepip нет — его заменит get-pip). Проверим.
"$PY" -c 'import venv' 2>/dev/null || die "$PY без модуля venv — доустанови с дисков или проверь пакет python3.9"
echo "== 0.5 sudo NOPASSWD (для тестов) =="
sudo -k 2>/dev/null || true
if sudo -n true 2>/dev/null; then ok "NOPASSWD sudo есть"
else
act "настраиваю NOPASSWD"
echo "$TESTER ALL=(ALL) NOPASSWD: ALL" | sudo tee /etc/sudoers.d/$TESTER >/dev/null
sudo chmod 0440 /etc/sudoers.d/$TESTER
sudo -n true 2>/dev/null && ok "NOPASSWD настроен" || warn "NOPASSWD не поднялся — тесты будут спрашивать пароль"
fi
echo "== 0.6 ЗПС =="
if [ -L /etc/ima/policy ] && [ "$(readlink /etc/ima/policy)" = /etc/ima/policy.d/appraise ]; then
warn "ЗПС включена (policy→appraise) — venv (неподписанный python) под enforce не запустится."
warn "Выключи: sudo rm /etc/ima/policy; sudo ln -s /etc/ima/policy.d/empty /etc/ima/policy; sudo update-initramfs -u -k all; sudo reboot"
die "ЗПС enforce — тесты невозможны без выключения (или подписи venv, см. README §ЗПС)"
else
ok "ЗПС не в enforce (policy→$( [ -L /etc/ima/policy ] && readlink /etc/ima/policy || echo 'нет симлинка' ))"
fi
echo "== 1. лимит nproc =="
[ -f /etc/security/limits.d/90-nproc.conf ] || echo "$TESTER - nproc $NPROC_MIN" | sudo tee /etc/security/limits.d/90-nproc.conf >/dev/null
CUR="$(ulimit -u)"; case "$CUR" in unlimited) CUR=999999999;; esac
[ "$CUR" -ge "$NPROC_MIN" ] && ok "ulimit -u=$CUR" || { ulimit -u "$NPROC_MIN" 2>/dev/null && ok "ulimit -u поднят" || { warn "перелогинься после прогона"; NEED_RELOGIN=1; }; }
echo "== 2. права \$HOME =="
BAD=""
for f in .ICEauthority .Xauthority .config .cache .dbus .local; do
p="$HOME/$f"; [ -e "$p" ] && [ "$(stat -c %U "$p" 2>/dev/null)" != "$TESTER" ] && BAD="$BAD $f"
done
[ -n "$BAD" ] && { warn "не от $TESTER:$BAD — чиню"; sudo chown -R "$TESTER:$(id -gn)" "$HOME"; } || ok "владелец \$HOME ок"
echo "== 3. проект =="
export GIT_TERMINAL_PROMPT=1
if [ ! -d "$PROJECT_DIR/.git" ]; then
# Креды: заранее (GIT_USER=.. GIT_PASS=..) или спросим тут. Читаем с /dev/tty.
if [ -z "$GIT_USER" ]; then
printf " GitLab логин для %s: " "${PROJECT_GIT#https://}" > /dev/tty
read -r GIT_USER < /dev/tty || true
fi
if [ -z "$GIT_PASS" ] && [ -n "$GIT_USER" ]; then
printf " Пароль/токен для %s (ввод скрыт): " "$GIT_USER" > /dev/tty
read -rs GIT_PASS < /dev/tty || true; echo > /dev/tty
fi
CLONE_URL="$PROJECT_GIT"
if [ -n "$GIT_USER" ]; then
CLONE_URL="https://$(urlenc "$GIT_USER")@${PROJECT_GIT#https://}"
if [ -n "$GIT_PASS" ]; then AP="$(mktemp)"; printf '#!/bin/sh\nprintf %%s %q\n' "$GIT_PASS" > "$AP"; chmod +x "$AP"; export GIT_ASKPASS="$AP"; fi
else warn "логин не введён — git спросит креды сам"; fi
git clone "$CLONE_URL" "$PROJECT_DIR" || die "git clone не прошёл (неверные логин/пароль или нет доступа к $PROJECT_GIT)"
[ -n "${AP:-}" ] && rm -f "$AP"; unset GIT_PASS; act "проект склонирован"
fi
cd "$PROJECT_DIR" || die "нет $PROJECT_DIR"
[ -n "$PROJECT_REF" ] && git checkout "$PROJECT_REF"
ok "$PROJECT_DIR"
echo "== 4. venv ($PY) + pip через get-pip =="
# ensurepip у системного python3.9 нет (пакет python3.9-venv не на дисках), поэтому venv
# создаём БЕЗ pip, а pip доставляем официальным get-pip (самодостаточный, не «debian-debundled»).
if [ ! -x venv/bin/python ]; then
"$PY" -m venv --without-pip venv || die "venv не создался ($PY -m venv). Проверь модуль venv у $PY."
fi
# shellcheck disable=SC1091
source venv/bin/activate
if ! python -m pip --version >/dev/null 2>&1; then
act "ставлю pip через get-pip"
GP="$(mktemp --suffix=.py)"
curl -fsSL "$GETPIP_URL" -o "$GP" || curl -fsSL "https://bootstrap.pypa.io/pip/3.9/get-pip.py" -o "$GP" \
|| die "get-pip.py не скачался (нет сети до bootstrap.pypa.io?)"
# shellcheck disable=SC2046
python "$GP" $(PIPIDX) || die "get-pip не отработал (сеть до PyPI/nexus?)"
rm -f "$GP"
fi
ok "pip: $(python -m pip --version 2>&1)"
echo "== 5. requirements.txt (wheel'ы с PyPI/nexus) =="
# shellcheck disable=SC2046
python -m pip install $(PIPIDX) --upgrade pip setuptools wheel >/dev/null 2>&1 || warn "апгрейд pip/setuptools/wheel не прошёл"
# shellcheck disable=SC2046
if python -m pip install $(PIPIDX) -r requirements.txt; then
ok "зависимости установлены"
else
warn "pip install -r requirements.txt с ошибкой."
echo " Если ошибка вида 'Building wheel ... error' — пакет пытается компилироваться (нет wheel'а"
echo " под cp39): назови его — возможно, нужен именно он с devel-диска. Если 'No matching distribution'"
echo " или таймаут — PyPI недоступен, задай внутренний индекс: PIP_INDEX=<nexus>/simple"
fi
echo "== 6. SDK (download_deps.py, с builds.aktivco.ru) =="
python download_deps.py || warn "download_deps.py не отработал (доступ к builds.aktivco.ru?)"
echo "== 7. продукт rtlogon (.deb) =="
if dpkg -l 2>/dev/null | grep -qiE 'rutokenlogon|unilogon'; then
ok "уже установлен: $(dpkg -l | grep -iE 'rutokenlogon|unilogon' | awk '{print $2"="$3}' | head -1)"
else
DEB="$PRODUCT_DEB"; ZIP="$PRODUCT_ZIP"
[ -z "$ZIP" ] && ZIP="$(ls "$SCRIPT_DIR"/unilogon-production-*.zip "$PWD"/unilogon-production-*.zip 2>/dev/null | head -1)"
if [ -z "$DEB" ] && [ -z "$ZIP" ]; then
TMP="$(mktemp -d)"; act "качаю продукт $PRODUCT_URL"
wget -q "$PRODUCT_URL" -O "$TMP/p.zip" && ZIP="$TMP/p.zip" || warn "zip не скачался — положи рядом или задай PRODUCT_DEB="
fi
if [ -z "$DEB" ] && [ -n "$ZIP" ]; then
act "распаковываю $(basename "$ZIP")"; EX="$(mktemp -d)"; unzip -q "$ZIP" -d "$EX"
find "$EX" -maxdepth 4 \( -name '*.tar.gz' -o -name '*.tgz' -o -name '*.tar' -o -name '*.zip' \) \
-exec sh -c 'd=$(dirname "$1"); case "$1" in *.zip) unzip -qo "$1" -d "$d";; *) tar -xf "$1" -C "$d";; esac' _ {} \; 2>/dev/null
ARCH="$(dpkg --print-architecture 2>/dev/null || echo amd64)"
# ОСнова = обычный debian-family пакет; БЕЗ astra/alt-тега и БЕЗ grdcontrol (лок. лиц-сервер)
DEB="$(find "$EX" -name "*_${ARCH}.deb" | grep -viE 'astra|alt|grdcontrol|guardant' | head -1)"
[ -z "$DEB" ] && DEB="$(find "$EX" -name '*.deb' | grep -viE 'astra|alt|grdcontrol|guardant|arm64|i386' | head -1)"
fi
if [ -n "$DEB" ] && [ -f "$DEB" ]; then
act "ставлю $(basename "$DEB") (KDE Plasma → DM sddm; lightdm приедет зависимостью с дисков)"
apt_get install -y "$DEB" || { sudo dpkg -i "$DEB"; apt_get -f install -y; } || warn "deb не встал — доустанови зависимости"
dpkg -s lightdm >/dev/null 2>&1 || warn "lightdm не установлен — тесты --use-system-gui no по /etc/lightdm скипнутся"
else warn "debian-family .deb не найден — задай PRODUCT_DEB=путь"; fi
fi
CLI="$(ls /opt/aktivco/*/app/*logon-cli 2>/dev/null | head -1)"; [ -n "$CLI" ] && "$CLI" --version 2>/dev/null && ok "cli: $CLI"
echo "== 8. Allure =="
if have allure; then ok "allure: $(allure --version 2>/dev/null)"
else
TGZ="allure-commandline-${ALLURE_VER}.tgz"
( cd /tmp && wget -q "https://repo1.maven.org/maven2/io/qameta/allure/allure-commandline/${ALLURE_VER}/${TGZ}" \
&& sudo tar -zxf "$TGZ" -C /opt/ && sudo ln -sf "/opt/allure-${ALLURE_VER}/bin/allure" /usr/local/bin/allure ) \
&& { act "Allure $ALLURE_VER"; allure --version 2>/dev/null || warn "allure не стартовал (java?)"; } \
|| warn "Allure не скачался — поставь вручную"
fi
echo
echo "=== Готово ==="
[ "${NEED_RELOGIN:-0}" = 1 ] && echo "ПЕРЕЛОГИНЬСЯ (или reboot) — поднимется nproc."
echo "Локальные тесты: cd $PROJECT_DIR && source venv/bin/activate && python -m pytest -m local"
echo "Доменные: sudo bash osnova_ad_join.sh | sudo bash osnova_freeipa_join.sh"