name: CI/CD on: push: workflow_dispatch: jobs: test: name: Тесты CRM runs-on: ubuntu-latest steps: - uses: actions/checkout@v4 - name: Синтаксис Python run: python3 -m compileall -q crm - name: Тесты CRM (sqlite-бэкенд, чистый каталог данных) run: | cd crm rm -rf data python3 -m pip install --break-system-packages -q -r requirements-dev.txt python3 -m pytest tests -q deploy: name: Деплой neon-bar86.ru runs-on: ubuntu-latest needs: test if: github.ref == 'refs/heads/main' && (github.event_name == 'push' || github.event_name == 'workflow_dispatch') steps: - uses: actions/checkout@v4 - name: SSH-окружение run: | mkdir -p ~/.ssh printf '%s\n' "${{ secrets.DEPLOY_KNOWN_HOSTS }}" > ~/.ssh/known_hosts printf '%s\n' "${{ secrets.DEPLOY_SSH_KEY }}" > ~/.ssh/deploy_key chmod 600 ~/.ssh/deploy_key ~/.ssh/known_hosts # Гейт №1: дамп БД до ЛЮБЫХ изменений — деплой всегда обратим по данным - name: Бэкап БД перед деплоем (ротация — храним 7) run: | ssh -i ~/.ssh/deploy_key ${{ secrets.DEPLOY_USER }}@${{ secrets.DEPLOY_HOST }} \ "cd ${{ secrets.DEPLOY_DIR }} && mkdir -p backups \ && docker exec neon-db sh -c 'pg_dump -U \$POSTGRES_USER \$POSTGRES_DB' | gzip > backups/db-pre-deploy-\$(date +%F-%H%M%S).sql.gz \ && test \$(stat -c%s backups/db-pre-deploy-*.sql.gz | tail -1) -gt 200 \ && ls -1t backups/db-pre-deploy-*.sql.gz | tail -n +8 | xargs -r rm -f \ && echo 'db backup ok'" # Код обновляется git-pull-ом на сервере: .env, crm/media, crm/data, # бэкапы и прочие незакоммиченные файлы защищены .gitignore - name: Обновить код из git run: | ssh -i ~/.ssh/deploy_key ${{ secrets.DEPLOY_USER }}@${{ secrets.DEPLOY_HOST }} \ "cd ${{ secrets.DEPLOY_DIR }} \ && git fetch origin \ && git reset --hard origin/main \ && git clean -fdq crm site \ && git log --oneline -1" # Гейт №2: сломанный compose/.env падает здесь, живые контейнеры не тронуты - name: Pre-flight — валидация compose и env run: | ssh -i ~/.ssh/deploy_key ${{ secrets.DEPLOY_USER }}@${{ secrets.DEPLOY_HOST }} \ "cd ${{ secrets.DEPLOY_DIR }} && docker compose config -q && echo 'compose+env ok'" - name: Сборка и перезапуск run: | ssh -i ~/.ssh/deploy_key ${{ secrets.DEPLOY_USER }}@${{ secrets.DEPLOY_HOST }} \ "cd ${{ secrets.DEPLOY_DIR }} && docker compose up -d --build 2>&1 | tail -15" # Гейт №3: не поднялось — пайплайн красный (данные в томе pgdata целы, # откат кода: git revert + перезапуск пайплайна; откат данных: дамп из backups/) - name: Health-check (сайт + API сквозь nginx) run: | ssh -i ~/.ssh/deploy_key ${{ secrets.DEPLOY_USER }}@${{ secrets.DEPLOY_HOST }} \ 'for i in $(seq 1 24); do curl -fsS --max-time 5 http://127.0.0.1:8080/api/healthz >/dev/null 2>&1 && curl -fsS --max-time 5 http://127.0.0.1:8080/ >/dev/null 2>&1 && exit 0 sleep 5 done echo "health check failed"; exit 1' - name: Чистка dangling-образов (диск 82%) if: always() run: | ssh -i ~/.ssh/deploy_key ${{ secrets.DEPLOY_USER }}@${{ secrets.DEPLOY_HOST }} "docker image prune -f"