diff --git a/.github/workflows/ci.yaml b/.github/workflows/ci.yaml index 49f4bae75..5fe1d8481 100644 --- a/.github/workflows/ci.yaml +++ b/.github/workflows/ci.yaml @@ -32,7 +32,7 @@ jobs: - name: Set up Python uses: actions/setup-python@v4 with: - python-version: '3.6' + python-version: '3.10' cache: 'pip' - name: Set up Python dependencies @@ -63,4 +63,10 @@ jobs: pytest -n 2 --cov=. --cov-report=term-missing --cov-report=xml - name: Run Codecov action - uses: codecov/codecov-action@v3 + uses: codecov/codecov-action@v4 + with: + fail_ci_if_error: false + files: ./src/coverage.xml + verbose: true + env: + CODECOV_TOKEN: ${{ secrets.CODECOV_TOKEN }} diff --git a/.python-version b/.python-version index 424e1794d..9dfc796e8 100644 --- a/.python-version +++ b/.python-version @@ -1 +1 @@ -3.6.8 +3.10.14 \ No newline at end of file diff --git a/Dockerfile b/Dockerfile index 945df5edf..510caf3f0 100644 --- a/Dockerfile +++ b/Dockerfile @@ -13,7 +13,7 @@ RUN yarn install --dev --frozen-lockfile \ # [Python Stage for Django web server] -FROM python:3.6-slim-buster as python_stage +FROM python:3.10.14-slim-bullseye as python_stage ENV PYTHONUNBUFFERED 1 ENV BASE_DIR /usr/local @@ -32,7 +32,15 @@ ENV PATH /home/docker/.local/bin:$PATH # Infrastructure tools # gettext is used for django to compile .po to .mo files. RUN apt-get update -RUN apt-get install gettext libpq-dev gcc mime-support -y +RUN apt-get install -y \ + libpq-dev \ + gcc \ + zlib1g-dev \ + libjpeg62-turbo-dev \ + mime-support \ + gettext \ + libxml2-dev \ + libxslt-dev # APP directory setup RUN adduser --system --disabled-login docker \ diff --git a/README.md b/README.md index 14676d65e..1098dd65b 100644 --- a/README.md +++ b/README.md @@ -10,7 +10,7 @@ This repository serves the website of PyCon TW, Python Conference Taiwan. This p ### Requirements - Git 1.8+ -- Python 3.6.x +- Python 3.10.x - Yarn 1.0+ - Node.js 8.0+ @@ -23,26 +23,15 @@ This repository serves the website of PyCon TW, Python Conference Taiwan. This p ## Run Tests -Tests are managed with [pytest-django](http://pytest-django.readthedocs.org/en/latest/tutorial.html). You have two options to run tests, either with the local environment, or in an isolated one via [Tox](http://tox.readthedocs.org/en/latest/). +Tests are managed with [pytest-django](http://pytest-django.readthedocs.org/en/latest/tutorial.html). You can run tests in your local environment: +- Run the following command inside `src`: -### Testing in the Local Environment + pytest -Run the following command inside `src`: - - pytest - -To run tests with coverage report: - - pytest --cov=. - - -### Testing with Tox - -Run the following inside the top-level directory (the one with `tox.ini`): - - tox +- To run tests with coverage report: + pytest --cov=. ## How to Contribute diff --git a/dev.Dockerfile b/dev.Dockerfile index 666f52e80..c58dd514f 100644 --- a/dev.Dockerfile +++ b/dev.Dockerfile @@ -11,7 +11,7 @@ RUN npm install -g yarn RUN yarn install --dev --frozen-lockfile # [Python Stage for Django web server] -FROM python:3.6-slim-buster as python_stage +FROM python:3.10.14-slim-bullseye as python_stage WORKDIR /app @@ -25,7 +25,10 @@ RUN apt-get install -y \ gcc \ zlib1g-dev \ libjpeg62-turbo-dev \ - gettext + mime-support \ + gettext \ + libxml2-dev \ + libxslt-dev # Only copy and install requirements to improve caching between builds # Install Python dependencies diff --git a/docker-compose.yml b/docker-compose.yml index 78e4d20da..d2f5b1f40 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -31,7 +31,6 @@ services: SECRET_KEY: ${SECRET_KEY} DATABASE_URL: ${DATABASE_URL} EMAIL_URL: ${EMAIL_URL} - DSN_URL: ${DSN_URL} GTM_TRACK_ID: ${GTM_TRACK_ID} SLACK_WEBHOOK_URL: ${SLACK_WEBHOOK_URL} diff --git a/document/deploy_docker_prod.md b/document/deploy_docker_prod.md index ef74c5e17..214708047 100644 --- a/document/deploy_docker_prod.md +++ b/document/deploy_docker_prod.md @@ -22,9 +22,6 @@ There are four configurations that must be set when running the container. e.g. `postgres://username:password@host_or_ip:5432/database_name`) * `EMAIL_URL` specifies how to connect to the mail server (e.g. `smtp+tls://username:password@host_or_ip:25`) - * `DSN_URL` specify how to connect to Sentry error reporting service - (e.g. `https://key@sentry.io/project`), please refer to - [Sentry's documentation on how to obtain Data Source Name](https://docs.sentry.io/error-reporting/quickstart/?platform=python) * (optional) `GTM_TRACK_ID` * (optional) `SLACK_WEBHOOK_URL` diff --git a/requirements/base.txt b/requirements/base.txt index 539b6b0d9..14936418d 100644 --- a/requirements/base.txt +++ b/requirements/base.txt @@ -6,10 +6,6 @@ Django==3.1.7 # https://github.com/yychen/dj-registry dj-registry==0.2 -# Reusable mixins for Django -# https://django-braces.readthedocs.io/en/latest/index.html -django-braces==1.14.0 - # Compresses linked and inline JavaScript or CSS into a single cached file. # https://django-compressor.readthedocs.io/en/latest/ django-compressor==2.4 @@ -29,7 +25,7 @@ django-crispy-forms==1.8.1 # Django-environ allows you to utilize 12factor inspired environment variables # to configure your Django application. # https://django-environ.readthedocs.io/en/latest/ -django-environ==0.4.5 +django-environ==0.11.2 # Django application and library for importing and exporting data with # included admin integration. @@ -44,18 +40,6 @@ django-extensions==2.2.6 # https://github.com/deschler/django-modeltranslation django-modeltranslation==0.16.2 -# Native Django task queue, scheduler and worker application using Python -# multiprocessing. -# https://django-q.readthedocs.io/ -django-q==1.1.0 - -# A pickled object field for Django. (Dependency of django-q) -# https://github.com/gintas/django-picklefield -django-picklefield==2.1.1 - -# Certifi, a carefully curated collection of Root Certificates for SSL -certifi>=2019.11.28 - # Faker is a Python package that generates fake data for you. # https://faker.readthedocs.io/en/master/ # @@ -92,13 +76,15 @@ sortedcontainers==2.1.0 # Tabulate, an utility for pretty-print tabular data # https://bitbucket.org/astanin/python-tabulate -tabulate==0.8.6 +tabulate==0.9.0 -urllib3==1.26.5 +# HTTP library +# https://github.com/psf/requests +requests==2.31.0 # For upload to Google cloud storage google-cloud-storage==1.36.1 -django-storages==1.11.1; +django-storages==1.11.1 # Django REST framework: a powerful and flexible toolkit for building Web APIs. # https://www.django-rest-framework.org/ diff --git a/requirements/dev.txt b/requirements/dev.txt index 025172814..79b8e4173 100644 --- a/requirements/dev.txt +++ b/requirements/dev.txt @@ -3,7 +3,7 @@ # A configurable set of panels that display various debug information about # the current request/response. # https://django-debug-toolbar.readthedocs.io/en/latest/ -django-debug-toolbar==2.2.1 +django-debug-toolbar==3.2.3 # The Python WSGI Utility Library # https://palletsprojects.com/p/werkzeug/ @@ -15,7 +15,7 @@ flake8==3.7.9 # A mature full-featured Python testing tool. # https://docs.pytest.org/en/latest/ -pytest==5.3.5 +pytest==6.2.5 # A plugin for py.test that provides a set of useful tools for testing Django # applications and projects. @@ -50,4 +50,4 @@ pytest-xdist==1.31.0 cssselect==1.1.0 # PostgreSQL database adapter for the Python -psycopg2-binary==2.8.5 \ No newline at end of file +psycopg2-binary==2.8.5 diff --git a/requirements/local.txt b/requirements/local.txt deleted file mode 100644 index 18ce9a344..000000000 --- a/requirements/local.txt +++ /dev/null @@ -1,2 +0,0 @@ --r dev.txt -tox diff --git a/requirements/production.txt b/requirements/production.txt index 7c17453b6..40761321b 100644 --- a/requirements/production.txt +++ b/requirements/production.txt @@ -6,20 +6,4 @@ psycopg2==2.8.4 # uWSGI aims at developing a full stack for building hosting services # https://uwsgi-docs.readthedocs.io/ -uWSGI==2.0.18 - -# django-redis is a BSD Licensed, full featured Redis cache/session backend for Django. -# http://niwinz.github.io/django-redis/latest/ -django-redis==4.8.0 - -# huey, a little task queue -# https://huey.readthedocs.io/en/latest/index.html -huey==0.4.9 - -# Redis client for Python -# https://redis-py.readthedocs.org/en/latest/ -redis==2.10.6 - -# Sentry client for Python -# https://raven.readthedocs.org/en/latest/ -raven==6.5.0 +uWSGI==2.0.24 diff --git a/src/proposals/management/commands/slack.py b/src/proposals/management/commands/slack.py index 165f5de26..389e01cbf 100644 --- a/src/proposals/management/commands/slack.py +++ b/src/proposals/management/commands/slack.py @@ -4,8 +4,7 @@ """ import json -import urllib3 -import certifi +import requests class Slack: @@ -13,12 +12,9 @@ class Slack: def __init__(self, url): """Config a Slack connection""" self.url = url - # The connection is initiated lazily by urllib3 - self.pool = urllib3.PoolManager( - # Always check the HTTPS certificate - cert_reqs='CERT_REQUIRED', - ca_certs=certifi.where(), - ) + # The connection is initiated lazily by requests + self.session = requests.Session() + self.session.headers.update({'Content-Type': "application/json"}) def notify(self, **kwargs): """Collect **kwargs as JSON and talk to Slack.""" @@ -31,10 +27,8 @@ def send(self, payload): :param: payload: A dict-like object passed as JSON content """ - response = self.pool.urlopen( - "POST", + response = self.session.post( self.url, - headers={'Content-Type': "application/json"}, - body=json.dumps(payload) + data=json.dumps(payload) ) return response.status, response.data.decode('utf8') diff --git a/src/proposals/tests/test_management.py b/src/proposals/tests/test_management.py index be32e6354..0dabf43ad 100644 --- a/src/proposals/tests/test_management.py +++ b/src/proposals/tests/test_management.py @@ -177,15 +177,13 @@ def test_slack_connect(): from proposals.management.commands.slack import Slack webhook_url = settings.SLACK_WEBHOOK_URL slack = Slack(url=webhook_url) - slack.pool.urlopen = unittest.mock.MagicMock( + slack.session.post = unittest.mock.MagicMock( return_value=FakeHTTPResponse(200, b'ok') ) slack.notify(text='Test') - slack.pool.urlopen.assert_called_once_with( - "POST", + slack.session.post.assert_called_once_with( webhook_url, - headers={'Content-Type': "application/json"}, - body=json.dumps({"text": "Test"}) + data=json.dumps({"text": "Test"}) ) diff --git a/src/pycontw2016/settings/base.py b/src/pycontw2016/settings/base.py index c6aaeeab1..1ddaf5f86 100644 --- a/src/pycontw2016/settings/base.py +++ b/src/pycontw2016/settings/base.py @@ -76,9 +76,7 @@ # https://docs.djangoproject.com/en/dev/ref/settings/#databases DATABASES = { - # Raises ImproperlyConfigured exception if DATABASE_URL not in - # os.environ - 'default': env.db(), + 'default': env.db_url(var='DATABASE_URL', default='postgres://postgres:secretpostgres@127.0.0.1:5432/pycontw2016'), } # Application definition @@ -98,7 +96,6 @@ 'compressor_toolkit', 'crispy_forms', 'django_extensions', - 'django_q', 'import_export', 'sorl.thumbnail', 'registry', diff --git a/src/pycontw2016/settings/local.py b/src/pycontw2016/settings/local.py index 929560a70..658f35ba4 100644 --- a/src/pycontw2016/settings/local.py +++ b/src/pycontw2016/settings/local.py @@ -96,6 +96,3 @@ } logging.config.dictConfig(LOGGING) - - -DJANGO_Q_DEBUG = True diff --git a/src/pycontw2016/settings/production/base.py b/src/pycontw2016/settings/production/base.py index e4fe66e2b..990c2d3d7 100644 --- a/src/pycontw2016/settings/production/base.py +++ b/src/pycontw2016/settings/production/base.py @@ -9,7 +9,7 @@ # Google Cloud storage from google.oauth2 import service_account -from ..base import BASE_DIR, INSTALLED_APPS, MIDDLEWARE, TEMPLATES, env +from ..base import BASE_DIR, TEMPLATES, env from ..base import * # noqa @@ -90,11 +90,6 @@ logging.config.dictConfig(LOGGING) -MIDDLEWARE += ( - 'raven.contrib.django.raven_compat.middleware.' - 'SentryResponseErrorIdMiddleware', -) - EMAIL_BACKEND = env.email_url()['EMAIL_BACKEND'] EMAIL_HOST = env.email_url()['EMAIL_HOST'] EMAIL_HOST_PASSWORD = env.email_url()['EMAIL_HOST_PASSWORD'] @@ -117,23 +112,8 @@ X_FRAME_OPTIONS = 'DENY' -# Setting for sentry - -INSTALLED_APPS += ( - 'raven.contrib.django.raven_compat', -) - -import raven # noqa - -RAVEN_CONFIG = { - 'dsn': env('DSN_URL'), - 'release': raven.fetch_git_sha(os.path.dirname(BASE_DIR)), -} - GTM_TRACK_ID = env('GTM_TRACK_ID', default=None) -DJANGO_Q_DEBUG = False - # Google Cloud storage GS_CREDENTIALS = service_account.Credentials.from_service_account_file( os.path.join(BASE_DIR, "google-cloud-storage.json") diff --git a/src/pycontw2016/wsgi.py b/src/pycontw2016/wsgi.py index 8edc7227f..2974a2113 100644 --- a/src/pycontw2016/wsgi.py +++ b/src/pycontw2016/wsgi.py @@ -18,26 +18,17 @@ ) application = get_wsgi_application() -if settings.DEBUG: +if settings.DEBUG and settings.WERKZEUG_DEBUG: # Wrap werkzeug debugger. - if settings.WERKZEUG_DEBUG: - try: - import django.views.debug - import six - from werkzeug.debug import DebuggedApplication - except ImportError: - pass - else: - def null_response(request, exc_type, exc_value, tb): - six.reraise(exc_type, exc_value, tb) - - django.views.debug.technical_500_response = null_response - application = DebuggedApplication(application, evalex=True) -else: - # Wrap Sentry. try: - from raven.contrib.django.raven_compat.middleware.wsgi import Sentry + import django.views.debug + import six + from werkzeug.debug import DebuggedApplication except ImportError: pass else: - application = Sentry(application) + def null_response(request, exc_type, exc_value, tb): + six.reraise(exc_type, exc_value, tb) + + django.views.debug.technical_500_response = null_response + application = DebuggedApplication(application, evalex=True) diff --git a/tox.ini b/tox.ini deleted file mode 100644 index c4927de3e..000000000 --- a/tox.ini +++ /dev/null @@ -1,25 +0,0 @@ -[tox] -envlist = py36 -skipsdist = true - -[testenv] -commands = pytest -n4 {posargs} -changedir = {toxinidir}/src -deps = -r{toxinidir}/requirements/travis.txt - -[flake8] -ignore = E221,E241,W504 -exclude = - .cache, - .git, - .tox, - __pycache__, - assets, - locale, - logs, - media, - migrations, - node_modules, - static, - templates, -max-complexity = 10