name: Wagtail CI on: push: paths-ignore: - 'docs/**' pull_request: paths-ignore: - 'docs/**' concurrency: group: ${{ github.workflow }}-${{ github.ref }} cancel-in-progress: true # Our test suite should cover: # - all supported databases against current Python and Django (including psycopg v2 and v3) # - at least one test run for each older supported version of Python and Django # - at least one test run for each supported Elasticsearch version # - a test run against Django's git main and active stable branch (allowing failures) # - test runs with USE_EMAIL_USER_MODEL=yes, DISABLE_TIMEZONE=yes and WAGTAIL_CHECK_TEMPLATE_NUMBER_FORMAT=1 # Current configuration: # - django 4.2, python 3.9, postgres:12, psycopg 2, parallel # - django 4.2, python 3.10, mysql:8.0 # - django 4.2, python 3.11, mariadb:10.5 # - django 5.0, python 3.11, sqlite, WAGTAIL_CHECK_TEMPLATE_NUMBER_FORMAT=1 # - django 5.1, python 3.12, mysql:8.4, parallel, USE_EMAIL_USER_MODEL=yes # - django 5.1, python 3.12, mariadb:11.4, USE_EMAIL_USER_MODEL=yes # - django 5.1, python 3.13, sqlite, parallel, USE_EMAIL_USER_MODEL=yes # - django 5.1, python 3.13, postgres:15, psycopg 3, parallel, DISABLE_TIMEZONE=yes # - django stable/5.1.x, python 3.11, postgres:15, psycopg 3 (allow failures) # - django main, python 3.13, postgres:latest, psycopg 3, parallel (allow failures) # - elasticsearch 7, django 4.2, python 3.9, postgres:latest, psycopg 2 # - opensearch 2, django 5.0, python 3.10, sqlite # - elasticsearch 8, django 5.1, python 3.13, sqlite, USE_EMAIL_USER_MODEL=yes # Some tests are run in parallel by passing --parallel to runtests.py. # When running tests in parallel, some errors cannot be pickled and result in # non-helpful tracebacks (see https://code.djangoproject.com/ticket/29023). # Thus, we keep one test run without --parallel for each supported database. # ElasticSearch tests are not run in parallel as the test suite is not thread-safe. permissions: contents: read # to fetch code (actions/checkout) jobs: test-sqlite: runs-on: ubuntu-latest strategy: matrix: include: - python: '3.11' django: 'Django>=5.0,<5.1' check_template_number_format: '1' - python: '3.13' django: 'Django>=5.1,<5.2' emailuser: emailuser parallel: '--parallel' steps: - uses: actions/checkout@v4 - name: Set up Python ${{ matrix.python }} uses: actions/setup-python@v5 with: python-version: ${{ matrix.python }} cache: 'pip' - name: Install dependencies run: | python -m pip install --upgrade pip pip install -e '.[testing]' --config-settings editable_mode=strict pip install "${{ matrix.django }}" - name: Test run: | WAGTAIL_FAIL_ON_VERSIONED_STATIC=1 DJANGO_SETTINGS_MODULE=wagtail.test.settings django-admin check coverage run --parallel-mode --source wagtail runtests.py ${{ matrix.parallel }} env: DATABASE_ENGINE: django.db.backends.sqlite3 USE_EMAIL_USER_MODEL: ${{ matrix.emailuser }} WAGTAIL_CHECK_TEMPLATE_NUMBER_FORMAT: ${{ matrix.check_template_number_format }} - name: Upload coverage data uses: actions/upload-artifact@v4 with: name: coverage-data-${{ github.job }}-${{ strategy.job-index }} path: .coverage.* include-hidden-files: true test-postgres: runs-on: ubuntu-latest continue-on-error: ${{ matrix.experimental }} strategy: matrix: include: - python: '3.9' django: 'Django>=4.2,<4.3' psycopg: 'psycopg2>=2.6' experimental: false parallel: '--parallel' - python: '3.13' django: 'Django>=5.0,<5.1' psycopg: 'psycopg>=3.1.8' postgres: 'postgres:15' notz: notz experimental: false parallel: '--parallel' - python: '3.11' django: 'git+https://github.com/django/django.git@stable/5.1.x#egg=Django' psycopg: 'psycopg>=3.1.8' postgres: 'postgres:15' experimental: true - python: '3.13' django: 'git+https://github.com/django/django.git@main#egg=Django' psycopg: 'psycopg>=3.1.8' experimental: true postgres: 'postgres:latest' parallel: '--parallel' services: postgres: image: ${{ matrix.postgres || 'postgres:12' }} env: POSTGRES_PASSWORD: postgres ports: - 5432:5432 options: --health-cmd pg_isready --health-interval 10s --health-timeout 5s --health-retries 5 steps: - uses: actions/checkout@v4 - name: Set up Python ${{ matrix.python }} uses: actions/setup-python@v5 with: python-version: ${{ matrix.python }} cache: 'pip' - name: Install dependencies run: | python -m pip install --upgrade pip pip install "${{ matrix.psycopg }}" pip install -e '.[testing]' --config-settings editable_mode=strict pip install "${{ matrix.django }}" ${{ matrix.install_extras }} - name: Test run: | WAGTAIL_FAIL_ON_VERSIONED_STATIC=1 DJANGO_SETTINGS_MODULE=wagtail.test.settings django-admin check coverage run --parallel-mode --source wagtail runtests.py ${{ matrix.parallel }} env: DATABASE_ENGINE: django.db.backends.postgresql DATABASE_HOST: localhost DATABASE_USER: postgres DATABASE_PASSWORD: postgres USE_EMAIL_USER_MODEL: ${{ matrix.emailuser }} DISABLE_TIMEZONE: ${{ matrix.notz }} - name: Upload coverage data uses: actions/upload-artifact@v4 with: name: coverage-data-${{ github.job }}-${{ strategy.job-index }} path: .coverage.* include-hidden-files: true test-mysql: runs-on: ubuntu-latest continue-on-error: ${{ matrix.experimental }} strategy: matrix: include: - python: '3.10' django: 'Django>=4.2,<4.3' experimental: false - python: '3.11' django: 'Django>=4.2,<4.3' experimental: false mysql: 'mariadb:10.5' - python: '3.12' django: 'Django>=5.1,<5.2' experimental: false mysql: 'mariadb:11.4' emailuser: emailuser - python: '3.12' django: 'Django>=5.1,<5.2' experimental: false parallel: '--parallel' mysql: 'mysql:8.4' emailuser: emailuser services: mysql: image: ${{ matrix.mysql || 'mysql:8.0' }} env: MARIADB_ROOT_PASSWORD: root MYSQL_ROOT_PASSWORD: root MYSQL_ALLOW_EMPTY_PASSWORD: yes MYSQL_DATABASE: wagtail HEALTH_CMD: ${{ startsWith(matrix.mysql, 'mariadb') && 'healthcheck.sh --connect --innodb_initialized' || 'mysqladmin --protocol=tcp ping' }} ports: - 3306:3306 options: --health-cmd=$HEALTH_CMD --health-interval=10s --health-timeout=5s --health-retries=3 --cap-add=sys_nice steps: - uses: actions/checkout@v4 - name: Set up Python ${{ matrix.python }} uses: actions/setup-python@v5 with: python-version: ${{ matrix.python }} cache: 'pip' - name: Install dependencies run: | python -m pip install --upgrade pip pip install "mysqlclient>=1.4,<2" pip install -e '.[testing]' --config-settings editable_mode=strict pip install "${{ matrix.django }}" - name: Test run: | WAGTAIL_FAIL_ON_VERSIONED_STATIC=1 DJANGO_SETTINGS_MODULE=wagtail.test.settings django-admin check coverage run --parallel-mode --source wagtail runtests.py ${{ matrix.parallel }} env: DATABASE_ENGINE: django.db.backends.mysql DATABASE_HOST: '127.0.0.1' DATABASE_USER: root DATABASE_PASSWORD: root USE_EMAIL_USER_MODEL: ${{ matrix.emailuser }} DISABLE_TIMEZONE: ${{ matrix.notz }} - name: Upload coverage data uses: actions/upload-artifact@v4 with: name: coverage-data-${{ github.job }}-${{ strategy.job-index }} path: .coverage.* include-hidden-files: true test-sqlite-elasticsearch8: runs-on: ubuntu-latest strategy: matrix: include: - python: '3.13' django: 'Django>=5.1,<5.2' emailuser: emailuser steps: - name: Configure sysctl limits run: | sudo swapoff -a sudo sysctl -w vm.swappiness=1 sudo sysctl -w fs.file-max=262144 sudo sysctl -w vm.max_map_count=262144 - uses: getong/elasticsearch-action@v1.3 with: elasticsearch version: 8.8.0 host port: 9200 container port: 9200 host node port: 9300 node port: 9300 discovery type: 'single-node' - uses: actions/checkout@v4 - name: Set up Python ${{ matrix.python }} uses: actions/setup-python@v5 with: python-version: ${{ matrix.python }} cache: 'pip' - name: Install dependencies run: | python -m pip install --upgrade pip pip install -e '.[testing]' --config-settings editable_mode=strict pip install "${{ matrix.django }}" pip install "elasticsearch>=8,<9" pip install certifi - name: Test run: | coverage run --parallel-mode --source wagtail runtests.py wagtail.search wagtail.documents wagtail.images --elasticsearch8 env: DATABASE_ENGINE: django.db.backends.sqlite3 USE_EMAIL_USER_MODEL: ${{ matrix.emailuser }} - name: Upload coverage data uses: actions/upload-artifact@v4 with: name: coverage-data-${{ github.job }}-${{ strategy.job-index }} path: .coverage.* include-hidden-files: true test-postgres-elasticsearch7: runs-on: ubuntu-latest continue-on-error: ${{ matrix.experimental }} strategy: matrix: include: - python: '3.9' django: 'Django>=4.2,<4.3' experimental: false services: postgres: image: postgres:latest env: POSTGRES_PASSWORD: postgres ports: - 5432:5432 options: --health-cmd pg_isready --health-interval 10s --health-timeout 5s --health-retries 5 steps: - name: Configure sysctl limits run: | sudo swapoff -a sudo sysctl -w vm.swappiness=1 sudo sysctl -w fs.file-max=262144 sudo sysctl -w vm.max_map_count=262144 - uses: elastic/elastic-github-actions/elasticsearch@master with: stack-version: 7.6.1 - uses: actions/checkout@v4 - name: Set up Python ${{ matrix.python }} uses: actions/setup-python@v5 with: python-version: ${{ matrix.python }} cache: 'pip' - name: Install dependencies run: | python -m pip install --upgrade pip pip install "psycopg2>=2.6" pip install -e '.[testing]' --config-settings editable_mode=strict pip install "${{ matrix.django }}" pip install "elasticsearch>=7,<8" pip install certifi - name: Test run: | coverage run --parallel-mode --source wagtail runtests.py wagtail.search wagtail.documents wagtail.images --elasticsearch7 env: DATABASE_ENGINE: django.db.backends.postgresql DATABASE_HOST: localhost DATABASE_USER: postgres DATABASE_PASSWORD: postgres USE_EMAIL_USER_MODEL: ${{ matrix.emailuser }} - name: Upload coverage data uses: actions/upload-artifact@v4 with: name: coverage-data-${{ github.job }}-${{ strategy.job-index }} path: .coverage.* include-hidden-files: true test-sqlite-opensearch2: runs-on: ubuntu-latest continue-on-error: ${{ matrix.experimental }} strategy: matrix: include: - python: '3.10' django: 'Django>=5.0,<5.1' experimental: false steps: - name: Configure sysctl limits run: | sudo swapoff -a sudo sysctl -w vm.swappiness=1 sudo sysctl -w fs.file-max=262144 sudo sysctl -w vm.max_map_count=262144 - uses: ankane/setup-opensearch@v1 with: opensearch-version: 2 - uses: actions/checkout@v4 - name: Set up Python ${{ matrix.python }} uses: actions/setup-python@v5 with: python-version: ${{ matrix.python }} cache: 'pip' - name: Install dependencies run: | python -m pip install --upgrade pip pip install -e '.[testing]' --config-settings editable_mode=strict pip install "${{ matrix.django }}" pip install "elasticsearch==7.13.4" pip install certifi - name: Test run: | coverage run --parallel-mode --source wagtail runtests.py wagtail.search wagtail.documents wagtail.images --elasticsearch7 env: DATABASE_ENGINE: django.db.backends.sqlite3 USE_EMAIL_USER_MODEL: ${{ matrix.emailuser }} - name: Upload coverage data uses: actions/upload-artifact@v4 with: name: coverage-data-${{ github.job }}-${{ strategy.job-index }} path: .coverage.* include-hidden-files: true coverage: needs: - test-sqlite - test-postgres - test-mysql - test-sqlite-elasticsearch8 - test-postgres-elasticsearch7 - test-sqlite-opensearch2 runs-on: ubuntu-latest steps: - name: Check out the repo uses: actions/checkout@v4 - name: Set up Python uses: actions/setup-python@v5 with: python-version: '3.10' - name: Install dependencies run: | python -m pip install --upgrade pip pip install coverage - name: Download coverage data uses: actions/download-artifact@v4 with: pattern: coverage-data-* merge-multiple: true - name: Combine coverage data run: | coverage combine - name: Generate coverage report run: | coverage report -m --skip-covered --skip-empty | sed 's/^/ /' >> $GITHUB_STEP_SUMMARY coverage html --skip-covered --skip-empty - name: Upload HTML report as artifact uses: actions/upload-artifact@v4 with: name: coverage-report path: coverage_html_report include-hidden-files: true - name: Upload coverage to Codecov uses: codecov/codecov-action@v3 with: flags: backend token: ${{ secrets.CODECOV_TOKEN }}