Skip to content

refactor: codebase optimization - module decomposition, test dedup, t… #89

refactor: codebase optimization - module decomposition, test dedup, t…

refactor: codebase optimization - module decomposition, test dedup, t… #89

Workflow file for this run

# =============================================================================
# CI/CD Pipeline
# =============================================================================
# Constitution: Reliability & Quality
# Runs on every push and PR: lint → type check → unit → integration → E2E → coverage
#
# Quality Gates:
# - All tests must pass
# - Coverage must not regress
# - Type checking must pass
# - Linting must pass
name: CI
on:
push:
branches: [main, develop]
pull_request:
branches: [main, develop]
env:
PYTHON_VERSION: "3.11"
UV_CACHE_DIR: /tmp/.uv-cache
jobs:
# ===========================================================================
# Lint & Type Check (Fast Feedback)
# ===========================================================================
lint:
name: Lint & Type Check
runs-on: ubuntu-latest
steps:
- name: Checkout code
uses: actions/checkout@v4
- name: Set up Python
uses: actions/setup-python@v5
with:
python-version: ${{ env.PYTHON_VERSION }}
- name: Install uv
uses: astral-sh/setup-uv@v4
with:
enable-cache: true
cache-dependency-glob: "uv.lock"
- name: Restore uv cache
uses: actions/cache@v4
with:
path: /tmp/.uv-cache
key: uv-${{ runner.os }}-${{ hashFiles('uv.lock') }}
restore-keys: |
uv-${{ runner.os }}-
- name: Install dependencies
run: uv sync --frozen --dev
- name: Run ruff (lint)
run: uv run ruff check src/ tests/
- name: Run ruff (format check)
run: uv run ruff format --check src/ tests/
- name: Run mypy
run: uv run mypy src/
- name: Minimize uv cache
run: uv cache prune --ci
# ===========================================================================
# Unit Tests (Fast, Isolated)
# ===========================================================================
unit-tests:
name: Unit Tests
runs-on: ubuntu-latest
needs: lint
steps:
- name: Checkout code
uses: actions/checkout@v4
- name: Set up Python
uses: actions/setup-python@v5
with:
python-version: ${{ env.PYTHON_VERSION }}
- name: Install uv
uses: astral-sh/setup-uv@v4
with:
enable-cache: true
cache-dependency-glob: "uv.lock"
- name: Install dependencies
run: uv sync --frozen --dev
- name: Run unit tests
run: |
uv run pytest tests/unit \
-v \
--tb=short \
-m "unit or not (integration or e2e)" \
--cov=src \
--cov-report=xml:coverage-unit.xml \
--cov-report=term-missing
mv .coverage .coverage.unit
- name: Upload coverage
uses: actions/upload-artifact@v4
with:
name: coverage-unit
include-hidden-files: true
path: |
coverage-unit.xml
.coverage.unit
# ===========================================================================
# Integration Tests (Require Services)
# ===========================================================================
integration-tests:
name: Integration Tests
runs-on: ubuntu-latest
needs: unit-tests
services:
postgres:
image: postgres:16-alpine
env:
POSTGRES_USER: test
POSTGRES_PASSWORD: test
POSTGRES_DB: aether_test
ports:
- 5432:5432
options: >-
--health-cmd pg_isready
--health-interval 10s
--health-timeout 5s
--health-retries 5
steps:
- name: Checkout code
uses: actions/checkout@v4
- name: Set up Python
uses: actions/setup-python@v5
with:
python-version: ${{ env.PYTHON_VERSION }}
- name: Install uv
uses: astral-sh/setup-uv@v4
with:
enable-cache: true
cache-dependency-glob: "uv.lock"
- name: Install dependencies
run: uv sync --frozen --dev
- name: Run integration tests
env:
DATABASE_URL: postgresql+asyncpg://test:test@localhost:5432/aether_test
OPENAI_API_KEY: test-key
HA_URL: http://localhost:8123
HA_TOKEN: test-token
run: |
uv run pytest tests/integration \
-v \
--tb=short \
-m "integration" \
--cov=src \
--cov-report=xml:coverage-integration.xml \
--cov-fail-under=0
mv .coverage .coverage.integration
- name: Upload coverage
uses: actions/upload-artifact@v4
with:
name: coverage-integration
include-hidden-files: true
path: |
coverage-integration.xml
.coverage.integration
# ===========================================================================
# E2E Tests (Full System)
# ===========================================================================
e2e-tests:
name: E2E Tests
runs-on: ubuntu-latest
needs: integration-tests
steps:
- name: Checkout code
uses: actions/checkout@v4
- name: Set up Python
uses: actions/setup-python@v5
with:
python-version: ${{ env.PYTHON_VERSION }}
- name: Install uv
uses: astral-sh/setup-uv@v4
with:
enable-cache: true
cache-dependency-glob: "uv.lock"
- name: Install dependencies
run: uv sync --frozen --dev
- name: Start test services
run: |
docker compose -f infrastructure/test/docker-compose.test.yaml up -d
# Wait for services
sleep 10
- name: Run E2E tests
env:
DATABASE_URL: postgresql+asyncpg://test:test@localhost:5433/aether_test
OPENAI_API_KEY: test-key
HA_URL: http://localhost:8124
HA_TOKEN: test-token
MLFLOW_TRACKING_URI: http://localhost:5001
run: |
uv run pytest tests/e2e \
-v \
--tb=short \
-m "e2e" \
--cov=src \
--cov-report=xml:coverage-e2e.xml \
--cov-fail-under=0
mv .coverage .coverage.e2e || true
- name: Stop test services
if: always()
run: docker compose -f infrastructure/test/docker-compose.test.yaml down -v
- name: Upload coverage
if: always()
uses: actions/upload-artifact@v4
with:
name: coverage-e2e
include-hidden-files: true
path: |
coverage-e2e.xml
.coverage.e2e
# ===========================================================================
# Coverage Report
# ===========================================================================
coverage:
name: Coverage Report
runs-on: ubuntu-latest
needs: [unit-tests, integration-tests, e2e-tests]
steps:
- name: Checkout code
uses: actions/checkout@v4
- name: Download coverage artifacts
uses: actions/download-artifact@v4
with:
pattern: coverage-*
merge-multiple: true
- name: Upload to Codecov
uses: codecov/codecov-action@v4
with:
files: coverage-unit.xml,coverage-integration.xml,coverage-e2e.xml
fail_ci_if_error: false
verbose: true
- name: Check coverage threshold
run: |
# Combine binary .coverage files from each test step and enforce threshold
pip install coverage
coverage combine .coverage.unit .coverage.integration .coverage.e2e || coverage combine .coverage.unit .coverage.integration || coverage combine .coverage.unit
coverage report --fail-under=80
# ===========================================================================
# Security Scan
# ===========================================================================
security:
name: Security Scan
runs-on: ubuntu-latest
needs: lint
steps:
- name: Checkout code
uses: actions/checkout@v4
- name: Set up Python
uses: actions/setup-python@v5
with:
python-version: ${{ env.PYTHON_VERSION }}
- name: Install uv
uses: astral-sh/setup-uv@v4
with:
enable-cache: true
cache-dependency-glob: "uv.lock"
- name: Install dependencies
run: uv sync --frozen --dev
- name: Run bandit security scan
run: uv run bandit -r src/ -c pyproject.toml
- name: Check for vulnerabilities in dependencies
run: |
pip install pip-audit
pip-audit --requirement <(uv pip compile pyproject.toml) || true