# CI Workflow Guide

This document describes the CI/CD pipeline for DocuElevate, focusing on the **Run Tests & Linting** workflow.

## Overview

The CI workflow (`.github/workflows/tests.yaml`) runs automatically on every push and pull request. It is designed so that **each linter and the test suite run as independent jobs**, ensuring that a failure in one tool never blocks the results from another.

## Workflow Jobs

| Job | Tool | Purpose | Enforced |
|--------|--------|---------------------------------------------|----------|
| `lint` | Ruff | Fast Python linter (replaces Flake8, Black, isort, Bandit) | ✅ |
| `html-lint` | djLint | HTML template accessibility linter | ✅ |
| `dependency-scan` | pip-audit | Dependency vulnerability scanning against OSV/PyPA advisories | ✅ |
| `run-tests` | pytest | All unit + integration tests with coverage (excludes e2e) | ✅ |
| `mypy` | mypy | Static type checking | ✅ |

### Pipeline Flow

```
Stage 1 (parallel):  lint, html-lint, dependency-scan
                          │
Stage 2 (parallel):  run-tests + mypy
                          │
Stage 3:             build  (only after all above pass)
```

The pipeline follows a **fail-early** strategy: fast linters run first to catch regressions early, then the full test suite and type checks run in parallel.

> **Note:** DocuElevate uses Ruff, a modern all-in-one Python linter that consolidates the functionality of Flake8, Black, isort, and Bandit. This streamlined approach reduces CI complexity while maintaining code quality and security standards.

### All Tests (`run-tests`)

- **Timeout:** 300 seconds (per test via `pytest-timeout`)
- Runs all tests (unit, integration, and Docker-based) in a single step
- Excludes tests marked `e2e`
- Uses Redis and RabbitMQ service containers
- Docker daemon available for testcontainers (WebDAV, OAuth mock server, etc.)
- Collects coverage and uploads to Codecov
- Uploads `junit.xml` and `coverage.xml` as workflow artifacts

### Ruff Lint & Format

- **Linting**: Checks `app/` and `tests/` for code quality issues
  - Enforces PEP 8 style with `line-length=120`
  - Includes security checks (replaces Bandit)
  - Checks import order (replaces isort)
  - Catches common bugs (replaces Flake8 + Pylint patterns)
- **Formatting**: Verifies code is formatted consistently (replaces Black)
  - Runs in check mode (no files are modified)
  - Automatically fixable with `ruff format`

### Mypy

- Type checks `app/` with appropriate configuration from `pyproject.toml`.
- Requires full project dependencies (installs `requirements-dev.txt`).

## Artifacts

The following artifacts are uploaded after every run:

| Artifact | Contents | Condition |
|-------------------------------|--------------------------------------|----------------------|
| `test-results` | `junit.xml`, `coverage.xml` | Always (unless cancelled) |

## Running Linters Locally

You can run the same checks locally before pushing:

```bash
# Install dev dependencies
pip install -r requirements-dev.txt

# Run Ruff linting
ruff check app/ tests/

# Run Ruff formatting check
ruff format --check app/ tests/

# Run type checking
mypy app/

# Run all tests except e2e (same as CI)
pytest tests/ -v --timeout=300 --cov=app --cov-report=term -m "not e2e"

# Run only fast tests locally (skip Docker/external/slow)
pytest tests/ -v --timeout=120 -m "not e2e and not requires_docker and not requires_external and not slow"

# Run only Docker/external/slow tests locally (requires Docker)
pytest tests/ -v --timeout=300 -m "(requires_docker or requires_external or slow) and not e2e"
```

Or use pre-commit hooks to run checks automatically on each commit (recommended):

```bash
pip install pre-commit
pre-commit install
pre-commit run --all-files
```

Pre-commit hooks include Ruff linting/formatting, Mypy type checking, secret detection, and conventional commit validation.

## Additional Security Scanning

Beyond the linting and testing workflows, DocuElevate uses additional security tools:

### CodeQL (`.github/workflows/codeql.yml`)

- **Purpose**: Advanced security scanning for code vulnerabilities
- **Frequency**: Runs on push to main, pull requests, and weekly schedule
- **Languages**: Python, JavaScript, GitHub Actions
- **Coverage**: Detects security vulnerabilities, bugs, and code quality issues
- **Native GitHub integration**: Results appear in the Security tab

### Codecov

- **Purpose**: Test coverage tracking and visualization
- **Integration**: Automatically receives coverage reports from test workflow
- **Features**: Coverage trends, PR comments, coverage diffs

## Design Decisions

### Why Separate Jobs Instead of Steps?

Previously, all linters ran as sequential steps in a single job. This meant:

- A failure in one tool would prevent others from running.
- Contributors only saw feedback from the **first** tool that failed, not all of them.

By splitting into independent jobs:

- **All tools always run** regardless of other failures.
- Contributors see **all** feedback in a single CI run.
- Jobs run **in parallel**, reducing total wall-clock time.

### Why Ruff Instead of Multiple Tools?

DocuElevate uses Ruff as an all-in-one linting solution, replacing:
- **Flake8** (PEP 8 style checking)
- **Black** (code formatting)
- **isort** (import sorting)
- **Bandit** (security linting)
- **Pylint** (some code quality checks)

**Benefits:**
- 10-100x faster than traditional tools
- Single configuration in `pyproject.toml`
- Consistent behavior across all checks
- Auto-fix capability for most issues
- Active development and modern Python support

### Why Are All Checks Enforced?

All checks are set to fail the CI (no `continue-on-error`). This ensures:

- The codebase stays consistently formatted (Ruff format).
- Style and quality issues are caught early (Ruff check).
- Type errors surface before merge (Mypy).
- Security issues are flagged immediately (Ruff security rules + CodeQL).

## Tool Comparison & Rationale

This project previously used multiple overlapping tools. Here's why the current setup was chosen:

| Tool Category | Current Tool | Replaced Tools | Rationale |
|---------------|--------------|----------------|-----------|
| Linting & Formatting | **Ruff** | Flake8, Black, isort, Bandit, Pylint | Modern, fast, comprehensive, single tool |
| Type Checking | **Mypy** | - | Industry standard, unique value |
| Security Scanning | **CodeQL** | - | GitHub native, free, enterprise-grade |
| Coverage Tracking | **Codecov** | - | Excellent visualization and PR integration |

**Rejected/Removed:**
- **DeepSource**: Redundant with Ruff + CodeQL (removed in this update)
- **SonarQube**: Not configured, enterprise-focused, redundant with current tools
- **Snyk**: Not configured, CodeQL provides adequate security scanning

## Copilot Code Compliance

All code — whether written by hand or suggested by GitHub Copilot — goes through the same CI pipeline. Copilot-generated code is linted, type-checked, and security-scanned identically to human-written code. Contributors using Copilot should ensure suggestions pass all checks before committing.
