From 0bd7a06b79247653b9ac374284c462512faca8cd Mon Sep 17 00:00:00 2001 From: crankynetman Date: Fri, 1 May 2026 11:48:08 -0500 Subject: [PATCH 1/5] chore(deps): bump and cleanup all versions for django 5.2 upgrade --- config/settings/base.py | 1 - requirements/base.txt | 38 ++++++++++++++++++------------------- requirements/local.txt | 29 ++++++++++++++-------------- requirements/production.txt | 6 +++--- 4 files changed, 37 insertions(+), 37 deletions(-) diff --git a/config/settings/base.py b/config/settings/base.py index 289f5f59..a3e67cdd 100644 --- a/config/settings/base.py +++ b/config/settings/base.py @@ -72,7 +72,6 @@ "channels", "corsheaders", "crispy_forms", - "django_celery_beat", "django_eventstream", "netfields", "simple_history", diff --git a/requirements/base.txt b/requirements/base.txt index 14f80a77..ac2e4204 100644 --- a/requirements/base.txt +++ b/requirements/base.txt @@ -1,34 +1,34 @@ setuptools<82 # until we modernize everything, we need this. -argon2-cffi==21.3.0 # https://github.com/hynek/argon2_cffi +argon2-cffi # https://github.com/hynek/argon2_cffi autobahn<25.10.1 # Pin to avoid v25.10.1 that is giving build troubles. -django-celery-beat # https://github.com/celery/django-celery-beat django-netfields # https://pypi.org/project/django-netfields/ -flower==1.0.0 # https://github.com/mher/flower -python-slugify==6.1.2 # https://github.com/un33k/python-slugify -uvicorn[standard]==0.17.6 # https://github.com/encode/uvicorn -whitenoise==6.2.0 # https://github.com/evansd/whitenoise +flower # https://github.com/mher/flower +python-slugify # https://github.com/un33k/python-slugify +uvicorn[standard] # https://github.com/encode/uvicorn +whitenoise # https://github.com/evansd/whitenoise # Django # ------------------------------------------------------------------------------ -channels_redis==3.4.0 -crispy-bootstrap5==0.6 # https://github.com/django-crispy-forms/crispy-bootstrap5 -django~=4.2 # https://www.djangoproject.com/ +channels_redis +crispy-bootstrap5 # https://github.com/django-crispy-forms/crispy-bootstrap5 +django~=5.2 # https://www.djangoproject.com/ +django-crispy-forms -django-allauth==0.51.0 # https://github.com/pennersr/django-allauth -django-environ==0.9.0 # https://github.com/joke2k/django-environ -django-eventstream==4.5.1 # https://github.com/fanout/django-eventstream -django-model-utils==4.2.0 # https://github.com/jazzband/django-model-utils -django-redis==5.3.0 -django-simple-history>=3.4.0 # 3.4.0+ dropped the pkg_resources dependency +django-allauth # https://github.com/pennersr/django-allauth +django-environ # https://github.com/joke2k/django-environ +django-eventstream # https://github.com/fanout/django-eventstream +django-model-utils # https://github.com/jazzband/django-model-utils +django-redis +django-simple-history # Django REST Framework -djangorestframework~=3.15 # https://github.com/encode/django-rest-framework -django-cors-headers==3.13.0 # https://github.com/adamchainz/django-cors-headers +djangorestframework # https://github.com/encode/django-rest-framework +django-cors-headers # https://github.com/adamchainz/django-cors-headers drf-spectacular # https://github.com/tfranzel/drf-spectacular # OIDC # ------------------------------------------------------------------------------ -mozilla_django_oidc==4.0.1 # https://github.com/mozilla/mozilla-django-oidc +mozilla-django-oidc # https://github.com/mozilla/mozilla-django-oidc -websockets~=10.3 +websockets diff --git a/requirements/local.txt b/requirements/local.txt index 1b774efc..f5b62021 100644 --- a/requirements/local.txt +++ b/requirements/local.txt @@ -1,15 +1,16 @@ -r base.txt -Werkzeug[watchdog]==2.0.3 # https://github.com/pallets/werkzeug -ipdb==0.13.9 # https://github.com/gotcha/ipdb -psycopg2-binary==2.9.10 # https://github.com/psycopg/psycopg2 -watchgod==0.8.2 # https://github.com/samuelcolvin/watchgod +Werkzeug[watchdog] # https://github.com/pallets/werkzeug +ipdb # https://github.com/gotcha/ipdb +psycopg2-binary # https://github.com/psycopg/psycopg2 +watchgod # https://github.com/samuelcolvin/watchgod # Testing # ------------------------------------------------------------------------------ -django-stubs==1.11.0 # https://github.com/typeddjango/django-stubs -pytest-sugar==1.1.1 # https://github.com/Frozenball/pytest-sugar -behave-django==1.7.0 # https://github.com/behave/behave-django +daphne +django-stubs[compatible-mypy] # https://github.com/typeddjango/django-stubs +pytest-sugar # https://github.com/Frozenball/pytest-sugar +behave-django # https://github.com/behave/behave-django # Documentation # ------------------------------------------------------------------------------ @@ -28,19 +29,19 @@ mkdocstrings-python==1.12.2 # ------------------------------------------------------------------------------ flake8-docstrings==1.7.0 # https://github.com/pycqa/flake8-docstrings flake8-isort==4.1.1 # https://github.com/gforcada/flake8-isort -black==22.3.0 # https://github.com/psf/black +black==24.4.2 # https://github.com/psf/black pylint-django==2.5.3 # https://github.com/PyCQA/pylint-django pylint-celery==0.3 # https://github.com/PyCQA/pylint-celery -pre-commit==4.5.1 # https://github.com/pre-commit/pre-commit +pre-commit==2.19.0 # https://github.com/pre-commit/pre-commit # Django # ------------------------------------------------------------------------------ -factory-boy==3.2.1 # https://github.com/FactoryBoy/factory_boy +factory-boy # https://github.com/FactoryBoy/factory_boy -django-debug-toolbar==3.4.0 # https://github.com/jazzband/django-debug-toolbar -django-extensions==3.1.5 # https://github.com/django-extensions/django-extensions -django-coverage-plugin==3.1.0 # https://github.com/nedbat/django_coverage_plugin -pytest-django==4.5.2 # https://github.com/pytest-dev/pytest-django +django-debug-toolbar # https://github.com/jazzband/django-debug-toolbar +django-extensions # https://github.com/django-extensions/django-extensions +django-coverage-plugin # https://github.com/nedbat/django_coverage_plugin +pytest-django # https://github.com/pytest-dev/pytest-django # Debugging # ------------------------------------------------------------------------------ diff --git a/requirements/production.txt b/requirements/production.txt index 3c6a79b7..b444eca9 100644 --- a/requirements/production.txt +++ b/requirements/production.txt @@ -2,9 +2,9 @@ -r base.txt -gunicorn==20.1.0 # https://github.com/benoitc/gunicorn -psycopg2==2.9.3 # https://github.com/psycopg/psycopg2 +gunicorn # https://github.com/benoitc/gunicorn +psycopg2 # https://github.com/psycopg/psycopg2 # Django # ------------------------------------------------------------------------------ -django-anymail==8.6 # https://github.com/anymail/django-anymail +django-anymail # https://github.com/anymail/django-anymail From 2b19aa0380b8fd34398afe3243ee5f3ad2ed0543 Mon Sep 17 00:00:00 2001 From: crankynetman Date: Fri, 1 May 2026 11:48:32 -0500 Subject: [PATCH 2/5] fix(django): change things for django 5.2 --- config/consumers.py | 10 ++++++---- pyproject.toml | 1 - scram/route_manager/tests/test_admin.py | 2 +- scram/route_manager/tests/test_websockets.py | 6 +++--- scram/users/forms.py | 4 ++-- 5 files changed, 12 insertions(+), 11 deletions(-) diff --git a/config/consumers.py b/config/consumers.py index acaf11c9..fd0c30cd 100644 --- a/config/consumers.py +++ b/config/consumers.py @@ -2,7 +2,7 @@ import logging -from asgiref.sync import sync_to_async +from channels.db import database_sync_to_async from channels.generic.websocket import AsyncJsonWebsocketConsumer from scram.route_manager.models import Entry, WebSocketSequenceElement @@ -23,7 +23,7 @@ async def connect(self): await self.accept() # Filter WebSocketSequenceElements by actiontype - elements = await sync_to_async(list)( + elements = await database_sync_to_async(list)( WebSocketSequenceElement.objects.filter(action_type__name=self.actiontype).order_by("order_num"), ) if not elements: @@ -31,11 +31,13 @@ async def connect(self): return # Avoid lazy evaluation - routes = await sync_to_async(list)(Entry.objects.filter(is_active=True).values_list("route__route", flat=True)) + routes = await database_sync_to_async(list)( + Entry.objects.filter(is_active=True).values_list("route__route", flat=True) + ) for route in routes: for element in elements: - msg = await sync_to_async(lambda e: e.websocketmessage)(element) + msg = await database_sync_to_async(lambda e: e.websocketmessage)(element) msg.msg_data[msg.msg_data_route_field] = str(route) await self.send_json({"type": msg.msg_type, "message": msg.msg_data}) diff --git a/pyproject.toml b/pyproject.toml index 2cc81574..80951ea4 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -6,7 +6,6 @@ version = "1.5.1" [tool.pytest.ini_options] addopts = [ "--ds=config.settings.test", - "--reuse-db", "--ignore=scheduler" ] minversion = "6.0" diff --git a/scram/route_manager/tests/test_admin.py b/scram/route_manager/tests/test_admin.py index 4cf0a4d3..5063fc94 100644 --- a/scram/route_manager/tests/test_admin.py +++ b/scram/route_manager/tests/test_admin.py @@ -35,7 +35,7 @@ def test_who_filter_lookups(self): def test_who_filter_queryset_with_value(self): """Test that the queryset is filtered correctly when a user is selected.""" - who_filter = WhoFilter(request=None, params={"who": "admin"}, model=Entry, model_admin=EntryAdmin) + who_filter = WhoFilter(request=None, params={"who": ["admin"]}, model=Entry, model_admin=EntryAdmin) queryset = Entry.objects.all() filtered_queryset = who_filter.queryset(None, queryset) diff --git a/scram/route_manager/tests/test_websockets.py b/scram/route_manager/tests/test_websockets.py index ca9cd528..00a9bdfa 100644 --- a/scram/route_manager/tests/test_websockets.py +++ b/scram/route_manager/tests/test_websockets.py @@ -8,7 +8,7 @@ from channels.routing import URLRouter from channels.testing import WebsocketCommunicator from django.contrib.auth import get_user_model -from django.test import TestCase +from django.test import TransactionTestCase from django.urls import reverse from config.routing import websocket_urlpatterns @@ -44,7 +44,7 @@ async def get_communicators(actiontypes, should_match, *args, **kwds): await communicator.disconnect() -class TestTranslatorBaseCase(TestCase): +class TestTranslatorBaseCase(TransactionTestCase): """Base case that other test cases build on top of. Three translators in one group, test one v4 and one v6.""" def setUp(self): @@ -52,7 +52,7 @@ def setUp(self): # TODO: This is copied from test_api; should de-dupe this. self.url = reverse("api:v1:entry-list") self.superuser = get_user_model().objects.create_superuser("admin", "admin@example.net", "admintestpassword") - self.client.login(username="admin", password="admintestpassword") + self.client.force_login(self.superuser) self.uuid = "0e7e1cbd-7d73-4968-bc4b-ce3265dc2fd3" self.action_name = "block" diff --git a/scram/users/forms.py b/scram/users/forms.py index b079f2a4..3807fdac 100644 --- a/scram/users/forms.py +++ b/scram/users/forms.py @@ -16,10 +16,10 @@ class Meta(admin_forms.UserChangeForm.Meta): model = User -class UserCreationForm(admin_forms.UserCreationForm): +class UserCreationForm(admin_forms.AdminUserCreationForm): """Define a form to create a User.""" - class Meta(admin_forms.UserCreationForm.Meta): + class Meta(admin_forms.AdminUserCreationForm.Meta): """Map to the User model and provide custom error messages.""" model = User From b00f3f5ab37610f7674894c3ca6032b835adb34d Mon Sep 17 00:00:00 2001 From: crankynetman Date: Fri, 1 May 2026 12:49:34 -0500 Subject: [PATCH 3/5] chore(async): re-add sync_to_async stuff --- config/consumers.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/config/consumers.py b/config/consumers.py index 55118ccf..1b47b6e7 100644 --- a/config/consumers.py +++ b/config/consumers.py @@ -29,7 +29,7 @@ def update_connect_cache(): cache.get_or_set(f"translator_count:{self.actiontype}", 0) cache.incr(f"translator_count:{self.actiontype}") - await sync_to_async(update_connect_cache)() + await database_sync_to_async(update_connect_cache)() # Filter WebSocketSequenceElements by actiontype elements = await database_sync_to_async(list)( @@ -63,7 +63,7 @@ def update_disconnect_cache(): except (ValueError, TypeError): cache.set(f"translator_count:{self.actiontype}", 0) - await sync_to_async(update_disconnect_cache)() + await database_sync_to_async(update_disconnect_cache)() async def receive_json(self, content): """Handle a WebSocket message.""" @@ -77,7 +77,7 @@ async def receive_json(self, content): } cache_key = f"translator_stats:{self.actiontype}" logger.info("Received heartbeat for %s: %s (Key: %s)", self.actiontype, stats, cache_key) - await sync_to_async(cache.set)(cache_key, stats, timeout=300) + await database_sync_to_async(cache.set)(cache_key, stats, timeout=300) elif content["type"] == "translator_check_resp": # We received a check response from a translator, forward to web UI. channel = content.pop("channel") From 9eb42a6d031b4541b9612eaf3215af8187ef136d Mon Sep 17 00:00:00 2001 From: crankynetman Date: Fri, 1 May 2026 12:54:58 -0500 Subject: [PATCH 4/5] chore(deps): bump redis to match compose --- .github/workflows/pytest.yml | 2 +- .github/workflows/pytest_next_python.yml | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/pytest.yml b/.github/workflows/pytest.yml index 7a9ea2fb..7139262f 100644 --- a/.github/workflows/pytest.yml +++ b/.github/workflows/pytest.yml @@ -39,7 +39,7 @@ jobs: --health-retries 5 redis: - image: redis:5.0 + image: redis:8.2.2 ports: - 6379:6379 diff --git a/.github/workflows/pytest_next_python.yml b/.github/workflows/pytest_next_python.yml index 623a1277..b907ebef 100644 --- a/.github/workflows/pytest_next_python.yml +++ b/.github/workflows/pytest_next_python.yml @@ -40,7 +40,7 @@ jobs: --health-retries 5 redis: - image: redis:5.0 + image: redis:8.2.2 ports: - 6379:6379 From 12c56e464406ef051ceec0392b7a8ddb75702a38 Mon Sep 17 00:00:00 2001 From: crankynetman Date: Fri, 1 May 2026 13:11:45 -0500 Subject: [PATCH 5/5] ci(migrations): apply migrations in a simpler way (less awk(ward)) --- .github/workflows/pytest_next_python.yml | 9 +-------- 1 file changed, 1 insertion(+), 8 deletions(-) diff --git a/.github/workflows/pytest_next_python.yml b/.github/workflows/pytest_next_python.yml index b907ebef..cb3e041f 100644 --- a/.github/workflows/pytest_next_python.yml +++ b/.github/workflows/pytest_next_python.yml @@ -65,14 +65,7 @@ jobs: DATABASE_URL: "postgres://scram:@localhost:5432/test_scram" run: | python manage.py makemigrations --noinput || true - UNAPPLIED_MIGRATIONS=$(python manage.py showmigrations --plan | grep '\[ \]' | awk '{print $2}') - if [ -n "$UNAPPLIED_MIGRATIONS" ]; then - for migration in $UNAPPLIED_MIGRATIONS; do - python manage.py migrate $migration --fake-initial --noinput - done - else - echo "No unapplied migrations." - fi + python manage.py migrate --fake-initial --noinput -v 2 - name: Check for duplicate migrations run: |