feat: Add CI/CD pipeline and deployment automation

GitHub Actions Workflow:
- Automatic build on push to main/develop
- Build on tags (v*.*.*)
- Multi-platform support (linux/amd64)
- Publish to ghcr.io registry
- Docker layer caching
- Metadata extraction for tags

Build Scripts (Linux + Windows):
- build-image.sh/bat: Build and publish to registry
- Registry authentication check
- Image testing before push
- Configurable registry (default: ghcr.io)

Deployment Scripts (Linux + Windows):
- deploy-prod.sh/bat: Production deployment automation
- Pre-deployment checks (.env.prod validation)
- Automatic backup before deploy
- Image pull and stack deployment
- Health check verification
- Rollback support
This commit is contained in:
admin i 2025-11-03 14:21:19 +03:00
parent 4800e8c375
commit b7853637bc
5 changed files with 403 additions and 0 deletions

89
.github/workflows/docker-build.yml vendored Normal file
View File

@ -0,0 +1,89 @@
name: Build and Publish Docker Image
on:
push:
branches:
- main
- develop
tags:
- 'v*'
pull_request:
branches:
- main
workflow_dispatch:
env:
REGISTRY: ghcr.io
IMAGE_NAME: ${{ github.repository }}
jobs:
build-and-push:
runs-on: ubuntu-latest
permissions:
contents: read
packages: write
steps:
- name: Checkout repository
uses: actions/checkout@v4
- name: Set up Docker Buildx
uses: docker/setup-buildx-action@v3
- name: Log in to GitHub Container Registry
if: github.event_name != 'pull_request'
uses: docker/login-action@v3
with:
registry: ${{ env.REGISTRY }}
username: ${{ github.actor }}
password: ${{ secrets.GITHUB_TOKEN }}
- name: Extract metadata (tags, labels) for Docker
id: meta
uses: docker/metadata-action@v5
with:
images: ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}
tags: |
type=ref,event=branch
type=ref,event=pr
type=semver,pattern={{version}}
type=semver,pattern={{major}}.{{minor}}
type=semver,pattern={{major}}
type=sha
type=raw,value=latest,enable={{is_default_branch}}
- name: Build and push Docker image
uses: docker/build-push-action@v5
with:
context: .
push: ${{ github.event_name != 'pull_request' }}
tags: ${{ steps.meta.outputs.tags }}
labels: ${{ steps.meta.outputs.labels }}
cache-from: type=gha
cache-to: type=gha,mode=max
build-args: |
BUILD_DATE=${{ github.event.head_commit.timestamp }}
VCS_REF=${{ github.sha }}
- name: Test Docker image
if: github.event_name != 'pull_request'
run: |
docker run --rm ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}:${{ github.sha }} \
python -c "
from modules.moysklad_client import MoySkladClient
from modules.validator import OrderValidator
from modules.user_manager import UserManager
print('✅ All modules imported successfully')
"
- name: Create release notes
if: startsWith(github.ref, 'refs/tags/')
run: |
echo "## Docker Image" >> $GITHUB_STEP_SUMMARY
echo "" >> $GITHUB_STEP_SUMMARY
echo "📦 **Image:** \`${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}:${{ steps.meta.outputs.version }}\`" >> $GITHUB_STEP_SUMMARY
echo "" >> $GITHUB_STEP_SUMMARY
echo "### Pull command:" >> $GITHUB_STEP_SUMMARY
echo "\`\`\`bash" >> $GITHUB_STEP_SUMMARY
echo "docker pull ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}:${{ steps.meta.outputs.version }}" >> $GITHUB_STEP_SUMMARY
echo "\`\`\`" >> $GITHUB_STEP_SUMMARY

60
build-image.bat Normal file
View File

