#!/bin/bash # ============================================================================= # SmartSolTech - Скрипт автоматического обновления # ============================================================================= set -e # Выход при любой ошибке # Цвета для вывода RED='\033[0;31m' GREEN='\033[0;32m' YELLOW='\033[1;33m' BLUE='\033[0;34m' NC='\033[0m' # No Color # Функция для логирования log() { echo -e "${BLUE}[$(date '+%Y-%m-%d %H:%M:%S')]${NC} $1" } success() { echo -e "${GREEN}✅ $1${NC}" } warning() { echo -e "${YELLOW}⚠️ $1${NC}" } error() { echo -e "${RED}❌ $1${NC}" } # Функция проверки зависимостей check_dependencies() { log "Проверка зависимостей..." if ! command -v git &> /dev/null; then error "Git не установлен" exit 1 fi if ! command -v docker &> /dev/null; then error "Docker не установлен" exit 1 fi if ! command -v docker-compose &> /dev/null; then error "Docker Compose не установлен" exit 1 fi success "Все зависимости найдены" } # Функция очистки staticfiles cleanup_staticfiles() { log "Очистка staticfiles..." if [ -d "smartsoltech/staticfiles" ]; then warning "Найдена папка staticfiles, удаляем..." chmod -R 755 smartsoltech/staticfiles 2>/dev/null || true rm -rf smartsoltech/staticfiles success "Staticfiles очищены" fi } # Функция обновления кода update_code() { local remote_name="${1:-origin}" log "Обновление кода из репозитория $remote_name..." # Проверяем существование удаленного репозитория if ! git remote | grep -q "^${remote_name}$"; then error "Удаленный репозиторий '$remote_name' не найден" log "Доступные репозитории:" git remote -v return 1 fi # Настраиваем стратегию pull если не настроено if [ -z "$(git config pull.rebase 2>/dev/null)" ]; then log "Настраиваем стратегию Git pull..." git config pull.rebase false fi # Сохраняем неотслеживаемые изменения if [ -n "$(git status --porcelain)" ]; then warning "Обнаружены локальные изменения, сохраняем..." git add . git commit -m "Auto commit before update $(date '+%Y-%m-%d %H:%M:%S')" || true fi # Получаем обновления log "Получение обновлений из $remote_name..." git fetch $remote_name # Обновляем текущую ветку local current_branch current_branch=$(git rev-parse --abbrev-ref HEAD) # Пробуем обновить с обработкой конфликтов if ! git pull $remote_name $current_branch; then error "Не удалось обновить код. Возможно есть конфликты." log "Попробуйте выполнить команды вручную:" log " git status" log " git merge --abort # если нужно отменить" log " git pull $remote_name $current_branch" return 1 fi success "Код обновлен с ветки $current_branch из $remote_name" } # Функция остановки контейнеров stop_containers() { log "Остановка контейнеров..." if docker-compose ps | grep -q "Up"; then docker-compose down success "Контейнеры остановлены" else warning "Контейнеры уже остановлены" fi } # Функция сборки образов build_images() { log "Сборка Docker образов..." # Принудительная пересборка docker-compose build --no-cache success "Образы собраны" } # Функция запуска контейнеров start_containers() { log "Запуск контейнеров..." # Запуск в фоновом режиме docker-compose up -d # Ожидание готовности log "Ожидание готовности сервисов..." sleep 10 # Проверка статуса if docker-compose ps | grep -q "Exit"; then error "Некоторые контейнеры завершились с ошибкой" docker-compose ps return 1 fi success "Контейнеры запущены" } # Функция выполнения миграций run_migrations() { log "Выполнение миграций Django..." # Ожидание готовности БД log "Ожидание готовности базы данных..." sleep 15 # Выполнение миграций docker-compose exec -T web python smartsoltech/manage.py migrate success "Миграции выполнены" } # Функция сбора статических файлов collect_static() { log "Сбор статических файлов..." docker-compose exec -T web python smartsoltech/manage.py collectstatic --noinput success "Статические файлы собраны" } # Функция проверки состояния сервисов health_check() { log "Проверка состояния сервисов..." # Проверка веб-сервера local max_attempts=30 local attempt=1 while [ $attempt -le $max_attempts ]; do if curl -sf http://localhost:8000/ > /dev/null 2>&1; then success "Веб-сервер доступен" break fi if [ $attempt -eq $max_attempts ]; then error "Веб-сервер недоступен после $max_attempts попыток" return 1 fi log "Попытка $attempt/$max_attempts - ожидание веб-сервера..." sleep 5 ((attempt++)) done # Показать статус контейнеров echo "" log "Статус контейнеров:" docker-compose ps success "Проверка здоровья завершена" } # Функция отображения логов show_logs() { log "Последние логи сервисов:" echo "" docker-compose logs --tail=20 web } # Функция бэкапа в удаленный репозиторий backup_to_remote() { local backup_remote="${1:-backup}" log "Создание бэкапа в удаленном репозитории $backup_remote..." if git remote | grep -q "^${backup_remote}$"; then # Проверяем есть ли изменения для коммита if ! git diff --quiet || ! git diff --cached --quiet; then git add . git commit -m "Auto backup before update $(date '+%Y-%m-%d %H:%M:%S')" fi # Пушим в backup if git push $backup_remote master; then success "Бэкап создан в удаленном репозитории $backup_remote" else warning "Не удалось создать бэкап в $backup_remote" fi else warning "$backup_remote репозиторий не настроен, пропускаем" fi } # Главная функция main() { local remote_source="${1:-origin}" local backup_remote="${2:-backup}" echo "" echo "🚀 SmartSolTech - Автоматическое обновление" echo "==========================================" echo "📡 Источник: $remote_source" if git remote | grep -q "^${backup_remote}$"; then echo "💾 Бэкап: $backup_remote" fi echo "" # Проверка что мы в правильной директории if [ ! -f "docker-compose.yml" ]; then error "docker-compose.yml не найден. Запустите скрипт из корня проекта." exit 1 fi local start_time start_time=$(date +%s) # Выполняем все этапы check_dependencies backup_to_remote "$backup_remote" cleanup_staticfiles update_code "$remote_source" stop_containers build_images start_containers run_migrations collect_static health_check local end_time end_time=$(date +%s) local duration=$((end_time - start_time)) echo "" echo "🎉 Обновление завершено успешно!" echo "⏱️ Время выполнения: ${duration} секунд" echo "📡 Источник обновления: $remote_source" echo "" echo "📊 Полезная информация:" echo " • Веб-сайт: http://localhost:8000" echo " • Админка: http://localhost:8000/admin" echo " • PgAdmin: http://localhost:8080" echo "" # Предложение показать логи read -p "Показать логи сервисов? (y/N): " show_logs_choice if [[ $show_logs_choice =~ ^[Yy]$ ]]; then show_logs fi success "Готово!" } # Обработка прерываний trap 'echo ""; error "Обновление прервано пользователем"; exit 130' INT TERM # Обработка ошибок error_handler() { local line_no=$1 error "Ошибка на строке $line_no" echo "" echo "Для диагностики можете выполнить:" echo " docker-compose logs" echo " docker-compose ps" exit 1 } trap 'error_handler $LINENO' ERR # Запуск с параметрами case "${1:-}" in --help|-h) echo "SmartSolTech - Скрипт автоматического обновления" echo "" echo "Использование:" echo " $0 - Полное обновление из origin (по умолчанию)" echo " $0 origin - Обновление из origin репозитория" echo " $0 backup - Обновление из backup репозитория" echo " $0 origin backup - Обновление из origin с бэкапом в backup" echo " $0 backup origin - Обновление из backup с бэкапом в origin" echo " $0 --help - Показать эту справку" echo " $0 --logs - Показать логи без обновления" echo " $0 --status - Показать статус без обновления" echo "" echo "Примеры:" echo " $0 # обновление из origin" echo " $0 backup # обновление из backup репозитория" echo " $0 origin backup # обновление из origin, бэкап в backup" echo "" exit 0 ;; --logs) show_logs exit 0 ;; --status) docker-compose ps health_check exit 0 ;; *) # Передаем все параметры в main main "$@" ;; esac