ci: registry-based releases — multi-arch images built in CI, servers only pull

- release workflow: buildx multi-arch (amd64+arm64) build+push of bot and admin images to Gitea Container Registry (git.softuniq.eu/telegram-market/telegram-shop[-admin])
- docker-compose: bot/admin use registry images (IMAGE_TAG for pinning); tor-proxy stays local alpine build
- install.sh: pull from registry instead of local docker build (no native compilation on weak hardware); interactive .env setup preserved
- new buildx runner gitea-host-buildx (label buildx) on x86_64 host
- REGISTRY_TOKEN replaced with package-scoped token (write:package)
- README/VERSION/.env.example updated
This commit is contained in:
NW
2026-08-11 10:37:09 +01:00
parent 33ec420fc0
commit 5c7bc242c5
6 changed files with 272 additions and 85 deletions

View File

@@ -155,40 +155,138 @@ else
fi
# ============================================================
# 4. Проверка обязательных переменных
# 4. Настройка переменных окружения (интерактивно)
# ============================================================
print_step "Проверка переменных окружения"
print_step "Настройка переменных окружения (интерактивно)"
MISSING_COUNT=0
MISSING_LIST=""
for VAR in BOT_TOKEN ADMIN_IDS ENCRYPTION_KEY; do
VAL=$(grep -E "^${VAR}=" .env 2>/dev/null | cut -d'=' -f2- | tr -d '"' | tr -d '\r' || true)
if [ -z "$VAL" ] || [ "$VAL" = "your_bot_token_here" ] || [ "$VAL" = "123456789,987654321" ]; then
MISSING_COUNT=$((MISSING_COUNT + 1))
MISSING_LIST="$MISSING_LIST $VAR"
# Функция для обновления переменной в .env (безопасно для спецсимволов в значении)
update_env_var() {
var_name="$1"
var_value="$2"
if grep -q "^${var_name}=" .env 2>/dev/null; then
awk -v key="$var_name" -v val="$var_value" '
BEGIN { FS = OFS = "="; done = 0 }
/^[A-Za-z_][A-Za-z0-9_]*=/ {
if ($1 == key) { print key "=" val; done = 1; next }
}
{ print }
END { if (!done) print key "=" val }
' .env > .env.tmp && mv .env.tmp .env
else
echo "${var_name}=${var_value}" >> .env
fi
done
}
if [ "$MISSING_COUNT" -ne 0 ]; then
print_warn "Не заполнены или имеют placeholder значения:$MISSING_LIST"
# Скрытый ввод секретов (POSIX sh, работает в Alpine ash)
read_secret() {
printf "%s" "$1"
stty -echo 2>/dev/null
read -r input
stty echo 2>/dev/null
printf "\n"
printf "${YELLOW}Откройте .env в редакторе и заполните:${NC}\n"
for V in $MISSING_LIST; do
printf " - %s\n" "$V"
done
printf "\n"
printf "Продолжить всё равно? (y/N): "
read -r CONTINUE
case "$CONTINUE" in
[Yy]|[Yy][Ee][Ss])
;;
*)
print_info "Откройте .env, заполните значения и запустите install.sh снова."
exit 0
;;
esac
}
# Функция для генерации случайного hex-строки (32 байта)
generate_random_hex() {
if command -v node >/dev/null 2>&1; then
node -e "console.log(require('crypto').randomBytes(32).toString('hex'))"
elif command -v openssl >/dev/null 2>&1; then
openssl rand -hex 32
else
od -An -tx32 /dev/urandom | tr -d ' \n'
fi
}
# BOT_TOKEN
current_bot_token=$(grep -E "^BOT_TOKEN=" .env 2>/dev/null | cut -d'=' -f2- | tr -d '"') || true
if [ -z "$current_bot_token" ] || [ "$current_bot_token" = "your_bot_token_here" ]; then
print_warn "BOT_TOKEN не задан или содержит placeholder."
read_secret "Введите BOT_TOKEN (от @BotFather): "
update_env_var "BOT_TOKEN" "$input"
bot_token="$input"
else
print_ok "Все обязательные переменные заполнены"
bot_token="$current_bot_token"
fi
# ADMIN_IDS
current_admin_ids=$(grep -E "^ADMIN_IDS=" .env 2>/dev/null | cut -d'=' -f2- | tr -d '"') || true
if [ -z "$current_admin_ids" ] || [ "$current_admin_ids" = "123456789,987654321" ]; then
print_warn "ADMIN_IDS не задан или содержит placeholder."
printf "Введите ADMIN_IDS (через запятую, например: 123456789,987654321): "
read -r input
update_env_var "ADMIN_IDS" "$input"
admin_ids="$input"
else
admin_ids="$current_admin_ids"
fi
# ENCRYPTION_KEY
current_encryption_key=$(grep -E "^ENCRYPTION_KEY=" .env 2>/dev/null | cut -d'=' -f2- | tr -d '"') || true
if [ -z "$current_encryption_key" ]; then
encryption_key=$(generate_random_hex)
update_env_var "ENCRYPTION_KEY" "$encryption_key"
print_ok "ENCRYPTION_KEY сгенерирован автоматически"
else
encryption_key="$current_encryption_key"
fi
# ADMIN_SECRET
current_admin_secret=$(grep -E "^ADMIN_SECRET=" .env 2>/dev/null | cut -d'=' -f2- | tr -d '"') || true
if [ -z "$current_admin_secret" ] || [ "$current_admin_secret" = "changeme" ]; then
admin_secret=$(generate_random_hex)
update_env_var "ADMIN_SECRET" "$admin_secret"
print_ok "ADMIN_SECRET сгенерирован автоматически"
else
admin_secret="$current_admin_secret"
fi
# SUPER_ADMIN_SECRET
current_super_admin_secret=$(grep -E "^SUPER_ADMIN_SECRET=" .env 2>/dev/null | cut -d'=' -f2- | tr -d '"') || true
if [ -z "$current_super_admin_secret" ] || [ "$current_super_admin_secret" = "changeme_super" ]; then
# Генерация до тех пор, пока не получим значение, отличное от ADMIN_SECRET
super_admin_secret=""
while [ -z "$super_admin_secret" ] || [ "$super_admin_secret" = "$admin_secret" ]; do
super_admin_secret=$(generate_random_hex)
done
update_env_var "SUPER_ADMIN_SECRET" "$super_admin_secret"
print_ok "SUPER_ADMIN_SECRET сгенерирован автоматически"
else
super_admin_secret="$current_super_admin_secret"
fi
# SHOP_ACTIVATED
current_shop_activated=$(grep -E "^SHOP_ACTIVATED=" .env 2>/dev/null | cut -d'=' -f2- | tr -d '"') || true
if [ -z "$current_shop_activated" ]; then
shop_activated="false"
update_env_var "SHOP_ACTIVATED" "$shop_activated"
print_info "SHOP_ACTIVATED установлен в 'false' (магазин стартует заблокированным)"
else
shop_activated="$current_shop_activated"
fi
# ADMIN_URL
current_admin_url=$(grep -E "^ADMIN_URL=" .env 2>/dev/null | cut -d'=' -f2- | tr -d '"') || true
if [ -z "$current_admin_url" ]; then
# Попытка определить LAN IP
lan_ip=$(hostname -I | awk '{print $1}')
if [ -z "$lan_ip" ]; then
lan_ip=$(ip route get 1 | awk '{print $7}' 2>/dev/null)
fi
if [ -n "$lan_ip" ]; then
admin_url="http://${lan_ip}:3000"
update_env_var "ADMIN_URL" "$admin_url"
print_info "ADMIN_URL установлен в $admin_url (определен автоматически)"
else
print_warn "Не удалось определить LAN IP. ADMIN_URL не установлен."
fi
else
admin_url="$current_admin_url"
fi
# Ограничиваем доступ к .env (содержит секреты)
if [ -f .env ]; then
chmod 600 .env
print_ok ".env защищён (chmod 600)"
fi
# ============================================================
@@ -199,22 +297,24 @@ mkdir -p db uploads
print_ok "db/, uploads/ готовы"
# ============================================================
# 6. Сборка образа
# 6. Загрузка образов из Gitea Container Registry
# ============================================================
print_step "Сборка Docker-образа (это может занять несколько минут)"
print_step "Загрузка образов из Gitea Container Registry"
print_info "Архитектура: $ARCH_NAME"
print_info "Нативные модули: better-sqlite3, tiny-secp256k1 (компилируются в builder)"
print_info "Образы собираются в CI (Gitea Actions, buildx multi-arch) — на сервере ничего не компилируется"
# Docker автоматически собирает под текущую архитектуру хоста
# buildx нужен только для multi-arch push в registry, для локальной сборки — обычный build
docker build -t telegram-shop:latest .
print_ok "Образ telegram-shop:latest собран"
# Релизная версия: по умолчанию latest, можно переопределить IMAGE_TAG=v1.2.9
IMAGE_TAG="${IMAGE_TAG:-latest}"
print_info "Используемая версия образа: $IMAGE_TAG"
$COMPOSE_CMD pull telegram_shop_prod tg_shop_admin
print_ok "Образы telegram-shop:${IMAGE_TAG} и telegram-shop-admin:${IMAGE_TAG} загружены"
# ============================================================
# 7. Запуск контейнеров
# ============================================================
print_step "Запуск контейнеров"
$COMPOSE_CMD up -d
IMAGE_TAG="${IMAGE_TAG}" $COMPOSE_CMD up -d
print_ok "Контейнеры запущены"
# ============================================================
@@ -257,7 +357,7 @@ else
fi
# ============================================================
# 9. Tor Proxy — onion-адреса
# 11. Tor Proxy — onion-адреса
# ============================================================
printf "\n"
print_step "Проверка Tor-прокси"
@@ -283,7 +383,7 @@ if [ "$TOR_RUNNING" -eq 1 ]; then
printf "\n ${BOLD}Подключение:${NC}\n"
printf " SSH: torify ssh root@%s\n" "$SSH_ONION"
printf " Admin: откройте http://%s в Tor Browser\n" "$ADMIN_ONION"
if [ -f "./tor-proxy/get-onions.sh" ]; then
sh ./tor-proxy/get-onions.sh 2>/dev/null || true
fi
@@ -295,6 +395,35 @@ else
printf " docker logs tor-proxy\n"
fi
# ============================================================
# 12. Финальная сводка
# ============================================================
printf "\n"
print_step "Финальная сводка"
# LAN IP для админки (если не определен ранее)
if [ -z "${admin_url:-}" ]; then
lan_ip=$(hostname -I | awk '{print $1}')
if [ -z "$lan_ip" ]; then
lan_ip=$(ip route get 1 | awk '{print $7}' 2>/dev/null)
fi
if [ -n "$lan_ip" ]; then
admin_url="http://${lan_ip}:3000"
else
admin_url="Не определен"
fi
fi
printf " ${CYAN}${BOLD}LAN URL админки:${NC} %s\n" "$admin_url"
printf " ${CYAN}${BOLD}SSH onion:${NC} %s\n" "$SSH_ONION"
printf " ${CYAN}${BOLD}Admin onion:${NC} %s\n" "$ADMIN_ONION"
printf " ${CYAN}${BOLD}ADMIN_SECRET:${NC} %s\n" "$admin_secret"
printf " ${CYAN}${BOLD}SUPER_ADMIN_SECRET:${NC} %s\n" "$super_admin_secret"
printf "\n"
printf "${YELLOW}⚠ Магазин стартует заблокированным (SHOP_ACTIVATED=false).${NC}\n"
printf "${YELLOW} Войдите как супер-админ, добавьте комиссионные кошельки и активируйте магазин в Настройках.${NC}\n"
printf "\n"
# ============================================================
# Готово
# ============================================================