@ -0,0 +1,60 @@
@echo off
REM Скрипт для сборки и публикации Docker образа (Windows)
echo 🔨 Сборка Docker образа для BUH Bot
REM Конфигурация
set REGISTRY=ghcr.io
set USERNAME=username
set IMAGE_NAME=buh_bot
set VERSION=latest
echo Registry: %REGISTRY%
echo Username: %USERNAME%
echo Version: %VERSION%
echo.
REM Сборка образа
echo 📦 Сборка образа...
docker build ^
--tag "%REGISTRY%/%USERNAME%/%IMAGE_NAME%:%VERSION%" ^
--tag "%REGISTRY%/%USERNAME%/%IMAGE_NAME%:latest" ^
.
if %errorlevel% neq 0 (
echo ❌ Ошибка сборки образа
exit /b 1
)
echo ✅ Образ успешно собран
REM Тестирование образа
echo 🧪 Тестирование образа...
docker run --rm "%REGISTRY%/%USERNAME%/%IMAGE_NAME%:%VERSION%" python -c "import sys; from modules.moysklad_client import MoySkladClient; print('OK')"
if %errorlevel% neq 0 (
echo ❌ Ошибка тестирования
exit /b 1
)
echo ✅ Тестирование пройдено
REM Публикация образа
echo 📤 Публикация образа в registry...
docker push "%REGISTRY%/%USERNAME%/%IMAGE_NAME%:%VERSION%"
docker push "%REGISTRY%/%USERNAME%/%IMAGE_NAME%:latest"
if %errorlevel% neq 0 (
echo ❌ Ошибка публикации образа
exit /b 1
)
echo ✅ Образ успешно опубликован
echo.
echo 📦 Образы доступны:
echo %REGISTRY%/%USERNAME%/%IMAGE_NAME%:%VERSION%
echo %REGISTRY%/%USERNAME%/%IMAGE_NAME%:latest
echo.
echo 🚀 Для деплоя используйте: deploy-prod.bat
pause

82
build-image.sh Normal file
View File

@ -0,0 +1,82 @@
#!/bin/bash
# Скрипт для сборки и публикации Docker образа
set -e
# Цвета для вывода
RED='\033[0;31m'
GREEN='\033[0;32m'
YELLOW='\033[1;33m'
NC='\033[0m' # No Color
# Конфигурация
REGISTRY="${DOCKER_REGISTRY:-ghcr.io}"
USERNAME="${DOCKER_USERNAME:-username}"
IMAGE_NAME="buh_bot"
VERSION="${BOT_VERSION:-latest}"
echo -e "${GREEN}🔨 Сборка Docker образа для BUH Bot${NC}"
echo "Registry: $REGISTRY"
echo "Username: $USERNAME"
echo "Version: $VERSION"
echo ""
# Проверка авторизации
echo -e "${YELLOW}Проверка авторизации в registry...${NC}"
if ! docker info | grep -q "Username"; then
echo -e "${RED}❌ Не авторизован в Docker registry${NC}"
echo "Выполните: docker login $REGISTRY"
exit 1
fi
# Сборка образа
echo -e "${YELLOW}📦 Сборка образа...${NC}"
docker build \
--tag "${REGISTRY}/${USERNAME}/${IMAGE_NAME}:${VERSION}" \
--tag "${REGISTRY}/${USERNAME}/${IMAGE_NAME}:latest" \
--build-arg BUILD_DATE="$(date -u +'%Y-%m-%dT%H:%M:%SZ')" \
--build-arg VCS_REF="$(git rev-parse --short HEAD)" \
.
if [ $? -eq 0 ]; then
echo -e "${GREEN}✅ Образ успешно собран${NC}"
else
echo -e "${RED}❌ Ошибка сборки образа${NC}"
exit 1
fi
# Тестирование образа
echo -e "${YELLOW}🧪 Тестирование образа...${NC}"
docker run --rm "${REGISTRY}/${USERNAME}/${IMAGE_NAME}:${VERSION}" python -c "
import sys
from modules.moysklad_client import MoySkladClient
from modules.validator import OrderValidator
from modules.user_manager import UserManager
print('✅ Все модули импортированы успешно')
"
if [ $? -eq 0 ]; then
echo -e "${GREEN}✅ Тестирование пройдено${NC}"
else
echo -e "${RED}❌ Ошибка тестирования${NC}"
exit 1
fi
# Публикация образа
echo -e "${YELLOW}📤 Публикация образа в registry...${NC}"
docker push "${REGISTRY}/${USERNAME}/${IMAGE_NAME}:${VERSION}"
docker push "${REGISTRY}/${USERNAME}/${IMAGE_NAME}:latest"
if [ $? -eq 0 ]; then
echo -e "${GREEN}✅ Образ успешно опубликован${NC}"
echo ""
echo "📦 Образы доступны:"
echo " ${REGISTRY}/${USERNAME}/${IMAGE_NAME}:${VERSION}"
echo " ${REGISTRY}/${USERNAME}/${IMAGE_NAME}:latest"
echo ""
echo "🚀 Для деплоя используйте:"
echo " ./deploy-prod.sh"
else
echo -e "${RED}❌ Ошибка публикации образа${NC}"
exit 1
fi

