Files
Bridge-and-Join-s/scripts/setup-dev-vm.sh
zuevav d5b5597c67 chore: каркас моно-репо и скрипт первичной подготовки dev-ВМ
Содержимое первого коммита:
- Структура моно-репо: cmd/{lk-gateway,m2m-core,nsd-adapter,lk-emulator,notify}/,
  internal/{m2m,nsdxml,fansystore,notify}/, services/crypto-service/,
  web/admin-ui/, deploy/docker-compose/, migrations/, docs/.
- Заглушки main.go во всех cmd/ — make build проходит из коробки.
- Makefile с целями build/test/lint/fmt/vet/tidy/ci/compose-up/compose-down.
- .golangci.yml, .gitignore, README.md (на русском).
- .claude/settings.json — общие ограничения Claude Code для команды
  (запрет sudo, rm -rf, доступа к /etc/cryptopro, /var/cryptopro).
- README в каждом каталоге — назначение и стадия (M1..M5).
- docs/architecture/overview.md — выжимка из плана проекта.
- docs/fansy-contract/v1/, docs/lk-contract/v1/ — точки сборки контрактов
  с командами Fansy и ЛК клиента.
- deploy/docker-compose/docker-compose.yml — dev-стек (PostgreSQL, MinIO).
- scripts/setup-dev-vm.sh — первичная подготовка dev-ВМ под РЕД ОС 7.x
  и Ubuntu 22.04+ (для компаний без бюджета на лицензии); ставит Go 1.23,
  Liberica JDK 21, Node.js 20 LTS, Podman, podman-compose, Claude Code CLI;
  создаёт пользователя dev, /srv/dev, аудит-history. Идемпотентен.
- scripts/README.md — описание скрипта и ограничений.

Что НЕ коммитим:
- Секреты, ключи, сертификаты — закрыто в .gitignore.
- Локальные настройки Claude Code (settings.local.json) и сессионные
  каталоги (.claude/projects/, .claude/worktrees/, .claude/logs/).

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-05 14:42:18 +03:00

