d025923ef5
Refactors .github/workflows/tests.yaml so that flake8, black, mypy, pylint, and bandit each run as their own job in parallel with the test job. This ensures a failure in one tool never blocks the others, and contributors see full feedback from every tool on every CI run. - Upgrades actions/checkout to v4 and actions/setup-python to v5 - All linter jobs are enforced (no continue-on-error) - Test artifacts (junit.xml, coverage.xml) always uploaded - Bandit JSON report always uploaded as artifact - Adds docs/CIWorkflow.md with maintainer documentation - Updates CONTRIBUTING.md with CI workflow table and link Co-authored-by: christianlouis <361235+christianlouis@users.noreply.github.com>
121 lines
4.3 KiB
Markdown
121 lines
4.3 KiB
Markdown
# 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 |
|
|
|--------|--------|---------------------------------------------|----------|
|
|
| `test` | pytest | Unit/integration tests with coverage | ✅ |
|
|
| `flake8`| flake8 | PEP 8 style linting | ✅ |
|
|
| `black` | black | Code formatting check | ✅ |
|
|
| `mypy` | mypy | Static type checking | ✅ |
|
|
| `pylint`| pylint | Code quality analysis | ✅ |
|
|
| `bandit`| bandit | Security vulnerability scanning | ✅ |
|
|
|
|
All six jobs start **in parallel** as soon as the workflow is triggered. No job depends on or waits for any other job.
|
|
|
|
### Tests
|
|
|
|
- Runs pytest with coverage reporting (XML + terminal).
|
|
- Excludes E2E tests that require Docker-in-Docker (`-m "not e2e"`).
|
|
- Uses Redis and RabbitMQ service containers.
|
|
- Uploads coverage and JUnit XML results to Codecov.
|
|
- Uploads `junit.xml` and `coverage.xml` as workflow artifacts (always, even on failure).
|
|
|
|
### Flake8
|
|
|
|
- Checks `app/` against PEP 8 with `max-line-length=120`.
|
|
- Ignores `E203` (whitespace before `:`) and `W503` (line break before binary operator), matching the Black formatter.
|
|
|
|
### Black
|
|
|
|
- Verifies that all files in `app/` are formatted with `black --line-length=120`.
|
|
- Runs in `--check` mode (no files are modified).
|
|
|
|
### Mypy
|
|
|
|
- Type checks `app/` with `--ignore-missing-imports`.
|
|
- Requires full project dependencies (installs `requirements-dev.txt`).
|
|
|
|
### Pylint
|
|
|
|
- Analyzes `app/` with `max-line-length=120`.
|
|
- Disables `C0111` (missing docstrings), `C0103` (naming conventions), and `R0903` (too few public methods).
|
|
- Requires full project dependencies (installs `requirements-dev.txt`).
|
|
|
|
### Bandit
|
|
|
|
- Produces a full JSON report (`bandit-report.json`) uploaded as a workflow artifact.
|
|
- **Fails the job** if any high- or medium-severity issues are found (`-ll` flag).
|
|
- The JSON report is always uploaded, even if the severity check fails.
|
|
|
|
## Artifacts
|
|
|
|
The following artifacts are uploaded after every run:
|
|
|
|
| Artifact | Contents | Condition |
|
|
|------------------|--------------------------------------|----------------------|
|
|
| `test-results` | `junit.xml`, `coverage.xml` | Always (unless cancelled) |
|
|
| `bandit-report` | `bandit-report.json` | 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 each linter
|
|
flake8 app/ --max-line-length=120 --extend-ignore=E203,W503
|
|
black --check app/ --line-length=120
|
|
mypy app/ --ignore-missing-imports
|
|
pylint app/ --max-line-length=120 --disable=C0111,C0103,R0903
|
|
bandit -r app/ -ll
|
|
|
|
# Run tests
|
|
pytest tests/ -v --cov=app --cov-report=term -m "not e2e"
|
|
```
|
|
|
|
Or use pre-commit hooks to run checks automatically on each commit:
|
|
|
|
```bash
|
|
pip install pre-commit
|
|
pre-commit install
|
|
pre-commit run --all-files
|
|
```
|
|
|
|
## Design Decisions
|
|
|
|
### Why Separate Jobs Instead of Steps?
|
|
|
|
Previously, all linters ran as sequential steps in a single job. This meant:
|
|
|
|
- A failure in flake8 would prevent black, mypy, pylint, and bandit 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 Are All Linters Enforced?
|
|
|
|
All linters are set to fail the CI (no `continue-on-error`). This ensures:
|
|
|
|
- The codebase stays consistently formatted (Black).
|
|
- Style issues are caught early (Flake8).
|
|
- Type errors surface before merge (Mypy).
|
|
- Code quality standards are maintained (Pylint).
|
|
- Security issues are flagged immediately (Bandit).
|
|
|
|
## 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.
|