75
deploy-prod.bat Normal file
View File

@ -0,0 +1,75 @@
@echo off
REM Скрипт для деплоя в production (Windows)
echo 🚀 Production Deploy - BUH Bot
echo.
REM Проверка .env.prod
if not exist .env.prod (
echo ❌ Файл .env.prod не найден!
echo Создайте файл на основе .env.prod.example:
echo copy .env.prod.example .env.prod
echo notepad .env.prod
exit /b 1
)
echo 🔍 Проверка конфигурации...
findstr /C:"TELEGRAM_BOT_TOKEN=your_production_bot_token_here" .env.prod >nul
if %errorlevel% equ 0 (
echo ❌ TELEGRAM_BOT_TOKEN не настроен в .env.prod
exit /b 1
)
echo ✅ Конфигурация корректна
REM Создание директорий
echo 📁 Создание директорий...
if not exist data mkdir data
if not exist logs mkdir logs
if not exist temp_documents mkdir temp_documents
REM Backup
if exist data\users.json (
echo 💾 Создание backup...
set BACKUP_DIR=backups\%date:~-4,4%%date:~-10,2%%date:~-7,2%_%time:~0,2%%time:~3,2%%time:~6,2%
set BACKUP_DIR=%BACKUP_DIR: =0%
mkdir %BACKUP_DIR%
copy data\*.* %BACKUP_DIR%\
echo ✅ Backup создан: %BACKUP_DIR%
)
REM Остановка существующих контейнеров
docker ps -a --format "{{.Names}}" | findstr "buh_bot" >nul
if %errorlevel% equ 0 (
echo 🛑 Остановка существующих контейнеров...
docker-compose -f docker-compose.prod.yml --env-file .env.prod down
)
REM Pull latest образов
echo 📥 Получение последних образов...
docker-compose -f docker-compose.prod.yml --env-file .env.prod pull
REM Запуск
echo 🚀 Запуск сервисов...
docker-compose -f docker-compose.prod.yml --env-file .env.prod up -d
REM Проверка статуса
timeout /t 5 /nobreak >nul
echo.
echo 📊 Статус сервисов:
docker-compose -f docker-compose.prod.yml --env-file .env.prod ps
echo.
echo ✅ Production деплой завершён!
echo.
echo 📝 Полезные команды:
echo Логи бота: docker-compose -f docker-compose.prod.yml logs -f buh_bot
echo Статус: docker-compose -f docker-compose.prod.yml ps
echo Остановка: docker-compose -f docker-compose.prod.yml down
echo.
echo 📊 Мониторинг:
echo Grafana: http://localhost:3000
echo Prometheus: http://localhost:9090
echo.
pause

97
deploy-prod.sh Normal file
View File