374 lines
16 KiB
Bash
Executable File
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
#!/usr/bin/env bash
# Скрипт первичной подготовки dev-ВМ для проекта Bridge and Join's
# (сервис M2M-перевода ЦБ через НРД).
#
# Поддерживаемые ОС:
# - РЕД ОС 7.x — целевая прод-ОС (вариант с бюджетом на лицензии)
# - Ubuntu 22.04+ — бюджетная альтернатива для разработки и для
# компаний, у которых ещё нет лицензий на РЕД ОС
# (для прод-сертификации в финсекторе всё равно
# потребуется РЕД ОС / Astra SE с сертификатом ФСТЭК)
#
# Что делает:
# - определяет ОС и пакетный менеджер;
# - устанавливает базовый dev-стек (Git, Make, jq, xmlstarlet, ...);
# - устанавливает Go 1.23, Liberica JDK 21, Node.js 20 LTS, Podman;
# - устанавливает Claude Code CLI;
# - создаёт пользователя dev и каталог /srv/dev;
# - готовит безопасный shell-history с timestamp.
#
# Запуск (под root или через sudo):
# sudo bash scripts/setup-dev-vm.sh
#
# Переменные окружения для тонкой настройки:
# GO_VERSION — версия Go (по умолчанию 1.23.4)
# JDK_VERSION — версия Liberica JDK (по умолчанию 21)
# NODE_VERSION — версия Node (по умолчанию 20)
# DEV_USER — имя пользователя для разработки (по умолчанию dev)
# DEV_HOME — домашняя директория dev (по умолчанию /home/dev)
# WORKSPACE_ROOT — корень рабочих репо (по умолчанию /srv/dev)
# SKIP_USER — 1, чтобы не создавать пользователя
# SKIP_CLAUDE — 1, чтобы не ставить Claude Code CLI
#
# Скрипт идемпотентен — повторный запуск не ломает то, что уже сделано.
set -euo pipefail
# --------------------------------------------------------------------- #
# Параметры
# --------------------------------------------------------------------- #
GO_VERSION="${GO_VERSION:-1.23.4}"
JDK_VERSION="${JDK_VERSION:-21}"
NODE_VERSION="${NODE_VERSION:-20}"
DEV_USER="${DEV_USER:-dev}"
DEV_HOME="${DEV_HOME:-/home/${DEV_USER}}"
WORKSPACE_ROOT="${WORKSPACE_ROOT:-/srv/dev}"
SKIP_USER="${SKIP_USER:-0}"
SKIP_CLAUDE="${SKIP_CLAUDE:-0}"
REPO_URL="https://git.zetit.ru/zuevav/Bridge-and-Join-s.git"
# --------------------------------------------------------------------- #
# Хелперы
# --------------------------------------------------------------------- #
log() { printf '[%s] %s\n' "$(date +'%Y-%m-%d %H:%M:%S')" "$*"; }
die() { printf '[ОШИБКА] %s\n' "$*" >&2; exit 1; }
need_root() { [[ "$EUID" -eq 0 ]] || die "Нужны права root. Запусти через sudo."; }
# --------------------------------------------------------------------- #
# 0. Предусловия — определение ОС и менеджера пакетов
# --------------------------------------------------------------------- #
need_root
if [[ ! -f /etc/os-release ]]; then
die "Файл /etc/os-release не найден. Проверь, что ОС поддерживает FHS."
fi
# shellcheck disable=SC1091
. /etc/os-release
OS_FAMILY="" # rpm | deb
OS_HUMAN=""
case "${ID:-}" in
redos)
OS_FAMILY="rpm"
OS_HUMAN="РЕД ОС ${VERSION_ID:-?}"
;;
rhel|centos|rocky|almalinux|fedora)
OS_FAMILY="rpm"
OS_HUMAN="${PRETTY_NAME:-RHEL-совместимая}"
log "ВНИМАНИЕ: ОС ${OS_HUMAN} — для разработки подходит, для прод-сертификации в финсекторе нужна РЕД ОС / Astra SE."
;;
ubuntu|debian)
OS_FAMILY="deb"
OS_HUMAN="${PRETTY_NAME:-${ID} ${VERSION_ID:-?}}"
log "ОС ${OS_HUMAN} — бюджетный вариант для разработки. Для прода в финсекторе нужна РЕД ОС / Astra SE с сертификатом ФСТЭК."
;;
astra)
OS_FAMILY="deb"
OS_HUMAN="Astra Linux ${VERSION_ID:-?}"
;;
*)
log "ВНИМАНИЕ: ОС определена как ${ID:-неизвестно} ${VERSION_ID:-?}."
if command -v dnf >/dev/null 2>&1 || command -v yum >/dev/null 2>&1; then
OS_FAMILY="rpm"
elif command -v apt-get >/dev/null 2>&1; then
OS_FAMILY="deb"
else
die "Не удалось определить пакетный менеджер. Поддерживаются dnf/yum (РЕД ОС, RHEL) и apt (Ubuntu, Debian, Astra)."
fi
log "Пробуем как ${OS_FAMILY}-совместимую."
;;
esac
log "ОС: ${OS_HUMAN}, семейство: ${OS_FAMILY}"
PKG_INSTALL=""
PKG_UPDATE=""
case "${OS_FAMILY}" in
rpm)
if command -v dnf >/dev/null 2>&1; then PKG_INSTALL="dnf install -y"; PKG_UPDATE="dnf -y makecache"
elif command -v yum >/dev/null 2>&1; then PKG_INSTALL="yum install -y"; PKG_UPDATE="yum -y makecache"
else die "Не найден dnf/yum"
fi
;;
deb)
export DEBIAN_FRONTEND=noninteractive
PKG_INSTALL="apt-get install -y --no-install-recommends"
PKG_UPDATE="apt-get update -y"
;;
esac
log "Обновление кэша пакетов"
${PKG_UPDATE}
# --------------------------------------------------------------------- #
# 1. Базовый системный стек
# --------------------------------------------------------------------- #
log "Шаг 1/8: установка базовых пакетов"
case "${OS_FAMILY}" in
rpm)
${PKG_INSTALL} \
git make curl wget tar gzip unzip ca-certificates \
jq xmlstarlet libxml2 \
gcc gcc-c++ binutils \
python3 python3-pip \
postgresql-libs \
openssl-libs
;;
deb)
${PKG_INSTALL} \
git make curl wget tar gzip unzip ca-certificates \
jq xmlstarlet libxml2-utils \
build-essential \
python3 python3-pip python3-venv \
libpq5 \
libssl3 \
gnupg lsb-release software-properties-common
;;
esac
# --------------------------------------------------------------------- #
# 2. Podman + podman-compose
# --------------------------------------------------------------------- #
log "Шаг 2/8: установка Podman и podman-compose"
case "${OS_FAMILY}" in
rpm)
${PKG_INSTALL} podman podman-docker || true
;;
deb)
${PKG_INSTALL} podman || true
# podman-docker может отсутствовать — не критично
apt-get install -y --no-install-recommends podman-docker 2>/dev/null || true
;;
esac
if ! command -v podman-compose >/dev/null 2>&1; then
pip3 install --quiet podman-compose || \
log "ВНИМАНИЕ: не удалось установить podman-compose через pip3. Поставь вручную."
fi
# Включаем сокет podman, чтобы клиенты, ожидающие docker.sock, работали без правок
systemctl enable --now podman.socket 2>/dev/null || true
# --------------------------------------------------------------------- #
# 3. Go
# --------------------------------------------------------------------- #
log "Шаг 3/8: установка Go ${GO_VERSION}"
GO_TARBALL="go${GO_VERSION}.linux-amd64.tar.gz"
GO_URL="https://go.dev/dl/${GO_TARBALL}"
if [[ -d /usr/local/go && -x /usr/local/go/bin/go ]]; then
CURRENT_GO_VER="$(/usr/local/go/bin/go version | awk '{print $3}' | sed 's/go//')"
if [[ "${CURRENT_GO_VER}" == "${GO_VERSION}" ]]; then
log "Go ${GO_VERSION} уже установлен"
else
log "Найден Go ${CURRENT_GO_VER}, переустанавливаем на ${GO_VERSION}"
rm -rf /usr/local/go
fi
fi
if [[ ! -d /usr/local/go ]]; then
TMP_TAR="/tmp/${GO_TARBALL}"
wget -q --show-progress -O "${TMP_TAR}" "${GO_URL}"
tar -C /usr/local -xzf "${TMP_TAR}"
rm -f "${TMP_TAR}"
fi
cat > /etc/profile.d/go.sh <<'EOF'
export PATH=$PATH:/usr/local/go/bin
export GOPATH=$HOME/go
export PATH=$PATH:$GOPATH/bin
EOF
chmod 0644 /etc/profile.d/go.sh
# --------------------------------------------------------------------- #
# 4. Liberica JDK
# --------------------------------------------------------------------- #
log "Шаг 4/8: установка Liberica JDK ${JDK_VERSION}"
case "${OS_FAMILY}" in
rpm)
if [[ ! -f /etc/yum.repos.d/bellsoft.repo ]]; then
cat > /etc/yum.repos.d/bellsoft.repo <<'EOF'
[bellsoft]
name=BellSoft Repository
baseurl=https://yum.bell-sw.com/
enabled=1
gpgcheck=1
gpgkey=https://download.bell-sw.com/pki/GPG-KEY-bellsoft
EOF
fi
${PKG_INSTALL} "bellsoft-java${JDK_VERSION}" || \
log "ВНИМАНИЕ: не удалось установить bellsoft-java${JDK_VERSION}. Альтернатива — скачать tar.gz с https://bell-sw.com/pages/downloads/."
;;
deb)
if [[ ! -f /etc/apt/sources.list.d/bellsoft.list ]]; then
curl -fsSL https://download.bell-sw.com/pki/GPG-KEY-bellsoft \
| gpg --dearmor -o /usr/share/keyrings/bellsoft.gpg
echo "deb [signed-by=/usr/share/keyrings/bellsoft.gpg] https://apt.bell-sw.com/ stable main" \
> /etc/apt/sources.list.d/bellsoft.list
apt-get update -y
fi
${PKG_INSTALL} "bellsoft-java${JDK_VERSION}-full" || \
${PKG_INSTALL} "bellsoft-java${JDK_VERSION}" || \
log "ВНИМАНИЕ: не удалось установить bellsoft-java${JDK_VERSION}. Альтернатива — скачать tar.gz с https://bell-sw.com/pages/downloads/."
;;
esac
# --------------------------------------------------------------------- #
# 5. Node.js LTS (для admin-ui и Claude Code CLI)
# --------------------------------------------------------------------- #
log "Шаг 5/8: установка Node.js ${NODE_VERSION} LTS"
need_install_node=1
if command -v node >/dev/null 2>&1; then
CUR_NODE_MAJOR="$(node --version | sed 's/v//;s/\..*//')"
if [[ "${CUR_NODE_MAJOR}" == "${NODE_VERSION}" ]]; then
log "Node.js ${NODE_VERSION} уже установлен"
need_install_node=0
fi
fi
if [[ "${need_install_node}" == "1" ]]; then
case "${OS_FAMILY}" in
rpm)
curl -fsSL "https://rpm.nodesource.com/setup_${NODE_VERSION}.x" | bash -
${PKG_INSTALL} nodejs
;;
deb)
curl -fsSL "https://deb.nodesource.com/setup_${NODE_VERSION}.x" | bash -
${PKG_INSTALL} nodejs
;;
esac
fi
# --------------------------------------------------------------------- #
# 6. Claude Code CLI
# --------------------------------------------------------------------- #
if [[ "${SKIP_CLAUDE}" != "1" ]]; then
log "Шаг 6/8: установка Claude Code CLI"
npm install -g @anthropic-ai/claude-code
else
log "Шаг 6/8: пропуск установки Claude Code CLI (SKIP_CLAUDE=1)"
fi
# --------------------------------------------------------------------- #
# 7. Пользователь dev и рабочая директория
# --------------------------------------------------------------------- #
if [[ "${SKIP_USER}" != "1" ]]; then
log "Шаг 7/8: подготовка пользователя ${DEV_USER}"
if ! id -u "${DEV_USER}" >/dev/null 2>&1; then
useradd -m -s /bin/bash -d "${DEV_HOME}" "${DEV_USER}"
log "Создан пользователь ${DEV_USER}"
else
log "Пользователь ${DEV_USER} уже существует"
fi
mkdir -p "${WORKSPACE_ROOT}"
chown "${DEV_USER}:${DEV_USER}" "${WORKSPACE_ROOT}"
HIST_SNIPPET="${DEV_HOME}/.bashrc.d-history"
cat > "${HIST_SNIPPET}" <<'EOF'
# История с timestamp и общая для всех сессий — для аудита.
export HISTTIMEFORMAT="%F %T "
export HISTSIZE=10000
export HISTFILESIZE=20000
shopt -s histappend
PROMPT_COMMAND="history -a; ${PROMPT_COMMAND:-}"
EOF
chown "${DEV_USER}:${DEV_USER}" "${HIST_SNIPPET}"
if ! grep -q "bashrc.d-history" "${DEV_HOME}/.bashrc" 2>/dev/null; then
echo "[ -f ${HIST_SNIPPET} ] && . ${HIST_SNIPPET}" >> "${DEV_HOME}/.bashrc"
fi
if ! grep -q "/usr/local/go/bin" "${DEV_HOME}/.bashrc" 2>/dev/null; then
cat >> "${DEV_HOME}/.bashrc" <<'EOF'
[ -f /etc/profile.d/go.sh ] && . /etc/profile.d/go.sh
EOF
fi
else
log "Шаг 7/8: пропуск подготовки пользователя (SKIP_USER=1)"
fi
# --------------------------------------------------------------------- #
# 8. Финальные проверки
# --------------------------------------------------------------------- #
log "Шаг 8/8: проверка установленных версий"
{
printf 'git: '; git --version
printf 'make: '; make --version | head -n1
printf 'jq: '; jq --version
printf 'xmlstarlet: '; xmlstarlet --version | head -n1
printf 'podman: '; podman --version 2>/dev/null || echo "(не установлен)"
printf 'podman-compose:'; podman-compose --version 2>/dev/null || echo "(не установлен)"
printf 'go: '; /usr/local/go/bin/go version
printf 'java: '; java -version 2>&1 | head -n1
printf 'node: '; node --version 2>/dev/null || echo "(не установлен)"
printf 'npm: '; npm --version 2>/dev/null || echo "(не установлен)"
if [[ "${SKIP_CLAUDE}" != "1" ]]; then
printf 'claude: '; claude --version 2>/dev/null || echo "(не установлен или требует первого запуска)"
fi
} || true
cat <<EOF
──────────────────────────────────────────────────────────────────────
Подготовка dev-ВМ завершена.
ОС: ${OS_HUMAN}
Дальше — под пользователем ${DEV_USER}:
su - ${DEV_USER}
cd ${WORKSPACE_ROOT}
# Положи свой публичный SSH-ключ в ~/.ssh/id_ed25519.pub
# и добавь его в git.zetit.ru → Settings → SSH Keys
git clone ${REPO_URL}
cd Bridge-and-Join-s
make ci # локальный прогон линтера, тестов и сборки
make compose-up # PostgreSQL + MinIO для разработки
# Запуск Claude Code из корня репо:
claude
Дополнительные шаги, которые НЕ автоматизированы (делаются вручную при
получении соответствующих артефактов от заказчика):
1. Установка КриптоПро CSP и КриптоПро JCP под целевую ОС.
2. Установка дистрибутива Интеграционного шлюза НРД.
3. Импорт тестовых сертификатов GUEST/TEST3 (ГОСТ + RSA).
4. Конфигурирование исходящего прокси/whitelist для git.zetit.ru,
gost-*.nsd.ru, rsa-*.nsd.ru, registry.npmjs.org, proxy.golang.org,
api.anthropic.com.
ВАЖНО про прод:
Эта ВМ годится для разработки и тестирования. Для прод-стенда в
финсекторе обязательны РЕД ОС / Astra SE с сертификатом ФСТЭК и
лицензированный КриптоПро класса КС1 — Ubuntu/Debian для прода
финсектора не подходят.
──────────────────────────────────────────────────────────────────────
EOF