From 8b0425f7845876ee9d0ea9b1b096cadc6b55d7f8 Mon Sep 17 00:00:00 2001 From: medmunds Date: Fri, 7 Jan 2022 11:21:26 -0800 Subject: [PATCH] CI: Refactor workflows, update test matrix * Split test, integration-test, and release workflows, simplifying config and logic for each. * Use environment to separate secrets for release workflow. * Update test matrix with newer Django and Python versions. --- .github/workflows/ci-cd.yml | 215 ------------------------- .github/workflows/integration-test.yml | 88 ++++++++++ .github/workflows/release.yml | 77 +++++++++ .github/workflows/test.yml | 107 ++++++++++++ README.rst | 10 +- tests/test_settings/settings_4_0.py | 33 ++-- tests/test_settings/settings_4_1.py | 124 ++++++++++++++ tox.ini | 15 +- 8 files changed, 433 insertions(+), 236 deletions(-) delete mode 100644 .github/workflows/ci-cd.yml create mode 100644 .github/workflows/integration-test.yml create mode 100644 .github/workflows/release.yml create mode 100644 .github/workflows/test.yml create mode 100644 tests/test_settings/settings_4_1.py diff --git a/.github/workflows/ci-cd.yml b/.github/workflows/ci-cd.yml deleted file mode 100644 index 24d0fce..0000000 --- a/.github/workflows/ci-cd.yml +++ /dev/null @@ -1,215 +0,0 @@ -name: ci-cd - -on: - push: - pull_request: - workflow_dispatch: - schedule: - # Weekly test (on branch main) every Thursday at 12:00 UTC. - # (Used to monitor compatibility with ESP APIs and other dependencies.) - - cron: '0 12 * * 4' - - -jobs: - skip_duplicate_runs: - # Avoid running the live integration tests twice on the same code - # (to conserve limited sending quotas in the live ESP test accounts) - runs-on: ubuntu-20.04 - outputs: - should_skip: ${{ steps.skip_check.outputs.should_skip }} - steps: - - id: skip_check - # uses: fkirc/skip-duplicate-actions@v3.4.0 - uses: fkirc/skip-duplicate-actions@4c656bbdb6906310fa6213604828008bc28fe55d - with: - concurrent_skipping: "same_content" - skip_after_successful_duplicate: "true" - cancel_others: "true" - do_not_skip: '["pull_request", "workflow_dispatch", "schedule"]' - - test: - name: ${{ matrix.config.tox }} ${{ matrix.config.options }} - runs-on: ubuntu-20.04 - needs: skip_duplicate_runs - timeout-minutes: 15 - strategy: - fail-fast: false - matrix: - config: - - { tox: "lint", python: "3.8" } - - { tox: "docs", python: "3.8" } - - # Anymail supports the same Python versions as Django, plus PyPy. - # https://docs.djangoproject.com/en/dev/faq/install/#what-python-version-can-i-use-with-django - - # Live API integration tests are only run on a few, representative Python/Django version - # combinations, to avoid rapidly consuming the testing accounts' entire send allotments. - - # Django 2.0: Python 3.5+ - - { tox: django20-py35-all, python: "3.5" } - - { tox: django20-py36-all, python: "3.6" } - - { tox: django20-pypy3-all, python: "pypy3" } - # Django 2.1: Python 3.5, 3.6, or 3.7 - - { tox: django21-py35-all, python: "3.5" } - - { tox: django21-py36-all, python: "3.6" } - - { tox: django21-py37-all, python: "3.7" } - - { tox: django21-pypy3-all, python: pypy3 } - # Django 2.2: Python 3.5, 3.6, 3.7, 3.8 (added in 2.2.8), 3.9 (added in 2.2.17) - - { tox: django22-py35-all, python: "3.5" } - - { tox: django22-py36-all, python: "3.6" } - - { tox: django22-py37-all, python: "3.7" } - - { tox: django22-py38-all, python: "3.8" } - - { tox: django22-py39-all, python: "3.9" } - - { tox: django22-pypy3-all, python: "pypy3" } - # Django 3.0: Python 3.6, 3.7, 3.8, 3.9 (added in 3.0.11) - - { tox: django30-py36-all, python: "3.6" } - - { tox: django30-py37-all, python: "3.7" } - - { tox: django30-py38-all, python: "3.8" } - - { tox: django30-py39-all, python: "3.9" } - - { tox: django30-pypy3-all, python: "pypy3" } - # Django 3.1: Python 3.6, 3.7, 3.8, 3.9 (added in 3.1.3) - - { tox: django31-py36-all, python: "3.6" } - - { tox: django31-py37-all, python: "3.7" } - - { tox: django31-py38-all, python: "3.8", options: run-live-tests } - - { tox: django31-py39-all, python: "3.9" } - - { tox: django31-pypy3-all, python: "pypy3" } - # Django 3.2: Python 3.6, 3.7, 3.8, 3.9 - - { tox: django32-py36-all, python: "3.6" } - - { tox: django32-py37-all, python: "3.7" } - - { tox: django32-py38-all, python: "3.8" } - - { tox: django32-py39-all, python: "3.9" } - - { tox: django32-pypy3-all, python: "pypy3" } - # Django 4.0: Python 3.8, 3.9, 3.10 - # Django current development (direct from GitHub source) - - { tox: djangoDev-py38-all, python: "3.8", options: allow-failures } - - { tox: djangoDev-py39-all, python: "3.9", options: allow-failures } - - { tox: djangoDev-py310-all, python: "3.10.0-alpha - 3.10", options: allow-failures } - # Install without optional extras (don't need to cover entire matrix) - - { tox: django31-py37-none, python: "3.7" } - - { tox: django31-py37-amazon_ses, python: "3.7" } - - { tox: django31-py37-postal, python: "3.7" } - # Test some specific older package versions - - { tox: django22-py37-all-old_urllib3, python: "3.7" } - - steps: - - name: Get code - uses: actions/checkout@v2 - - name: Setup Python ${{ matrix.config.python }} - # Ensure matrix Python version is installed and available for tox - uses: actions/setup-python@v2 - with: - python-version: ${{ matrix.config.python }} - - name: Setup default Python - # Change default Python version back to something consistent - # for installing/running tox - uses: actions/setup-python@v2 - with: - python-version: 3.9 - - name: Install tox - run: | - set -x - python --version - pip install tox - tox --version - - name: Test ${{ matrix.config.tox }} - run: | - tox -e ${{ matrix.config.tox }} - continue-on-error: ${{ contains( matrix.config.options, 'allow-failures' ) }} - env: - CONTINUOUS_INTEGRATION: true - TOX_FORCE_IGNORE_OUTCOME: false - ANYMAIL_RUN_LIVE_TESTS: >- - ${{ - contains( matrix.config.options, 'run-live-tests' ) - && needs.skip_duplicate_runs.outputs.should_skip != 'true' - }} - ANYMAIL_TEST_AMAZON_SES_ACCESS_KEY_ID: ${{ secrets.ANYMAIL_TEST_AMAZON_SES_ACCESS_KEY_ID }} - ANYMAIL_TEST_AMAZON_SES_REGION_NAME: ${{ secrets.ANYMAIL_TEST_AMAZON_SES_REGION_NAME }} - ANYMAIL_TEST_AMAZON_SES_SECRET_ACCESS_KEY: ${{ secrets.ANYMAIL_TEST_AMAZON_SES_SECRET_ACCESS_KEY }} - ANYMAIL_TEST_MAILGUN_API_KEY: ${{ secrets.ANYMAIL_TEST_MAILGUN_API_KEY }} - ANYMAIL_TEST_MAILGUN_DOMAIN: ${{ secrets.ANYMAIL_TEST_MAILGUN_DOMAIN }} - ANYMAIL_TEST_MAILJET_API_KEY: ${{ secrets.ANYMAIL_TEST_MAILJET_API_KEY }} - ANYMAIL_TEST_MAILJET_SECRET_KEY: ${{ secrets.ANYMAIL_TEST_MAILJET_SECRET_KEY }} - ANYMAIL_TEST_MANDRILL_API_KEY: ${{ secrets.ANYMAIL_TEST_MANDRILL_API_KEY }} - ANYMAIL_TEST_POSTMARK_SERVER_TOKEN: ${{ secrets.ANYMAIL_TEST_POSTMARK_SERVER_TOKEN }} - ANYMAIL_TEST_POSTMARK_TEMPLATE_ID: ${{ secrets.ANYMAIL_TEST_POSTMARK_TEMPLATE_ID }} - ANYMAIL_TEST_SENDGRID_API_KEY: ${{ secrets.ANYMAIL_TEST_SENDGRID_API_KEY }} - ANYMAIL_TEST_SENDGRID_TEMPLATE_ID: ${{ secrets.ANYMAIL_TEST_SENDGRID_TEMPLATE_ID }} - ANYMAIL_TEST_SENDINBLUE_API_KEY: ${{ secrets.ANYMAIL_TEST_SENDINBLUE_API_KEY }} - ANYMAIL_TEST_SPARKPOST_API_KEY: ${{ secrets.ANYMAIL_TEST_SPARKPOST_API_KEY }} - - identify_tag: - # If the (exact) ref on which this workflow is running is tagged, - # set outputs.tag to the tag; otherwise set it to an empty string - runs-on: ubuntu-20.04 - outputs: - tag: ${{ steps.get_tag.outputs.tag }} - steps: - - name: Get code - uses: actions/checkout@v2 - - name: Identify git tag - id: get_tag - run: | - TAG=$(git describe --tags --exact-match "$GITHUB_REF" 2>/dev/null || echo "") - echo "::set-output name=tag::$TAG" - echo "github ref: $GITHUB_REF" - echo "tag: ${TAG:-(no tag)}" - - release: - # Release on push or manual trigger of ref that is a v* tag - runs-on: ubuntu-20.04 - needs: - - identify_tag - - test - if: >- - startsWith(needs.identify_tag.outputs.tag, 'v') - && (github.event_name == 'push' - || github.event_name == 'workflow_dispatch') - steps: - - name: Get code - uses: actions/checkout@v2 - - name: Setup Python - uses: actions/setup-python@v2 - with: - python-version: 3.9 - - name: Check package version - id: version - env: - TAG: ${{ needs.identify_tag.outputs.tag }} - run: | - VERSION="v$(python setup.py --version)" - if [ "$VERSION" != "$TAG" ]; then - echo "::error ::package version '$VERSION' does not match git tag '$TAG'" - exit 1 - fi - echo "::set-output name=version::$VERSION" - echo "::set-output name=anchor::${VERSION//[^[:alnum:]]/-}" - - name: Install build requirements - run: | - pip install twine wheel - - name: Build - run: | - rm -rf build dist django_anymail.egg-info - python setup.py sdist bdist_wheel - twine check dist/* - - name: Publish package to PyPI - env: - TWINE_USERNAME: __token__ - TWINE_PASSWORD: ${{ secrets.PYPI_API_TOKEN }} - run: | - twine upload dist/* - - name: Publish release to GitHub - env: - GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} - TAG: ${{ steps.version.outputs.version }} - TITLE: ${{ steps.version.outputs.version }} - NOTES: | - [Changelog](https://anymail.readthedocs.io/en/stable/changelog/#${{ steps.version.outputs.anchor }}) - run: | - # gh release create-or-edit "$TAG" --title "$TITLE" --notes "$NOTES" ./dist/* - # (gh release doesn't support edit - 6/2021) - # (hub requires separate --attach=FILE arg for each file) - FILES=(./dist/*) - if ! hub release edit --message "$TITLE" --message "$NOTES" "${FILES[@]/#/--attach=}" "$TAG"; then - hub release create --message "$TITLE" --message "$NOTES" "${FILES[@]/#/--attach=}" "$TAG" - fi diff --git a/.github/workflows/integration-test.yml b/.github/workflows/integration-test.yml new file mode 100644 index 0000000..9b67786 --- /dev/null +++ b/.github/workflows/integration-test.yml @@ -0,0 +1,88 @@ +name: integration-test + +on: + pull_request: + push: + branches: [ "main", "v[0-9]*" ] + tags: [ "v[0-9]*" ] + workflow_dispatch: + schedule: + # Weekly test (on branch main) every Thursday at 12:15 UTC. + # (Used to monitor compatibility with ESP API changes.) + - cron: "15 12 * * 4" + + +jobs: + skip_duplicate_runs: + # Avoid running the live integration tests twice on the same code + # (to conserve limited sending quotas in the live ESP test accounts) + runs-on: ubuntu-20.04 + outputs: + should_skip: ${{ steps.skip_check.outputs.should_skip }} + steps: + - id: skip_check + # uses: fkirc/skip-duplicate-actions@v3.4.0 + uses: fkirc/skip-duplicate-actions@4c656bbdb6906310fa6213604828008bc28fe55d + with: + concurrent_skipping: "same_content" + skip_after_successful_duplicate: "true" + cancel_others: "true" + do_not_skip: '["pull_request", "workflow_dispatch", "schedule"]' + + test: + name: ${{ matrix.config.tox }} ${{ matrix.config.options }} + runs-on: ubuntu-20.04 + needs: skip_duplicate_runs + if: needs.skip_duplicate_runs.outputs.should_skip != 'true' + timeout-minutes: 15 + strategy: + fail-fast: false + matrix: + # Live API integration tests are run on only one representative Python/Django version + # combination, to avoid rapidly consuming the testing accounts' entire send allotments. + config: + - { tox: django40-py310-amazon_ses, python: "3.10" } + - { tox: django40-py310-mailgun, python: "3.10" } + - { tox: django40-py310-mailjet, python: "3.10" } + - { tox: django40-py310-mandrill, python: "3.10" } + - { tox: django40-py310-postal, python: "3.10" } + - { tox: django40-py310-postmark, python: "3.10" } + - { tox: django40-py310-sendgrid, python: "3.10" } + - { tox: django40-py310-sendinblue, python: "3.10" } + - { tox: django40-py310-sparkpost, python: "3.10" } + + steps: + - name: Get code + uses: actions/checkout@v2 + - name: Setup Python ${{ matrix.config.python }} + uses: actions/setup-python@v2 + with: + python-version: ${{ matrix.config.python }} + - name: Install tox + run: | + set -x + python --version + pip install tox + tox --version + - name: Test ${{ matrix.config.tox }} + run: | + tox -e ${{ matrix.config.tox }} + continue-on-error: ${{ contains( matrix.config.options, 'allow-failures' ) }} + env: + CONTINUOUS_INTEGRATION: true + TOX_FORCE_IGNORE_OUTCOME: false + ANYMAIL_RUN_LIVE_TESTS: true + ANYMAIL_TEST_AMAZON_SES_ACCESS_KEY_ID: ${{ secrets.ANYMAIL_TEST_AMAZON_SES_ACCESS_KEY_ID }} + ANYMAIL_TEST_AMAZON_SES_REGION_NAME: ${{ secrets.ANYMAIL_TEST_AMAZON_SES_REGION_NAME }} + ANYMAIL_TEST_AMAZON_SES_SECRET_ACCESS_KEY: ${{ secrets.ANYMAIL_TEST_AMAZON_SES_SECRET_ACCESS_KEY }} + ANYMAIL_TEST_MAILGUN_API_KEY: ${{ secrets.ANYMAIL_TEST_MAILGUN_API_KEY }} + ANYMAIL_TEST_MAILGUN_DOMAIN: ${{ secrets.ANYMAIL_TEST_MAILGUN_DOMAIN }} + ANYMAIL_TEST_MAILJET_API_KEY: ${{ secrets.ANYMAIL_TEST_MAILJET_API_KEY }} + ANYMAIL_TEST_MAILJET_SECRET_KEY: ${{ secrets.ANYMAIL_TEST_MAILJET_SECRET_KEY }} + ANYMAIL_TEST_MANDRILL_API_KEY: ${{ secrets.ANYMAIL_TEST_MANDRILL_API_KEY }} + ANYMAIL_TEST_POSTMARK_SERVER_TOKEN: ${{ secrets.ANYMAIL_TEST_POSTMARK_SERVER_TOKEN }} + ANYMAIL_TEST_POSTMARK_TEMPLATE_ID: ${{ secrets.ANYMAIL_TEST_POSTMARK_TEMPLATE_ID }} + ANYMAIL_TEST_SENDGRID_API_KEY: ${{ secrets.ANYMAIL_TEST_SENDGRID_API_KEY }} + ANYMAIL_TEST_SENDGRID_TEMPLATE_ID: ${{ secrets.ANYMAIL_TEST_SENDGRID_TEMPLATE_ID }} + ANYMAIL_TEST_SENDINBLUE_API_KEY: ${{ secrets.ANYMAIL_TEST_SENDINBLUE_API_KEY }} + ANYMAIL_TEST_SPARKPOST_API_KEY: ${{ secrets.ANYMAIL_TEST_SPARKPOST_API_KEY }} diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml new file mode 100644 index 0000000..f0c3b1c --- /dev/null +++ b/.github/workflows/release.yml @@ -0,0 +1,77 @@ +name: release + +# To release this package: +# 1. Update the version number and changelog in the source. +# Commit and push (to branch main or a vX.Y patch branch), +# and wait for tests to complete. +# 2. Tag with "vX.Y" or "vX.Y.Z": either create and push tag +# directly via git, or create and publish a GitHub release. +# +# This workflow will run in response to the new tag, and will: +# - Verify the source code and git tag version numbers match +# - Publish the package to PyPI +# - Create or update the release on GitHub + +on: + push: + tags: [ "v[0-9]*" ] + workflow_dispatch: + +jobs: + release: + runs-on: ubuntu-20.04 + environment: release + steps: + - name: Get code + uses: actions/checkout@v2 + - name: Setup Python + uses: actions/setup-python@v2 + with: + python-version: "3.10" + + - name: Get version + # (This will end the workflow if git and source versions don't match.) + id: version + run: | + VERSION="$(python setup.py --version)" + TAG="v$VERSION" + GIT_TAG="$(git tag -l --points-at "$GITHUB_REF" 'v*')" + if [ "$GIT_TAG" != "$TAG" ]; then + echo "::error ::package version '$TAG' does not match git tag '$GIT_TAG'" + exit 1 + fi + echo "::set-output name=version::$VERSION" + echo "::set-output name=tag::$TAG" + echo "::set-output name=anchor::${TAG//[^[:alnum:]]/-}" + + - name: Install build requirements + run: | + pip install twine wheel + - name: Build + run: | + rm -rf build dist django_anymail.egg-info + python setup.py sdist bdist_wheel + twine check dist/* + + - name: Publish to PyPI + env: + TWINE_USERNAME: __token__ + TWINE_PASSWORD: ${{ secrets.PYPI_API_TOKEN }} + run: | + twine upload dist/* + + - name: Release to GitHub + env: + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + TAG: ${{ steps.version.outputs.tag }} + TITLE: ${{ steps.version.outputs.tag }} + NOTES: | + [Changelog](https://anymail.readthedocs.io/en/stable/changelog/#${{ steps.version.outputs.anchor }}) + run: | + # gh release create-or-edit "$TAG" --target "$GITHUB_REF" --title "$TITLE" --notes "$NOTES" ./dist/* + # (gh release doesn't support edit - 6/2021) + # (hub requires separate --attach=FILE arg for each file) + FILES=(./dist/*) + if ! hub release edit --message "$TITLE" --message "$NOTES" "${FILES[@]/#/--attach=}" "$TAG"; then + hub release create -t "$GITHUB_SHA" --message "$TITLE" --message "$NOTES" "${FILES[@]/#/--attach=}" "$TAG" + fi diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml new file mode 100644 index 0000000..f7d3501 --- /dev/null +++ b/.github/workflows/test.yml @@ -0,0 +1,107 @@ +name: test + +on: + pull_request: + push: + branches: [ "main", "v[0-9]*" ] + tags: [ "v[0-9]*" ] + workflow_dispatch: + schedule: + # Weekly test (on branch main) every Thursday at 12:00 UTC. + # (Used to monitor compatibility with Django patches/dev and other dependencies.) + - cron: "0 12 * * 4" + + +jobs: + test: + name: ${{ matrix.config.tox }} ${{ matrix.config.options }} + runs-on: ubuntu-20.04 + timeout-minutes: 15 + strategy: + fail-fast: false + matrix: + config: + - { tox: "lint", python: "3.10" } + - { tox: "docs", python: "3.10" } + + # Anymail supports the same Python versions as Django, plus PyPy. + # https://docs.djangoproject.com/en/dev/faq/install/#what-python-version-can-i-use-with-django + + # Django 2.0: Python 3.5+ + - { tox: django20-py35-all, python: "3.5" } + - { tox: django20-py36-all, python: "3.6" } + - { tox: django20-pypy3-all, python: "pypy-3.6" } + # Django 2.1: Python 3.5, 3.6, or 3.7 + - { tox: django21-py35-all, python: "3.5" } + - { tox: django21-py36-all, python: "3.6" } + - { tox: django21-py37-all, python: "3.7" } + - { tox: django21-pypy3-all, python: "pypy-3.7" } + # Django 2.2: Python 3.5, 3.6, 3.7, 3.8 (added in 2.2.8), 3.9 (added in 2.2.17) + - { tox: django22-py35-all, python: "3.5" } + - { tox: django22-py36-all, python: "3.6" } + - { tox: django22-py37-all, python: "3.7" } + - { tox: django22-py38-all, python: "3.8" } + - { tox: django22-py39-all, python: "3.9" } + - { tox: django22-pypy3-all, python: "pypy-3.8" } + # Django 3.0: Python 3.6, 3.7, 3.8, 3.9 (added in 3.0.11) + - { tox: django30-py36-all, python: "3.6" } + - { tox: django30-py37-all, python: "3.7" } + - { tox: django30-py38-all, python: "3.8" } + - { tox: django30-py39-all, python: "3.9" } + - { tox: django30-pypy3-all, python: "pypy-3.8" } + # Django 3.1: Python 3.6, 3.7, 3.8, 3.9 (added in 3.1.3) + - { tox: django31-py36-all, python: "3.6" } + - { tox: django31-py37-all, python: "3.7" } + - { tox: django31-py38-all, python: "3.8" } + - { tox: django31-py39-all, python: "3.9" } + - { tox: django31-pypy3-all, python: "pypy-3.8" } + # Django 3.2: Python 3.6, 3.7, 3.8, 3.9 + - { tox: django32-py36-all, python: "3.6" } + - { tox: django32-py37-all, python: "3.7" } + - { tox: django32-py38-all, python: "3.8" } + - { tox: django32-py39-all, python: "3.9" } + - { tox: django32-pypy3-all, python: "pypy-3.8" } + # Django 4.0: Python 3.8, 3.9, 3.10 + - { tox: django40-py38-all, python: "3.8" } + - { tox: django40-py39-all, python: "3.9" } + - { tox: django40-py310-all, python: "3.10" } + - { tox: django40-pypy3-all, python: "pypy-3.8" } + # Django current development (direct from GitHub source) + - { tox: djangoDev-py38-all, python: "3.8", options: allow-failures } + - { tox: djangoDev-py39-all, python: "3.9", options: allow-failures } + - { tox: djangoDev-py310-all, python: "3.10", options: allow-failures } + - { tox: djangoDev-py311-all, python: "3.11.0-alpha - 3.11", options: allow-failures } + # Install without optional extras (don't need to cover entire matrix) + - { tox: django40-py310-none, python: "3.10" } + - { tox: django40-py310-amazon_ses, python: "3.10" } + - { tox: django40-py310-postal, python: "3.10" } + # Test some specific older package versions + - { tox: django22-py37-all-old_urllib3, python: "3.7" } + + steps: + - name: Get code + uses: actions/checkout@v2 + - name: Setup Python ${{ matrix.config.python }} + # Ensure matrix Python version is installed and available for tox + uses: actions/setup-python@v2 + with: + python-version: ${{ matrix.config.python }} + - name: Setup default Python + # Change default Python version back to something consistent + # for installing/running tox + uses: actions/setup-python@v2 + with: + python-version: "3.10" + - name: Install tox + run: | + set -x + python --version + pip install tox + tox --version + - name: Test ${{ matrix.config.tox }} + run: | + tox -e ${{ matrix.config.tox }} + continue-on-error: ${{ contains( matrix.config.options, 'allow-failures' ) }} + env: + CONTINUOUS_INTEGRATION: true + TOX_FORCE_IGNORE_OUTCOME: false diff --git a/README.rst b/README.rst index 9528957..33902bd 100644 --- a/README.rst +++ b/README.rst @@ -51,9 +51,13 @@ The package is released under the BSD license. .. END shared-intro -.. image:: https://github.com/anymail/django-anymail/workflows/ci-cd/badge.svg?branch=main - :target: https://github.com/anymail/django-anymail/actions?query=workflow:ci-cd+branch:main - :alt: build status in GitHub Actions +.. image:: https://github.com/anymail/django-anymail/workflows/test/badge.svg?branch=main + :target: https://github.com/anymail/django-anymail/actions?query=workflow:test+branch:main + :alt: test status in GitHub Actions + +.. image:: https://github.com/anymail/django-anymail/workflows/integration-test/badge.svg?branch=main + :target: https://github.com/anymail/django-anymail/actions?query=workflow:integration-test+branch:main + :alt: integration test status in GitHub Actions .. image:: https://readthedocs.org/projects/anymail/badge/?version=stable :target: https://anymail.readthedocs.io/en/stable/ diff --git a/tests/test_settings/settings_4_0.py b/tests/test_settings/settings_4_0.py index 788eba6..0888c34 100644 --- a/tests/test_settings/settings_4_0.py +++ b/tests/test_settings/settings_4_0.py @@ -1,23 +1,23 @@ """ Django settings for Anymail tests. -Generated by 'django-admin startproject' using Django 3.1. +Generated by 'django-admin startproject' using Django 4.0.1. For more information on this file, see -https://docs.djangoproject.com/en/dev/topics/settings/ +https://docs.djangoproject.com/en/4.0/topics/settings/ For the full list of settings and their values, see -https://docs.djangoproject.com/en/dev/ref/settings/ +https://docs.djangoproject.com/en/4.0/ref/settings/ """ -import os +from pathlib import Path -# Build paths inside the project like this: os.path.join(BASE_DIR, ...) -BASE_DIR = os.path.dirname(os.path.dirname(os.path.abspath(__file__))) +# Build paths inside the project like this: BASE_DIR / 'subdir'. +BASE_DIR = Path(__file__).resolve().parent.parent # Quick-start development settings - unsuitable for production -# See https://docs.djangoproject.com/en/dev/howto/deployment/checklist/ +# See https://docs.djangoproject.com/en/4.0/howto/deployment/checklist/ # SECURITY WARNING: keep the secret key used in production secret! SECRET_KEY = 'NOT_FOR_PRODUCTION_USE' @@ -72,18 +72,18 @@ WSGI_APPLICATION = 'tests.wsgi.application' # Database -# https://docs.djangoproject.com/en/dev/ref/settings/#databases +# https://docs.djangoproject.com/en/4.0/ref/settings/#databases DATABASES = { 'default': { 'ENGINE': 'django.db.backends.sqlite3', - 'NAME': os.path.join(BASE_DIR, 'db.sqlite3'), + 'NAME': BASE_DIR / 'db.sqlite3', } } # Password validation -# https://docs.djangoproject.com/en/dev/ref/settings/#auth-password-validators +# https://docs.djangoproject.com/en/4.0/ref/settings/#auth-password-validators AUTH_PASSWORD_VALIDATORS = [ { @@ -102,7 +102,7 @@ AUTH_PASSWORD_VALIDATORS = [ # Internationalization -# https://docs.djangoproject.com/en/dev/topics/i18n/ +# https://docs.djangoproject.com/en/4.0/topics/i18n/ LANGUAGE_CODE = 'en-us' @@ -110,12 +110,15 @@ TIME_ZONE = 'UTC' USE_I18N = True -USE_L10N = True - USE_TZ = True # Static files (CSS, JavaScript, Images) -# https://docs.djangoproject.com/en/dev/howto/static-files/ +# https://docs.djangoproject.com/en/4.0/howto/static-files/ -STATIC_URL = '/static/' +STATIC_URL = 'static/' + +# Default primary key field type +# https://docs.djangoproject.com/en/4.0/ref/settings/#default-auto-field + +DEFAULT_AUTO_FIELD = 'django.db.models.BigAutoField' diff --git a/tests/test_settings/settings_4_1.py b/tests/test_settings/settings_4_1.py new file mode 100644 index 0000000..0888c34 --- /dev/null +++ b/tests/test_settings/settings_4_1.py @@ -0,0 +1,124 @@ +""" +Django settings for Anymail tests. + +Generated by 'django-admin startproject' using Django 4.0.1. + +For more information on this file, see +https://docs.djangoproject.com/en/4.0/topics/settings/ + +For the full list of settings and their values, see +https://docs.djangoproject.com/en/4.0/ref/settings/ +""" + +from pathlib import Path + +# Build paths inside the project like this: BASE_DIR / 'subdir'. +BASE_DIR = Path(__file__).resolve().parent.parent + + +# Quick-start development settings - unsuitable for production +# See https://docs.djangoproject.com/en/4.0/howto/deployment/checklist/ + +# SECURITY WARNING: keep the secret key used in production secret! +SECRET_KEY = 'NOT_FOR_PRODUCTION_USE' + +# SECURITY WARNING: don't run with debug turned on in production! +DEBUG = True + +ALLOWED_HOSTS = [] + + +# Application definition + +INSTALLED_APPS = [ + 'django.contrib.admin', + 'django.contrib.auth', + 'django.contrib.contenttypes', + 'django.contrib.sessions', + 'django.contrib.messages', + 'django.contrib.staticfiles', + 'anymail', +] + +MIDDLEWARE = [ + 'django.middleware.security.SecurityMiddleware', + 'django.contrib.sessions.middleware.SessionMiddleware', + 'django.middleware.common.CommonMiddleware', + 'django.middleware.csrf.CsrfViewMiddleware', + 'django.contrib.auth.middleware.AuthenticationMiddleware', + 'django.contrib.messages.middleware.MessageMiddleware', + 'django.middleware.clickjacking.XFrameOptionsMiddleware', +] + +ROOT_URLCONF = 'tests.test_settings.urls' + +TEMPLATES = [ + { + 'BACKEND': 'django.template.backends.django.DjangoTemplates', + 'DIRS': [], + 'APP_DIRS': True, + 'OPTIONS': { + 'context_processors': [ + 'django.template.context_processors.debug', + 'django.template.context_processors.request', + 'django.contrib.auth.context_processors.auth', + 'django.contrib.messages.context_processors.messages', + ], + }, + }, +] + +WSGI_APPLICATION = 'tests.wsgi.application' + + +# Database +# https://docs.djangoproject.com/en/4.0/ref/settings/#databases + +DATABASES = { + 'default': { + 'ENGINE': 'django.db.backends.sqlite3', + 'NAME': BASE_DIR / 'db.sqlite3', + } +} + + +# Password validation +# https://docs.djangoproject.com/en/4.0/ref/settings/#auth-password-validators + +AUTH_PASSWORD_VALIDATORS = [ + { + 'NAME': 'django.contrib.auth.password_validation.UserAttributeSimilarityValidator', + }, + { + 'NAME': 'django.contrib.auth.password_validation.MinimumLengthValidator', + }, + { + 'NAME': 'django.contrib.auth.password_validation.CommonPasswordValidator', + }, + { + 'NAME': 'django.contrib.auth.password_validation.NumericPasswordValidator', + }, +] + + +# Internationalization +# https://docs.djangoproject.com/en/4.0/topics/i18n/ + +LANGUAGE_CODE = 'en-us' + +TIME_ZONE = 'UTC' + +USE_I18N = True + +USE_TZ = True + + +# Static files (CSS, JavaScript, Images) +# https://docs.djangoproject.com/en/4.0/howto/static-files/ + +STATIC_URL = 'static/' + +# Default primary key field type +# https://docs.djangoproject.com/en/4.0/ref/settings/#default-auto-field + +DEFAULT_AUTO_FIELD = 'django.db.models.BigAutoField' diff --git a/tox.ini b/tox.ini index 156df96..3f5d85c 100644 --- a/tox.ini +++ b/tox.ini @@ -3,11 +3,12 @@ envlist = # Factors: django-python-extras # Test these environments first, to catch most errors early... lint - django32-py39-all + django40-py310-all django20-py35-all docs # ... then test all the other supported combinations: - django32-py{36,37,38,py3}-all + django40-py{38,39,py3}-all + django32-py{36,37,38,39,py3}-all django31-py{36,37,38,39,py3}-all django30-py{36,37,38,39,py3}-all django22-py{35,36,37,38,39,py3}-all @@ -16,7 +17,7 @@ envlist = # ... then prereleases (if available): djangoDev-py{38,39,310,py3}-all # ... then partial installation (limit extras): - django31-py37-{none,amazon_ses,postal} + django40-py310-{none,amazon_ses,postal} # ... then older versions of some dependencies: django22-py37-all-old_urllib3 @@ -28,6 +29,7 @@ deps = django30: django~=3.0.0 django31: django~=3.1.0 django32: django~=3.2.0 + django40: django~=4.0.0 djangoDev: https://github.com/django/django/tarball/main old_urllib3: urllib3<1.25 extras = @@ -40,7 +42,14 @@ setenv = # tell runtests.py to limit some test tags based on extras factor none: ANYMAIL_SKIP_TESTS=amazon_ses,postal amazon_ses: ANYMAIL_ONLY_TEST=amazon_ses + mailgun: ANYMAIL_ONLY_TEST=mailgun + mailjet: ANYMAIL_ONLY_TEST=mailjet + mandrill: ANYMAIL_ONLY_TEST=mandrill postal: ANYMAIL_ONLY_TEST=postal + postmark: ANYMAIL_ONLY_TEST=postmark + sendgrid: ANYMAIL_ONLY_TEST=sendgrid + sendinblue: ANYMAIL_ONLY_TEST=sendinblue + sparkpost: ANYMAIL_ONLY_TEST=sparkpost ignore_outcome = # CI that wants to handle errors itself can set TOX_FORCE_IGNORE_OUTCOME=false djangoDev: {env:TOX_FORCE_IGNORE_OUTCOME:true}