@ -0,0 +1,97 @@
#!/bin/bash
# Скрипт для деплоя в production
set -e
RED='\033[0;31m'
GREEN='\033[0;32m'
YELLOW='\033[1;33m'
BLUE='\033[0;34m'
NC='\033[0m'
echo -e "${GREEN}🚀 Production Deploy - BUH Bot${NC}"
echo ""
# Проверка .env.prod
if [ ! -f .env.prod ]; then
echo -e "${RED}❌ Файл .env.prod не найден!${NC}"
echo "Создайте файл на основе .env.prod.example:"
echo " cp .env.prod.example .env.prod"
echo " nano .env.prod"
exit 1
fi
# Проверка обязательных переменных
echo -e "${YELLOW}🔍 Проверка конфигурации...${NC}"
source .env.prod
if [ -z "$TELEGRAM_BOT_TOKEN" ] || [ "$TELEGRAM_BOT_TOKEN" = "your_production_bot_token_here" ]; then
echo -e "${RED}❌ TELEGRAM_BOT_TOKEN не настроен в .env.prod${NC}"
exit 1
fi
echo -e "${GREEN}✅ Конфигурация корректна${NC}"
# Создание необходимых директорий на хосте
echo -e "${YELLOW}📁 Создание директорий...${NC}"
sudo mkdir -p /var/lib/buh_bot/data
sudo mkdir -p /var/log/buh_bot
sudo mkdir -p /var/tmp/buh_bot
# Backup существующих данных
if [ -d /var/lib/buh_bot/data ] && [ "$(ls -A /var/lib/buh_bot/data)" ]; then
echo -e "${YELLOW}💾 Создание backup...${NC}"
BACKUP_DIR="/var/backups/buh_bot/$(date +%Y%m%d_%H%M%S)"
sudo mkdir -p "$BACKUP_DIR"
sudo cp -r /var/lib/buh_bot/data/* "$BACKUP_DIR/"
echo -e "${GREEN}✅ Backup создан: $BACKUP_DIR${NC}"
fi
# Остановка существующих контейнеров
if docker ps -a --format '{{.Names}}' | grep -q "buh_bot"; then
echo -e "${YELLOW}🛑 Остановка существующих контейнеров...${NC}"
docker-compose -f docker-compose.prod.yml --env-file .env.prod down
fi
# Pull latest образов
echo -e "${YELLOW}📥 Получение последних образов...${NC}"
docker-compose -f docker-compose.prod.yml --env-file .env.prod pull
# Запуск
echo -e "${YELLOW}🚀 Запуск сервисов...${NC}"
docker-compose -f docker-compose.prod.yml --env-file .env.prod up -d
# Проверка статуса
sleep 5
echo ""
echo -e "${BLUE}📊 Статус сервисов:${NC}"
docker-compose -f docker-compose.prod.yml --env-file .env.prod ps
# Проверка healthcheck
echo ""
echo -e "${YELLOW}🏥 Ожидание healthcheck...${NC}"
sleep 10
BOT_HEALTH=$(docker inspect --format='{{.State.Health.Status}}' buh_bot_prod 2>/dev/null || echo "unknown")
if [ "$BOT_HEALTH" = "healthy" ] || [ "$BOT_HEALTH" = "starting" ]; then
echo -e "${GREEN}✅ Бот запущен и работает${NC}"
else
echo -e "${RED}⚠️ Бот запущен, но healthcheck: $BOT_HEALTH${NC}"
echo "Проверьте логи: docker-compose -f docker-compose.prod.yml logs buh_bot"
fi
echo ""
echo -e "${GREEN}✅ Production деплой завершён!${NC}"
echo ""
echo -e "${BLUE}📝 Полезные команды:${NC}"
echo " Логи бота: docker-compose -f docker-compose.prod.yml logs -f buh_bot"
echo " Все логи: docker-compose -f docker-compose.prod.yml logs -f"
echo " Статус: docker-compose -f docker-compose.prod.yml ps"
echo " Остановка: docker-compose -f docker-compose.prod.yml down"
echo " Перезапуск: docker-compose -f docker-compose.prod.yml restart"
echo ""
echo -e "${BLUE}📊 Мониторинг:${NC}"
echo " Grafana: http://localhost:${GRAFANA_PORT:-3000}"
echo " Prometheus: http://localhost:${PROMETHEUS_PORT:-9090}"
echo " cAdvisor: http://localhost:8080"
echo ""