Files
gh-christianlouis-docuelevate/docs/CIWorkflow.md
T
copilot-swe-agent[bot] d025923ef5 ci: split linters into independent parallel jobs and add CI documentation
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>
2026-02-12 03:07:53 +00:00

4.3 KiB

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:

# 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:

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.