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.