Stasis

Python Django

Django application with tests, coverage, and migrations

Python Django Application

CI/CD pipeline for Django applications with testing, coverage, and migrations.

Overview

This pipeline handles Django projects with:

  • Dependency installation
  • Code linting (flake8, black, isort)
  • Unit tests with pytest
  • Coverage reporting
  • Database migrations check
  • Static file collection
  • Security checks

Use Case

  • Django web applications
  • Django REST Framework APIs
  • Django with Celery
  • Django with Channels

Prerequisites

  • Python 3.8+ project
  • requirements.txt or pyproject.toml
  • pytest configured

Configuration

# runner.yaml
image:
  name: "python"
  tag: "3.11-slim"
  pull_policy: "IfNotPresent"

on:
  push:
    - "main"
    - "develop"
  pull_request:
    - "main"

global_env:
  DJANGO_SETTINGS_MODULE: "myproject.settings.test"
  PYTHONUNBUFFERED: "1"

timeout: 1200  # 20 minutes

steps:
  setup:
    type: "pre"
    scripts:
      - python --version
      - pip --version
      - pip install --upgrade pip
      - pip install -r requirements.txt
      - pip install -r requirements-dev.txt

  lint:
    type: "exec"
    scripts:
      - |
        echo "Running flake8..."
        flake8 . --count --select=E9,F63,F7,F82 --show-source --statistics
        flake8 . --count --exit-zero --max-complexity=10 --max-line-length=127 --statistics
      - |
        echo "Running black check..."
        black --check --diff .
      - |
        echo "Running isort check..."
        isort --check-only --diff .
    continue_on_error: false

  test:
    type: "exec"
    scripts:
      - |
        echo "Running tests with coverage..."
        pytest \
          --cov=myproject \
          --cov-report=html:coverage/html \
          --cov-report=xml:coverage/coverage.xml \
          --cov-report=term-missing \
          --junitxml=coverage/junit.xml \
          -v \
          --tb=short
    envs:
      DJANGO_SETTINGS_MODULE: "myproject.settings.test"
    continue_on_error: false
    timeout: 600

  migrations_check:
    type: "exec"
    scripts:
      - |
        echo "Checking for missing migrations..."
        python manage.py makemigrations --check --dry-run
      - |
        echo "Running migrations..."
        python manage.py migrate --noinput
    continue_on_error: false

  security_check:
    type: "exec"
    scripts:
      - |
        echo "Running security checks..."
        python manage.py check --deploy --fail-level WARNING
    continue_on_error: true

Step-by-Step Explanation

Step 1: Setup

setup:
  type: "pre"
  scripts:
    - pip install --upgrade pip
    - pip install -r requirements.txt
    - pip install -r requirements-dev.txt

What it does:

  • Upgrades pip
  • Installs production dependencies
  • Installs development dependencies (pytest, flake8, etc.)

Step 2: Lint

lint:
  type: "exec"
  scripts:
    - flake8 . --count --select=E9,F63,F7,F82 --show-source --statistics
    - black --check --diff .
    - isort --check-only --diff .

What it does:

  • Runs flake8 for critical errors
  • Checks code formatting with black
  • Checks import sorting with isort

Step 3: Test

test:
  type: "exec"
  scripts:
    - pytest --cov=myproject --cov-report=html:coverage/html ...

What it does:

  • Runs all tests with coverage
  • Generates HTML and XML coverage reports
  • Generates JUnit XML for CI integration

Step 4: Migrations Check

migrations_check:
  type: "exec"
  scripts:
    - python manage.py makemigrations --check --dry-run
    - python manage.py migrate --noinput

What it does:

  • Checks if all model changes have migrations
  • Runs migrations on test database
  • Fails if migrations are missing

Environment Variables

VariableRequiredDescription
DJANGO_SETTINGS_MODULEYesDjango settings module
DATABASE_URLNoDatabase connection URL
SECRET_KEYNoDjango secret key

Artifacts

FileDescription
coverage/html/HTML coverage report
coverage/coverage.xmlXML coverage report
coverage/junit.xmlJUnit test results
staticfiles/Collected static files

Customization

Use PostgreSQL for tests

services:
  postgres:
    image: postgres:17-alpine
    environment:
      POSTGRES_DB: test_db
      POSTGRES_PASSWORD: test_pass

steps:
  test:
    type: "exec"
    scripts:
      - pytest
    envs:
      DATABASE_URL: "postgresql://postgres:test_pass@postgres:5432/test_db"

Add Django Debug Toolbar check

debug_toolbar:
  type: "exec"
  scripts:
    - |
      echo "Checking debug toolbar is disabled..."
      python -c "from django.conf import settings; assert not settings.DEBUG"
  continue_on_error: false

Next Steps

On this page