За что отвечает CI в процессе разработки
Комментарии (1)
Ответ сгенерирован нейросетью и может содержать ошибки
CI (Continuous Integration) в разработке
CI — это практика и инструментарий, который автоматизирует проверку кода и обеспечивает качество при каждом коммите. Рассмотрю полный спектр ответственности CI.
1. Основная ответственность CI
Главная роль: Проверить, что код готов к merge, ДО того как он попадёт в main ветку.
Разработчик коммитит код → CI запускается → Проверяет всё → Результат: OK или FAILED
Eсли CI падает, код в main не попадает (merge блокируется).
2. Линтинг и статический анализ
Задача: Поймать стилистические и логические ошибки до code review.
# .github/workflows/lint.yml
name: Lint
on: [push, pull_request]
jobs:
lint:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3
- uses: actions/setup-python@v4
with:
python-version: '3.10'
- name: Install dependencies
run: pip install ruff black mypy
- name: Check formatting
run: black --check .
- name: Lint
run: ruff check .
- name: Type checking
run: mypy .
Что проверяет:
- Форматирование кода (Black, Prettier)
- Стиль (PEP 8, Ruff)
- Типизация (MyPy, Pyright)
- Неиспользуемые импорты
- Потенциальные ошибки
# Рассмотрите пример: Ruff поймает это
# Неиспользуемый импорт
import os # ❌ Ruff скажет: "Unused import"
# Неопределённая переменная
print(undefined_var) # ❌ MyPy скажет: "Name is not defined"
# Плохой формат
def func(x,y,z): # ❌ Black переформатирует
pass
# После CI fix
def func(x, y, z): # ✅ Правильно
pass
3. Unit тестирование
Задача: Убедиться, что логика работает правильно.
name: Tests
on: [push, pull_request]
jobs:
test:
runs-on: ubuntu-latest
services:
postgres:
image: postgres:14
env:
POSTGRES_PASSWORD: password
redis:
image: redis:7
steps:
- uses: actions/checkout@v3
- uses: actions/setup-python@v4
- name: Install dependencies
run: pip install -e .
- name: Run tests
run: pytest --cov=. --cov-report=xml
- name: Upload coverage
uses: codecov/codecov-action@v3
Пример теста:
import pytest
from app.users import create_user, get_user
@pytest.mark.asyncio
async def test_create_user():
"""Тест создания пользователя"""
user = await create_user(email="test@example.com", password="secure123")
assert user.email == "test@example.com"
assert user.password_hash is not None # Зашифрована
@pytest.mark.asyncio
async def test_get_user():
"""Тест получения пользователя"""
user = await get_user(user_id=1)
assert user is not None
assert user.id == 1
@pytest.mark.asyncio
async def test_user_not_found():
"""Тест: пользователь не найден"""
user = await get_user(user_id=99999)
assert user is None
CI проверяет:
- Все тесты проходят
- Coverage >= 80% (или что-то требуемое)
- Нет flaky tests
- Performance tests (если есть)
4. Интеграционные тесты
Задача: Убедиться, что компоненты работают вместе.
# Пример: интеграция API + БД
import pytest
from httpx import AsyncClient
from app.main import app
@pytest.mark.asyncio
async def test_create_user_api():
"""Полный flow: HTTP request → Business logic → Database"""
async with AsyncClient(app=app, base_url="http://test") as client:
response = await client.post(
"/api/v1/users",
json={"email": "test@example.com", "password": "secure123"}
)
assert response.status_code == 201
data = response.json()
assert data["email"] == "test@example.com"
# Проверяем, что в БД сохранилось
get_response = await client.get(f"/api/v1/users/{data['id']}")
assert get_response.status_code == 200
5. Security сканирование
Задача: Поймать уязвимости в зависимостях и коде.
name: Security
on: [push, pull_request]
jobs:
security:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3
# Проверка зависимостей
- name: Check dependencies
run: pip install safety && safety check
# SAST (Static Application Security Testing)
- name: Security scanning
run: pip install bandit && bandit -r . -f json
# Secret scanning
- name: Secret scanning
uses: trufflesecurity/trufflehog@main
Что проверяет:
# Bandit поймёт уязвимости
# ❌ Уязвимо: SQL injection
query = f"SELECT * FROM users WHERE id = {user_id}"
db.execute(query)
# ✅ Безопасно: parameterized query
query = "SELECT * FROM users WHERE id = ?"
db.execute(query, (user_id,))
# ❌ Уязвимо: hardcoded secret
API_KEY = "sk_live_123456789"
# ✅ Безопасно: из environment
import os
API_KEY = os.getenv("API_KEY")
# ❌ Уязвимо: слабый hash
import hashlib
password_hash = hashlib.md5(password).hexdigest()
# ✅ Безопасно: bcrypt
from bcrypt import hashpw, gensalt
password_hash = hashpw(password.encode(), gensalt())
6. Build и артефакты
Задача: Убедиться, что проект успешно собирается.
name: Build
on: [push, pull_request]
jobs:
build:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3
- name: Build Docker image
run: docker build -t myapp:latest .
- name: Push to registry
if: github.ref == 'refs/heads/main'
run: |
docker tag myapp:latest myregistry.azurecr.io/myapp:latest
docker push myregistry.azurecr.io/myapp:latest
Dockerfile:
FROM python:3.10-slim
WORKDIR /app
COPY requirements.txt .
RUN pip install -r requirements.txt
COPY . .
CMD ["uvicorn", "app.main:app", "--host", "0.0.0.0"]
7. Code coverage отчёты
Задача: Отследить процент протестированного кода.
- name: Run tests with coverage
run: pytest --cov=app --cov-report=xml --cov-report=html
- name: Upload to codecov.io
uses: codecov/codecov-action@v3
with:
files: ./coverage.xml
Требования в проекте:
# pyproject.toml или setup.cfg
[tool.pytest.ini_options]
addopts = "--cov=app --cov-report=term-missing --cov-fail-under=80"
# CI падает, если coverage < 80%
8. Code review автоматизация
Задача: Автоматически комментировать PR'ы.
name: Code Review Bot
on: [pull_request]
jobs:
review:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3
- name: AI Code Review (optional)
uses: anc95/ChatGPT-CodeReview@main
with:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
9. Performance тестирование
Задача: Убедиться, что изменения не сломали производительность.
# benchmarks/test_performance.py
import pytest
from app.algorithm import process_data
def test_process_data_performance(benchmark):
"""Тест производительности"""
data = list(range(1000))
result = benchmark(process_data, data)
assert len(result) > 0
# CI запустит с pytest-benchmark
# pytest benchmarks/ --benchmark-compare
10. Полный CI pipeline пример
name: CI Pipeline
on:
push:
branches: [main, develop]
pull_request:
branches: [main, develop]
jobs:
lint:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3
- uses: actions/setup-python@v4
- run: pip install ruff black mypy
- run: black --check .
- run: ruff check .
- run: mypy .
test:
runs-on: ubuntu-latest
needs: lint # Тесты только если lint passed
services:
postgres:
image: postgres:14
env:
POSTGRES_PASSWORD: password
steps:
- uses: actions/checkout@v3
- uses: actions/setup-python@v4
- run: pip install -e .
- run: pytest --cov=app --cov-fail-under=80
security:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3
- run: pip install safety bandit
- run: safety check
- run: bandit -r . -f json
build:
runs-on: ubuntu-latest
needs: [lint, test, security]
if: github.ref == 'refs/heads/main'
steps:
- uses: actions/checkout@v3
- name: Build and push
run: |
docker build -t myapp:latest .
docker push myregistry.azurecr.io/myapp:latest
11. CI vs CD
CI (Continuous Integration): ← Мы говорим про это
- Проверка кода при коммите
- Автоматические тесты
- Статический анализ
- Падает, если что-то не так
CD (Continuous Deployment): ← Это следующий шаг
- Автоматический деплой на production
- Запускается, если CI прошёл успешно
- Может быть с approval'ом
Заключение
CI отвечает за:
✅ Качество кода — линтинг, форматирование, типизация ✅ Функциональность — unit, интеграционные тесты ✅ Безопасность — сканирование уязвимостей, secret detection ✅ Надёжность — coverage отчёты, performance tests ✅ Сборка — docker image'ы, артефакты
Правило: Если CI не зелёный — код в main не попадает. Это защита качества для всей команды.