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>
This commit is contained in:
zuevav
2026-05-05 14:42:18 +03:00
parent 6222612af2
commit d5b5597c67
25 changed files with 1037 additions and 0 deletions
+373
View File
@@ -0,0 +1,373 @@
#!/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