From 7d12bebb6e676a1e3f272ad39cb8be3799a213f9 Mon Sep 17 00:00:00 2001 From: Andrey Date: Sun, 1 Mar 2026 01:01:54 +0300 Subject: [PATCH] feat: improve CI/CD workflows and code quality checks - ci.yml: fix workflow_dispatch (was missing options), add pull_request trigger - deploy.yml: add dry_run option for safe testing deployments - Makefile: improve code quality targets to include bots subdirectories - docker-compose.yml: clean up telegram-bot env vars (use env_file) Made-with: Cursor --- .github/workflows/ci.yml | 19 +++------- .github/workflows/deploy.yml | 32 +++++++++++++++-- Makefile | 69 ++++++++++++++++++++++++------------ docker-compose.yml | 17 +-------- 4 files changed, 83 insertions(+), 54 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 7d06489..05ca777 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -3,12 +3,9 @@ name: CI pipeline on: push: branches: [ 'dev-*', 'feature/**' ] + pull_request: + branches: [ 'main' ] workflow_dispatch: - inputs: - action: - description: 'Action to perform' - required: true - type: choice jobs: test: @@ -29,17 +26,11 @@ jobs: run: | python -m pip install --upgrade pip pip install -r tests/infra/requirements-test.txt - pip install flake8 black isort mypy || true + pip install "black>=24.0,<25" "isort>=5.13,<6" flake8 mypy || true - - name: Code formatting check (Black) + - name: Code quality (Black + isort) — те же команды, что make code-quality run: | - echo "🔍 Checking code formatting with Black..." - black --check . || (echo "❌ Code formatting issues found. Run 'black .' to fix." && exit 1) - - - name: Import sorting check (isort) - run: | - echo "🔍 Checking import sorting with isort..." - isort --check-only . || (echo "❌ Import sorting issues found. Run 'isort .' to fix." && exit 1) + make format-check import-check || (echo "" && echo "❌ Code style drift. Locally run: make import-fix && make format && git add -A && git commit -m 'style: isort + black'" && exit 1) - name: Linting (flake8) - Critical errors run: | diff --git a/.github/workflows/deploy.yml b/.github/workflows/deploy.yml index 36e2285..d8db848 100644 --- a/.github/workflows/deploy.yml +++ b/.github/workflows/deploy.yml @@ -16,6 +16,14 @@ on: description: 'Commit hash to rollback to (optional, uses last successful if empty)' required: false type: string + dry_run: + description: 'Dry run (only for deploy — no SSH, only show planned steps)' + required: false + type: choice + default: 'no' + options: + - 'no' + - 'yes' jobs: deploy: @@ -24,6 +32,8 @@ jobs: if: | github.event_name == 'push' || (github.event_name == 'workflow_dispatch' && github.event.inputs.action == 'deploy') + env: + DRY_RUN: ${{ github.event.inputs.dry_run == 'yes' }} concurrency: group: production-deploy cancel-in-progress: false @@ -36,7 +46,25 @@ jobs: with: ref: main + - name: Dry run (simulate deploy steps) + if: github.event_name == 'workflow_dispatch' && github.event.inputs.dry_run == 'yes' + run: | + echo "🔍 DRY RUN — no SSH, no changes on server" + echo "Would run on server:" + echo " 1. cd /home/prod" + echo " 2. CURRENT_COMMIT + history; git fetch origin main && git reset --hard origin/main" + echo " 3. docker-compose config (validate)" + echo " 4. docker-compose stop prometheus grafana uptime-kuma alertmanager" + echo " 5. docker-compose build --pull prometheus grafana uptime-kuma alertmanager" + echo " 6. docker-compose up -d prometheus grafana uptime-kuma alertmanager" + echo "" + echo "Secrets/vars required: SERVER_HOST, SERVER_USER, SSH_PRIVATE_KEY, SSH_PORT" + if [ -f docker-compose.yml ]; then + echo "✅ docker-compose.yml present in repo (validation would run on server)" + fi + - name: Deploy to server + if: github.event_name != 'workflow_dispatch' || github.event.inputs.dry_run != 'yes' uses: appleboy/ssh-action@v1.0.0 with: host: ${{ vars.SERVER_HOST || secrets.SERVER_HOST }} @@ -105,7 +133,7 @@ jobs: echo "✅ Infrastructure containers rebuilt and started (bots remain running)" - name: Update deploy history - if: always() + if: always() && env.DRY_RUN != 'true' uses: appleboy/ssh-action@v1.0.0 with: host: ${{ vars.SERVER_HOST || secrets.SERVER_HOST }} @@ -126,7 +154,7 @@ jobs: fi - name: Send deployment notification - if: always() + if: always() && env.DRY_RUN != 'true' uses: appleboy/telegram-action@v1.0.0 with: to: ${{ secrets.TELEGRAM_CHAT_ID }} diff --git a/Makefile b/Makefile index 6beef68..dd9b1a5 100644 --- a/Makefile +++ b/Makefile @@ -346,58 +346,83 @@ auth-list: ## Показать список пользователей мони # ======================================== # Code Quality & Formatting # ======================================== +# Явный список .py файлов (find не зависит от .gitignore). Black при обходе директорий +# учитывает .gitignore, поэтому передаём файлы явно, чтобы проверять и bots/. +PY_FIND_PATHS := . bots/telegram-helper-bot bots/AnonBot +PY_FIND_EXCLUDE := -not -path '*/.venv/*' -not -path '*/venv/*' -not -path '*/__pycache__/*' -not -path '*/.git/*' format-check: ## Проверить форматирование кода (Black) @echo "🔍 Checking code formatting with Black..." - @if [ -f .venv/bin/python ]; then \ - .venv/bin/python -m black --check . || (echo "❌ Code formatting issues found. Run 'make format' to fix." && exit 1); \ + @PY_FILES=$$(find $(PY_FIND_PATHS) -name "*.py" $(PY_FIND_EXCLUDE) 2>/dev/null | sort -u); \ + if [ -z "$$PY_FILES" ]; then \ + PY_FILES=$$(find . -name "*.py" $(PY_FIND_EXCLUDE) 2>/dev/null); \ + fi; \ + echo " Files: $$(echo "$$PY_FILES" | wc -l | tr -d ' ') .py files"; \ + if [ -f .venv/bin/python ]; then \ + .venv/bin/python -m black --check $$PY_FILES || (echo "❌ Code formatting issues found. Run 'make format' to fix." && exit 1); \ else \ - python3 -m black --check . || (echo "❌ Code formatting issues found. Run 'make format' to fix." && exit 1); \ + python3 -m black --check $$PY_FILES || (echo "❌ Code formatting issues found. Run 'make format' to fix." && exit 1); \ fi @echo "✅ Code formatting is correct!" format: ## Автоматически исправить форматирование кода (Black) @echo "🎨 Formatting code with Black..." - @if [ -f .venv/bin/python ]; then \ - .venv/bin/python -m black .; \ + @PY_FILES=$$(find $(PY_FIND_PATHS) -name "*.py" $(PY_FIND_EXCLUDE) 2>/dev/null | sort -u); \ + if [ -z "$$PY_FILES" ]; then \ + PY_FILES=$$(find . -name "*.py" $(PY_FIND_EXCLUDE) 2>/dev/null); \ + fi; \ + echo " Files: $$(echo "$$PY_FILES" | wc -l | tr -d ' ') .py files"; \ + if [ -f .venv/bin/python ]; then \ + .venv/bin/python -m black $$PY_FILES; \ else \ - python3 -m black .; \ + python3 -m black $$PY_FILES; \ fi @echo "✅ Code formatted!" format-diff: ## Показать что будет изменено Black (без применения) @echo "📋 Showing Black diff (no changes applied)..." - @if [ -f .venv/bin/python ]; then \ - .venv/bin/python -m black --diff .; \ + @PY_FILES=$$(find $(PY_FIND_PATHS) -name "*.py" $(PY_FIND_EXCLUDE) 2>/dev/null | sort -u); \ + if [ -z "$$PY_FILES" ]; then PY_FILES=$$(find . -name "*.py" $(PY_FIND_EXCLUDE) 2>/dev/null); fi; \ + if [ -f .venv/bin/python ]; then \ + .venv/bin/python -m black --diff $$PY_FILES; \ else \ - python3 -m black --diff .; \ + python3 -m black --diff $$PY_FILES; \ fi -import-check: ## Проверить сортировку импортов (isort) +import-check: ## Проверить сортировку импортов (isort, профиль black) @echo "🔍 Checking import sorting with isort..." - @if [ -f .venv/bin/python ]; then \ - .venv/bin/python -m isort --check-only . || (echo "❌ Import sorting issues found. Run 'make import-fix' to fix." && exit 1); \ + @PY_FILES=$$(find $(PY_FIND_PATHS) -name "*.py" $(PY_FIND_EXCLUDE) 2>/dev/null | sort -u); \ + if [ -z "$$PY_FILES" ]; then PY_FILES=$$(find . -name "*.py" $(PY_FIND_EXCLUDE) 2>/dev/null); fi; \ + echo " Files: $$(echo "$$PY_FILES" | wc -l | tr -d ' ') .py files"; \ + if [ -f .venv/bin/python ]; then \ + .venv/bin/python -m isort --profile black --check-only $$PY_FILES || (echo "❌ Import sorting issues found. Run 'make import-fix' to fix." && exit 1); \ else \ - python3 -m isort --check-only . || (echo "❌ Import sorting issues found. Run 'make import-fix' to fix." && exit 1); \ + python3 -m isort --profile black --check-only $$PY_FILES || (echo "❌ Import sorting issues found. Run 'make import-fix' to fix." && exit 1); \ fi @echo "✅ Import sorting is correct!" -import-fix: ## Автоматически исправить сортировку импортов (isort) +import-fix: ## Автоматически исправить сортировку импортов (isort, профиль black) @echo "📦 Fixing import sorting with isort..." - @if [ -f .venv/bin/python ]; then \ - .venv/bin/python -m isort .; \ + @PY_FILES=$$(find $(PY_FIND_PATHS) -name "*.py" $(PY_FIND_EXCLUDE) 2>/dev/null | sort -u); \ + if [ -z "$$PY_FILES" ]; then PY_FILES=$$(find . -name "*.py" $(PY_FIND_EXCLUDE) 2>/dev/null); fi; \ + if [ -f .venv/bin/python ]; then \ + .venv/bin/python -m isort --profile black $$PY_FILES; \ else \ - python3 -m isort .; \ + python3 -m isort --profile black $$PY_FILES; \ fi @echo "✅ Imports sorted!" lint-check: ## Проверить код линтером (flake8) - только критические ошибки @echo "🔍 Running flake8 linter (critical errors only)..." - @if [ -f .venv/bin/python ]; then \ - .venv/bin/python -m flake8 . --count --select=E9,F63,F7,F82 --show-source --statistics --exclude=".venv,venv,__pycache__,.git,*.pyc" || true; \ - else \ - python3 -m flake8 . --count --select=E9,F63,F7,F82 --show-source --statistics --exclude=".venv,venv,__pycache__,.git,*.pyc" || true; \ - fi + @for dir in . bots/telegram-helper-bot bots/AnonBot; do \ + if [ -d "$$dir" ]; then \ + if [ -f .venv/bin/python ]; then \ + .venv/bin/python -m flake8 $$dir --count --select=E9,F63,F7,F82 --show-source --statistics --exclude=".venv,venv,__pycache__,.git,*.pyc" 2>/dev/null || true; \ + else \ + python3 -m flake8 $$dir --count --select=E9,F63,F7,F82 --show-source --statistics --exclude=".venv,venv,__pycache__,.git,*.pyc" 2>/dev/null || true; \ + fi; \ + fi; \ + done @echo "✅ Linting check completed (non-critical warnings in dependencies ignored)!" code-quality: format-check import-check lint-check ## Проверить качество кода (все проверки) diff --git a/docker-compose.yml b/docker-compose.yml index 614ea0b..a38a84e 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -148,23 +148,8 @@ services: - LOG_RETENTION_DAYS=${LOG_RETENTION_DAYS:-30} - METRICS_HOST=${METRICS_HOST:-0.0.0.0} - METRICS_PORT=${METRICS_PORT:-8080} - # Telegram settings (токены из GitHub Secrets имеют приоритет над .env) - - TELEGRAM_BOT_TOKEN=${TELEGRAM_BOT_TOKEN:-${BOT_TOKEN}} - - TELEGRAM_LISTEN_BOT_TOKEN=${TELEGRAM_LISTEN_BOT_TOKEN:-${LISTEN_BOT_TOKEN}} - - TELEGRAM_TEST_BOT_TOKEN=${TELEGRAM_TEST_BOT_TOKEN:-${TEST_BOT_TOKEN}} - - TELEGRAM_PREVIEW_LINK=${PREVIEW_LINK:-false} - - TELEGRAM_MAIN_PUBLIC=${MAIN_PUBLIC} - - TELEGRAM_GROUP_FOR_POSTS=${GROUP_FOR_POSTS} - - TELEGRAM_GROUP_FOR_MESSAGE=${GROUP_FOR_MESSAGE} - - TELEGRAM_GROUP_FOR_LOGS=${GROUP_FOR_LOGS} - - TELEGRAM_IMPORTANT_LOGS=${IMPORTANT_LOGS} - - TELEGRAM_ARCHIVE=${ARCHIVE} - - TELEGRAM_TEST_GROUP=${TEST_GROUP} - # Bot settings - - SETTINGS_LOGS=${LOGS:-false} - - SETTINGS_TEST=${TEST:-false} - # Database - DATABASE_PATH=${DATABASE_PATH:-database/tg-bot-database.db} + # Остальные переменные (токены, чаты, админы) — только из env_file: ./bots/telegram-helper-bot/.env volumes: - ./bots/telegram-helper-bot/database:/app/database:rw - ./bots/telegram-helper-bot/logs:/app/logs:rw