diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml index 41582a43..ba0aa5d9 100644 --- a/.gitlab-ci.yml +++ b/.gitlab-ci.yml @@ -25,7 +25,7 @@ before_script: pre-commit-check: <<: *only-default stage: pre-commit - image: python:3.11-bullseye + image: python:3.11-bookworm # variables: # PRE_COMMIT_HOME: ${CI_PROJECT_DIR}/.cache/pre-commit # cache: @@ -53,7 +53,7 @@ secret_detection: test-3.8-core: <<: *only-default - image: python:3.8-bullseye + image: python:3.8-bookworm script: - tox -e py38-core artifacts: @@ -65,7 +65,7 @@ test-3.8-core: test-3.9-core: <<: *only-default - image: python:3.9-bullseye + image: python:3.9-bookworm script: - tox -e py39-core artifacts: @@ -77,7 +77,7 @@ test-3.9-core: test-3.10-core: <<: *only-default - image: python:3.10-bullseye + image: python:3.10-bookworm script: - tox -e py310-core artifacts: @@ -89,7 +89,7 @@ test-3.10-core: test-3.11-core: <<: *only-default - image: python:3.11-bullseye + image: python:3.11-bookworm script: - tox -e py311-core artifacts: @@ -101,7 +101,7 @@ test-3.11-core: test-3.12-core: <<: *only-default - image: python:3.12-rc-bullseye + image: python:3.12-bookworm script: - tox -e py312-core artifacts: @@ -113,7 +113,7 @@ test-3.12-core: test-3.8-all: <<: *only-default - image: python:3.8-bullseye + image: python:3.8-bookworm script: - tox -e py38-all artifacts: @@ -125,7 +125,7 @@ test-3.8-all: test-3.9-all: <<: *only-default - image: python:3.9-bullseye + image: python:3.9-bookworm script: - tox -e py39-all artifacts: @@ -137,7 +137,7 @@ test-3.9-all: test-3.10-all: <<: *only-default - image: python:3.10-bullseye + image: python:3.10-bookworm script: - tox -e py310-all artifacts: @@ -149,7 +149,7 @@ test-3.10-all: test-3.11-all: <<: *only-default - image: python:3.11-bullseye + image: python:3.11-bookworm script: - tox -e py311-all artifacts: @@ -162,7 +162,7 @@ test-3.11-all: test-3.12-all: <<: *only-default - image: python:3.12-rc-bullseye + image: python:3.12-bookworm script: - tox -e py312-all artifacts: @@ -174,7 +174,7 @@ test-3.12-all: build-test: stage: test - image: python:3.11-bullseye + image: python:3.11-bookworm before_script: - python -m pip install --upgrade pip @@ -193,13 +193,13 @@ build-test: test-docs: <<: *only-default - image: python:3.11-bullseye + image: python:3.11-bookworm script: - tox -e docs deploy_production: stage: deploy - image: python:3.11-bullseye + image: python:3.11-bookworm before_script: - python -m pip install --upgrade pip diff --git a/allianceauth/apps.py b/allianceauth/apps.py index 053f71f8..098f50ba 100644 --- a/allianceauth/apps.py +++ b/allianceauth/apps.py @@ -5,26 +5,5 @@ from django.core.checks import Warning, Error, register class AllianceAuthConfig(AppConfig): name = 'allianceauth' - -@register() -def check_settings(app_configs, **kwargs): - from django.conf import settings - - errors = [] - if hasattr(settings, "SITE_URL"): - if settings.SITE_URL[-1] == "/": - errors.append(Warning( - "'SITE_URL' Has a trailing slash. This may lead to incorrect links being generated by Auth.")) - else: - errors.append(Error( - "No 'SITE_URL' found is settings. This may lead to incorrect links being generated by Auth or Errors in 3rd party modules.")) - if hasattr(settings, "CSRF_TRUSTED_ORIGINS"): - if hasattr(settings, "SITE_URL"): - if settings.SITE_URL not in settings.CSRF_TRUSTED_ORIGINS: - errors.append(Warning( - "'SITE_URL' not found in 'CSRF_TRUSTED_ORIGINS'. Auth may not load pages correctly until this is rectified.")) - else: - errors.append(Error( - "No 'CSRF_TRUSTED_ORIGINS' found is settings, Auth may not load pages correctly until this is rectified")) - - return errors + def ready(self) -> None: + import allianceauth.checks # noqa diff --git a/allianceauth/checks.py b/allianceauth/checks.py new file mode 100644 index 00000000..82ee858c --- /dev/null +++ b/allianceauth/checks.py @@ -0,0 +1,171 @@ +from typing import List +from django import db +from django.core.checks import CheckMessage, Error, register, Warning +from allianceauth.utils.cache import get_redis_client +from django.utils import timezone +from packaging.version import InvalidVersion, Version as Pep440Version +from celery import current_app +from django.conf import settings +from sqlite3.dbapi2 import sqlite_version_info + +""" +A = System Packages +B = Configuration +""" + + +@register() +def django_settings(app_configs, **kwargs) -> List[CheckMessage]: + errors: List[CheckMessage] = [] + if hasattr(settings, "SITE_URL"): + if settings.SITE_URL[-1] == "/": + errors.append(Warning("'SITE_URL' Has a trailing slash. This may lead to incorrect links being generated by Auth.", hint="", id="allianceauth.checks.B005")) + else: + errors.append(Error("No 'SITE_URL' found is settings. This may lead to incorrect links being generated by Auth or Errors in 3rd party modules.", hint="", id="allianceauth.checks.B006")) + + if hasattr(settings, "CSRF_TRUSTED_ORIGINS") and hasattr(settings, "SITE_URL"): + if settings.SITE_URL not in settings.CSRF_TRUSTED_ORIGINS: + errors.append(Warning("'SITE_URL' not found in 'CSRF_TRUSTED_ORIGINS'. Auth may not load pages correctly until this is rectified.", hint="", id="allianceauth.checks.B007")) + else: + errors.append(Error("No 'CSRF_TRUSTED_ORIGINS' found is settings, Auth may not load pages correctly until this is rectified", hint="", id="allianceauth.checks.B008")) + + return errors + + +@register() +def system_package_redis(app_configs, **kwargs) -> List[CheckMessage]: + errors: List[CheckMessage] = [] + try: + redis_version = Pep440Version(get_redis_client().info()['redis_version']) + except InvalidVersion: + errors.append(Warning("Unable to confirm Redis Version")) + return errors + + if redis_version.major == 7 and redis_version.minor == 2 and timezone.now() > timezone.datetime(year=2025, month=8, day=31, tzinfo=timezone.utc): + errors.append(Error(f"Redis {redis_version.public} in Security Support only, Updating Suggested", hint="https://redis.io/docs/latest/operate/oss_and_stack/install/install-redis/install-redis-on-linux/", id="allianceauth.checks.A001")) + elif redis_version.major == 7 and redis_version.minor == 0: + errors.append(Warning(f"Redis {redis_version.public} in Security Support only, Updating Suggested", hint="https://redis.io/docs/latest/operate/oss_and_stack/install/install-redis/install-redis-on-linux/", id="allianceauth.checks.A002")) + elif redis_version.major == 6 and redis_version.minor == 2: + errors.append(Warning(f"Redis {redis_version.public} in Security Support only, Updating Suggested", hint="https://redis.io/docs/latest/operate/oss_and_stack/install/install-redis/install-redis-on-linux/", id="allianceauth.checks.A018")) + elif redis_version.major in [6, 5]: + errors.append(Error(f"Redis {redis_version.public} EOL", hint="https://redis.io/docs/latest/operate/oss_and_stack/install/install-redis/install-redis-on-linux/", id="allianceauth.checks.A003")) + + return errors + + +@register() +def system_package_mysql(app_configs, **kwargs) -> List[CheckMessage]: + errors: List[CheckMessage] = [] + + for connection in db.connections.all(): + if connection.vendor == "mysql": + try: + mysql_version = Pep440Version(".".join(str(i) for i in connection.mysql_version)) + except InvalidVersion: + errors.append(Warning("Unable to confirm MySQL Version")) + return errors + + # MySQL 8 + if mysql_version.major == 8 and mysql_version.minor == 4 and timezone.now() > timezone.datetime(year=2032, month=4, day=30, tzinfo=timezone.utc): + errors.append(Error(f"MySQL {mysql_version.public} EOL", hint="https://dev.mysql.com/doc/mysql-apt-repo-quick-guide/en/", id="allianceauth.checks.A004")) + elif mysql_version.major == 8 and mysql_version.minor == 3: + errors.append(Warning(f"MySQL {mysql_version.public} Non LTS", hint="https://dev.mysql.com/doc/mysql-apt-repo-quick-guide/en/", id="allianceauth.checks.A005")) + elif mysql_version.major == 8 and mysql_version.minor == 2: + errors.append(Warning(f"MySQL {mysql_version.public} Non LTS", hint="https://dev.mysql.com/doc/mysql-apt-repo-quick-guide/en/", id="allianceauth.checks.A006")) + elif mysql_version.major == 8 and mysql_version.minor == 1: + errors.append(Error(f"MySQL {mysql_version.public} EOL", hint="https://dev.mysql.com/doc/mysql-apt-repo-quick-guide/en/", id="allianceauth.checks.A007")) + elif mysql_version.major == 8 and mysql_version.minor == 0 and timezone.now() > timezone.datetime(year=2026, month=4, day=30, tzinfo=timezone.utc): + errors.append(Error(f"MySQL {mysql_version.public} EOL", hint="https://dev.mysql.com/doc/mysql-apt-repo-quick-guide/en/", id="allianceauth.checks.A008")) + elif mysql_version.major < 8: # This will also catch Mariadb 5.x + errors.append(Error(f"MySQL or MariaDB {mysql_version.public} EOL", hint="https://dev.mysql.com/doc/mysql-apt-repo-quick-guide/en/", id="allianceauth.checks.A009")) + return errors + + +@register() +def system_package_mariadb(app_configs, **kwargs) -> List[CheckMessage]: + errors: List[CheckMessage] = [] + + for connection in db.connections.all(): + if connection.vendor == "mysql": # Still to find a way to determine MySQL vs MariaDB + try: + mariadb_version = Pep440Version(".".join(str(i) for i in connection.mysql_version)) + except InvalidVersion: + errors.append(Warning("Unable to confirm MariaDB Version")) + return errors + + # MariaDB 11 + if mariadb_version.major == 11 and mariadb_version.minor == 4 and timezone.now() > timezone.datetime(year=2029, month=5, day=19, tzinfo=timezone.utc): + errors.append(Error(f"MariaDB {mariadb_version.public} EOL", hint="https://mariadb.org/download/?t=repo-config", id="allianceauth.checks.A010")) + elif mariadb_version.major == 11 and mariadb_version.minor == 2: + errors.append(Warning(f"MariaDB {mariadb_version.public} Non LTS", hint="https://mariadb.org/download/?t=repo-config", id="allianceauth.checks.A018")) + if timezone.now() > timezone.datetime(year=2024, month=11, day=21, tzinfo=timezone.utc): + errors.append(Error(f"MariaDB {mariadb_version.public} EOL", hint="https://mariadb.org/download/?t=repo-config", id="allianceauth.checks.A011")) + elif mariadb_version.major == 11 and mariadb_version.minor == 1: + errors.append(Warning(f"MariaDB {mariadb_version.public} Non LTS", hint="https://mariadb.org/download/?t=repo-config", id="allianceauth.checks.A019")) + if timezone.now() > timezone.datetime(year=2024, month=8, day=21, tzinfo=timezone.utc): + errors.append(Error(f"MariaDB {mariadb_version.public} EOL", hint="https://mariadb.org/download/?t=repo-config", id="allianceauth.checks.A012")) + elif mariadb_version.major == 11 and mariadb_version.minor in [0, 3]: # Demote versions down here once EOL + errors.append(Error(f"MariaDB {mariadb_version.public} EOL", hint="https://mariadb.org/download/?t=repo-config.", id="allianceauth.checks.A013")) + + # MariaDB 10 + elif mariadb_version.major == 10 and mariadb_version.minor == 11 and timezone.now() > timezone.datetime(year=2028, month=2, day=10, tzinfo=timezone.utc): + errors.append(Error(f"MariaDB {mariadb_version.public} EOL", hint="https://mariadb.org/download/?t=repo-config.", id="allianceauth.checks.A014")) + elif mariadb_version.major == 10 and mariadb_version.minor == 6 and timezone.now() > timezone.datetime(year=2026, month=7, day=6, tzinfo=timezone.utc): + errors.append(Error(f"MariaDB {mariadb_version.public} EOL", hint="https://mariadb.org/download/?t=repo-config", id="allianceauth.checks.A0015")) + elif mariadb_version.major == 10 and mariadb_version.minor == 5 and timezone.now() > timezone.datetime(year=2025, month=6, day=24, tzinfo=timezone.utc): + errors.append(Error(f"MariaDB {mariadb_version.public} EOL", hint="https://mariadb.org/download/?t=repo-config", id="allianceauth.checks.A016")) + elif mariadb_version.major == 10 and mariadb_version.minor in [0, 1, 2, 3, 4, 7, 9, 10]: # Demote versions down here once EOL + errors.append(Error(f"MariaDB {mariadb_version.public} EOL", hint="https://mariadb.org/download/?t=repo-config", id="allianceauth.checks.A017")) + + return errors + + +@register() +def system_package_sqlite(app_configs, **kwargs) -> List[CheckMessage]: + errors: List[CheckMessage] = [] + for connection in db.connections.all(): + if connection.vendor == "sqlite": + try: + sqlite_version = Pep440Version(".".join(str(i) for i in sqlite_version_info)) + except InvalidVersion: + errors.append(Warning("Unable to confirm SQLite Version")) + return errors + if sqlite_version.major == 3 and sqlite_version.minor < 27: + errors.append(Error(f"SQLite {sqlite_version.public} Unsupported by Django", hint="https://pkgs.org/download/sqlite3", id="allianceauth.checks.A020")) + return errors + + +@register() +def sql_settings(app_configs, **kwargs) -> List[CheckMessage]: + errors: List[CheckMessage] = [] + for connection in db.connections.all(): + if connection.vendor == "mysql": + if connection.settings_dict["OPTIONS"]["charset"] != "utf8mb4": + errors.append(Error("SQL Charset is not set correctly", hint="https://gitlab.com/allianceauth/allianceauth/-/commit/89be2456fb2d741b86417e889da9b6129525bec8", id="allianceauth.checks.B001")) + # This hasn't actually been set on AA yet + # if connection.settings_dict["OPTIONS"]["charset"] != "utf8mb4_unicode_ci": + # errors.append(Error("SQL Collation is not set correctly", hint="", id="allianceauth.checks.B002")) + # if connection.vendor == "sqlite": + + return errors + + +@register() +def celery_settings(app_configs, **kwargs) -> List[CheckMessage]: + errors: List[CheckMessage] = [] + if current_app.conf.broker_transport_options != {'priority_steps': [0, 1, 2, 3, 4, 5, 6, 7, 8, 9], 'queue_order_strategy': 'priority'}: + errors.append(Error("Celery Priorities are not set correctly", hint="https://gitlab.com/allianceauth/allianceauth/-/commit/8861ec0a61790eca0261f1adc1cc04ca5f243cbc", id="allianceauth.checks.B003")) + + if current_app.conf.broker_connection_retry_on_startup != True: + errors.append(Error("Celery broker_connection_retry_on_startup not set correctly", hint="https://gitlab.com/allianceauth/allianceauth/-/commit/380c41400b535447839e5552df2410af35a75280", id="allianceauth.checks.B004")) + return errors + + +# IDEAS + +# Any other celery things weve manually changed over the years +# I'd be happy to add Community App checks, old versions the owners dont want to support etc. + + +# Check Default Collation on DB +# Check Charset Collation on all tables