mirror of
https://gitlab.com/allianceauth/allianceauth.git
synced 2026-02-13 10:36:25 +01:00
Compare commits
14 Commits
v4.11.1
...
63fb449060
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
63fb449060 | ||
|
|
235675fa9b | ||
|
|
a065f043eb | ||
|
|
0839920032 | ||
|
|
29ad4acff7 | ||
|
|
3a5b84d1f9 | ||
|
|
bbcb94021e | ||
|
|
d50f13528b | ||
|
|
c88521af88 | ||
|
|
2bd5ff8723 | ||
|
|
84484cebcb | ||
|
|
5ee34fcb2d | ||
|
|
046473def1 | ||
|
|
6aaba2bf3d |
@@ -51,30 +51,6 @@ secret_detection:
|
|||||||
stage: gitlab
|
stage: gitlab
|
||||||
before_script: []
|
before_script: []
|
||||||
|
|
||||||
test-3.8-core:
|
|
||||||
<<: *only-default
|
|
||||||
image: python:3.8-bookworm
|
|
||||||
script:
|
|
||||||
- tox -e py38-core
|
|
||||||
artifacts:
|
|
||||||
when: always
|
|
||||||
reports:
|
|
||||||
coverage_report:
|
|
||||||
coverage_format: cobertura
|
|
||||||
path: coverage.xml
|
|
||||||
|
|
||||||
test-3.9-core:
|
|
||||||
<<: *only-default
|
|
||||||
image: python:3.9-bookworm
|
|
||||||
script:
|
|
||||||
- tox -e py39-core
|
|
||||||
artifacts:
|
|
||||||
when: always
|
|
||||||
reports:
|
|
||||||
coverage_report:
|
|
||||||
coverage_format: cobertura
|
|
||||||
path: coverage.xml
|
|
||||||
|
|
||||||
test-3.10-core:
|
test-3.10-core:
|
||||||
<<: *only-default
|
<<: *only-default
|
||||||
image: python:3.10-bookworm
|
image: python:3.10-bookworm
|
||||||
@@ -111,29 +87,18 @@ test-3.12-core:
|
|||||||
coverage_format: cobertura
|
coverage_format: cobertura
|
||||||
path: coverage.xml
|
path: coverage.xml
|
||||||
|
|
||||||
test-3.8-all:
|
test-3.13-core:
|
||||||
<<: *only-default
|
<<: *only-default
|
||||||
image: python:3.8-bookworm
|
image: python:3.13-rc-bookworm
|
||||||
script:
|
script:
|
||||||
- tox -e py38-all
|
- tox -e py313-core
|
||||||
artifacts:
|
|
||||||
when: always
|
|
||||||
reports:
|
|
||||||
coverage_report:
|
|
||||||
coverage_format: cobertura
|
|
||||||
path: coverage.xml
|
|
||||||
|
|
||||||
test-3.9-all:
|
|
||||||
<<: *only-default
|
|
||||||
image: python:3.9-bookworm
|
|
||||||
script:
|
|
||||||
- tox -e py39-all
|
|
||||||
artifacts:
|
artifacts:
|
||||||
when: always
|
when: always
|
||||||
reports:
|
reports:
|
||||||
coverage_report:
|
coverage_report:
|
||||||
coverage_format: cobertura
|
coverage_format: cobertura
|
||||||
path: coverage.xml
|
path: coverage.xml
|
||||||
|
allow_failure: true
|
||||||
|
|
||||||
test-3.10-all:
|
test-3.10-all:
|
||||||
<<: *only-default
|
<<: *only-default
|
||||||
@@ -172,9 +137,22 @@ test-3.12-all:
|
|||||||
coverage_format: cobertura
|
coverage_format: cobertura
|
||||||
path: coverage.xml
|
path: coverage.xml
|
||||||
|
|
||||||
|
test-3.13-all:
|
||||||
|
<<: *only-default
|
||||||
|
image: python:3.13-rc-bookworm
|
||||||
|
script:
|
||||||
|
- tox -e py313-all
|
||||||
|
artifacts:
|
||||||
|
when: always
|
||||||
|
reports:
|
||||||
|
coverage_report:
|
||||||
|
coverage_format: cobertura
|
||||||
|
path: coverage.xml
|
||||||
|
allow_failure: true
|
||||||
|
|
||||||
build-test:
|
build-test:
|
||||||
stage: test
|
stage: test
|
||||||
image: python:3.11-bookworm
|
image: python:3.12-bookworm
|
||||||
|
|
||||||
before_script:
|
before_script:
|
||||||
- python -m pip install --upgrade pip
|
- python -m pip install --upgrade pip
|
||||||
@@ -193,13 +171,13 @@ build-test:
|
|||||||
|
|
||||||
test-docs:
|
test-docs:
|
||||||
<<: *only-default
|
<<: *only-default
|
||||||
image: python:3.11-bookworm
|
image: python:3.12-bookworm
|
||||||
script:
|
script:
|
||||||
- tox -e docs
|
- tox -e docs
|
||||||
|
|
||||||
deploy_production:
|
deploy_production:
|
||||||
stage: deploy
|
stage: deploy
|
||||||
image: python:3.11-bookworm
|
image: python:3.12-bookworm
|
||||||
|
|
||||||
before_script:
|
before_script:
|
||||||
- python -m pip install --upgrade pip
|
- python -m pip install --upgrade pip
|
||||||
@@ -215,10 +193,10 @@ deploy_production:
|
|||||||
|
|
||||||
build-image:
|
build-image:
|
||||||
before_script: []
|
before_script: []
|
||||||
image: docker:24.0
|
image: docker:27.0
|
||||||
stage: docker
|
stage: docker
|
||||||
services:
|
services:
|
||||||
- docker:24.0-dind
|
- docker:27-dind
|
||||||
script: |
|
script: |
|
||||||
CURRENT_DATE=$(echo $CI_COMMIT_TIMESTAMP | head -c 10 | tr -d -)
|
CURRENT_DATE=$(echo $CI_COMMIT_TIMESTAMP | head -c 10 | tr -d -)
|
||||||
IMAGE_TAG=$CI_REGISTRY_IMAGE/auth:$CURRENT_DATE-$CI_COMMIT_SHORT_SHA
|
IMAGE_TAG=$CI_REGISTRY_IMAGE/auth:$CURRENT_DATE-$CI_COMMIT_SHORT_SHA
|
||||||
@@ -239,10 +217,10 @@ build-image:
|
|||||||
|
|
||||||
build-image-dev:
|
build-image-dev:
|
||||||
before_script: []
|
before_script: []
|
||||||
image: docker:24.0
|
image: docker:27
|
||||||
stage: docker
|
stage: docker
|
||||||
services:
|
services:
|
||||||
- docker:24.0-dind
|
- docker:27-dind
|
||||||
script: |
|
script: |
|
||||||
CURRENT_DATE=$(echo $CI_COMMIT_TIMESTAMP | head -c 10 | tr -d -)
|
CURRENT_DATE=$(echo $CI_COMMIT_TIMESTAMP | head -c 10 | tr -d -)
|
||||||
IMAGE_TAG=$CI_REGISTRY_IMAGE/auth:$CURRENT_DATE-$CI_COMMIT_BRANCH-$CI_COMMIT_SHORT_SHA
|
IMAGE_TAG=$CI_REGISTRY_IMAGE/auth:$CURRENT_DATE-$CI_COMMIT_BRANCH-$CI_COMMIT_SHORT_SHA
|
||||||
@@ -260,10 +238,10 @@ build-image-dev:
|
|||||||
|
|
||||||
build-image-mr:
|
build-image-mr:
|
||||||
before_script: []
|
before_script: []
|
||||||
image: docker:24.0
|
image: docker:27
|
||||||
stage: docker
|
stage: docker
|
||||||
services:
|
services:
|
||||||
- docker:24.0-dind
|
- docker:27-dind
|
||||||
script: |
|
script: |
|
||||||
CURRENT_DATE=$(echo $CI_COMMIT_TIMESTAMP | head -c 10 | tr -d -)
|
CURRENT_DATE=$(echo $CI_COMMIT_TIMESTAMP | head -c 10 | tr -d -)
|
||||||
IMAGE_TAG=$CI_REGISTRY_IMAGE/auth:$CURRENT_DATE-$CI_MERGE_REQUEST_SOURCE_BRANCH_NAME-$CI_COMMIT_SHORT_SHA
|
IMAGE_TAG=$CI_REGISTRY_IMAGE/auth:$CURRENT_DATE-$CI_MERGE_REQUEST_SOURCE_BRANCH_NAME-$CI_COMMIT_SHORT_SHA
|
||||||
|
|||||||
@@ -9,7 +9,7 @@ repos:
|
|||||||
rev: v3.15.2
|
rev: v3.15.2
|
||||||
hooks:
|
hooks:
|
||||||
- id: pyupgrade
|
- id: pyupgrade
|
||||||
args: [--py38-plus]
|
args: [--py310-plus]
|
||||||
- repo: https://github.com/adamchainz/django-upgrade
|
- repo: https://github.com/adamchainz/django-upgrade
|
||||||
rev: 1.17.0
|
rev: 1.17.0
|
||||||
hooks:
|
hooks:
|
||||||
|
|||||||
@@ -7,11 +7,11 @@ version: 2
|
|||||||
|
|
||||||
# Set the version of Python and other tools you might need
|
# Set the version of Python and other tools you might need
|
||||||
build:
|
build:
|
||||||
os: ubuntu-22.04
|
os: ubuntu-24.04
|
||||||
apt_packages:
|
apt_packages:
|
||||||
- redis
|
- redis
|
||||||
tools:
|
tools:
|
||||||
python: "3.11"
|
python: "3.12"
|
||||||
jobs:
|
jobs:
|
||||||
post_system_dependencies:
|
post_system_dependencies:
|
||||||
- redis-server --daemonize yes
|
- redis-server --daemonize yes
|
||||||
|
|||||||
@@ -12,7 +12,7 @@ from django.conf import settings
|
|||||||
logger = logging.getLogger(__name__)
|
logger = logging.getLogger(__name__)
|
||||||
|
|
||||||
|
|
||||||
def active_tasks_count() -> Optional[int]:
|
def active_tasks_count() -> int | None:
|
||||||
"""Return count of currently active tasks
|
"""Return count of currently active tasks
|
||||||
or None if celery workers are not online.
|
or None if celery workers are not online.
|
||||||
"""
|
"""
|
||||||
@@ -20,7 +20,7 @@ def active_tasks_count() -> Optional[int]:
|
|||||||
return _tasks_count(inspect.active())
|
return _tasks_count(inspect.active())
|
||||||
|
|
||||||
|
|
||||||
def _tasks_count(data: dict) -> Optional[int]:
|
def _tasks_count(data: dict) -> int | None:
|
||||||
"""Return count of tasks in data from celery inspect API."""
|
"""Return count of tasks in data from celery inspect API."""
|
||||||
try:
|
try:
|
||||||
tasks = itertools.chain(*data.values())
|
tasks = itertools.chain(*data.values())
|
||||||
@@ -29,7 +29,7 @@ def _tasks_count(data: dict) -> Optional[int]:
|
|||||||
return len(list(tasks))
|
return len(list(tasks))
|
||||||
|
|
||||||
|
|
||||||
def queued_tasks_count() -> Optional[int]:
|
def queued_tasks_count() -> int | None:
|
||||||
"""Return count of queued tasks. Return None if there was an error."""
|
"""Return count of queued tasks. Return None if there was an error."""
|
||||||
try:
|
try:
|
||||||
with current_app.connection_or_acquire() as conn:
|
with current_app.connection_or_acquire() as conn:
|
||||||
|
|||||||
@@ -2,7 +2,8 @@ from django.urls import include
|
|||||||
from django.contrib.auth.decorators import user_passes_test
|
from django.contrib.auth.decorators import user_passes_test
|
||||||
from django.core.exceptions import PermissionDenied
|
from django.core.exceptions import PermissionDenied
|
||||||
from functools import wraps
|
from functools import wraps
|
||||||
from typing import Callable, Iterable, Optional
|
from typing import Optional
|
||||||
|
from collections.abc import Callable, Iterable
|
||||||
|
|
||||||
from django.urls import include
|
from django.urls import include
|
||||||
from django.contrib import messages
|
from django.contrib import messages
|
||||||
@@ -17,7 +18,7 @@ def user_has_main_character(user):
|
|||||||
|
|
||||||
|
|
||||||
def decorate_url_patterns(
|
def decorate_url_patterns(
|
||||||
urls, decorator: Callable, excluded_views: Optional[Iterable] = None
|
urls, decorator: Callable, excluded_views: Iterable | None = None
|
||||||
):
|
):
|
||||||
"""Decorate views given in url patterns except when they are explicitly excluded.
|
"""Decorate views given in url patterns except when they are explicitly excluded.
|
||||||
|
|
||||||
|
|||||||
@@ -16,7 +16,7 @@ class _TaskCounts(NamedTuple):
|
|||||||
retried: int
|
retried: int
|
||||||
failed: int
|
failed: int
|
||||||
total: int
|
total: int
|
||||||
earliest_task: Optional[dt.datetime]
|
earliest_task: dt.datetime | None
|
||||||
hours: int
|
hours: int
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -17,7 +17,7 @@ class EventSeries:
|
|||||||
|
|
||||||
_ROOT_KEY = "ALLIANCEAUTH_EVENT_SERIES"
|
_ROOT_KEY = "ALLIANCEAUTH_EVENT_SERIES"
|
||||||
|
|
||||||
def __init__(self, key_id: str, redis: Optional[Redis] = None) -> None:
|
def __init__(self, key_id: str, redis: Redis | None = None) -> None:
|
||||||
self._redis = get_redis_client_or_stub() if not redis else redis
|
self._redis = get_redis_client_or_stub() if not redis else redis
|
||||||
self._key_id = str(key_id)
|
self._key_id = str(key_id)
|
||||||
self.clear()
|
self.clear()
|
||||||
@@ -46,7 +46,7 @@ class EventSeries:
|
|||||||
my_id = self._redis.incr(self._key_counter)
|
my_id = self._redis.incr(self._key_counter)
|
||||||
self._redis.zadd(self._key_sorted_set, {my_id: event_time.timestamp()})
|
self._redis.zadd(self._key_sorted_set, {my_id: event_time.timestamp()})
|
||||||
|
|
||||||
def all(self) -> List[dt.datetime]:
|
def all(self) -> list[dt.datetime]:
|
||||||
"""List of all known events."""
|
"""List of all known events."""
|
||||||
return [
|
return [
|
||||||
event[1]
|
event[1]
|
||||||
@@ -75,7 +75,7 @@ class EventSeries:
|
|||||||
maximum = "+inf" if not latest else latest.timestamp()
|
maximum = "+inf" if not latest else latest.timestamp()
|
||||||
return self._redis.zcount(self._key_sorted_set, min=minimum, max=maximum)
|
return self._redis.zcount(self._key_sorted_set, min=minimum, max=maximum)
|
||||||
|
|
||||||
def first_event(self, earliest: dt.datetime = None) -> Optional[dt.datetime]:
|
def first_event(self, earliest: dt.datetime = None) -> dt.datetime | None:
|
||||||
"""Date/Time of first event. Returns `None` if series has no events.
|
"""Date/Time of first event. Returns `None` if series has no events.
|
||||||
|
|
||||||
Args:
|
Args:
|
||||||
|
|||||||
@@ -1,4 +1,3 @@
|
|||||||
from typing import List
|
|
||||||
from django import db
|
from django import db
|
||||||
from django.core.checks import CheckMessage, Error, register, Warning
|
from django.core.checks import CheckMessage, Error, register, Warning
|
||||||
from allianceauth.utils.cache import get_redis_client
|
from allianceauth.utils.cache import get_redis_client
|
||||||
@@ -7,7 +6,7 @@ from packaging.version import InvalidVersion, Version as Pep440Version
|
|||||||
from celery import current_app
|
from celery import current_app
|
||||||
from django.conf import settings
|
from django.conf import settings
|
||||||
from sqlite3.dbapi2 import sqlite_version_info
|
from sqlite3.dbapi2 import sqlite_version_info
|
||||||
|
import datetime
|
||||||
"""
|
"""
|
||||||
A = System Packages
|
A = System Packages
|
||||||
B = Configuration
|
B = Configuration
|
||||||
@@ -15,8 +14,8 @@ B = Configuration
|
|||||||
|
|
||||||
|
|
||||||
@register()
|
@register()
|
||||||
def django_settings(app_configs, **kwargs) -> List[CheckMessage]:
|
def django_settings(app_configs, **kwargs) -> list[CheckMessage]:
|
||||||
errors: List[CheckMessage] = []
|
errors: list[CheckMessage] = []
|
||||||
if hasattr(settings, "SITE_URL"):
|
if hasattr(settings, "SITE_URL"):
|
||||||
if settings.SITE_URL[-1] == "/":
|
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"))
|
errors.append(Warning("'SITE_URL' Has a trailing slash. This may lead to incorrect links being generated by Auth.", hint="", id="allianceauth.checks.B005"))
|
||||||
@@ -33,15 +32,15 @@ def django_settings(app_configs, **kwargs) -> List[CheckMessage]:
|
|||||||
|
|
||||||
|
|
||||||
@register()
|
@register()
|
||||||
def system_package_redis(app_configs, **kwargs) -> List[CheckMessage]:
|
def system_package_redis(app_configs, **kwargs) -> list[CheckMessage]:
|
||||||
errors: List[CheckMessage] = []
|
errors: list[CheckMessage] = []
|
||||||
try:
|
try:
|
||||||
redis_version = Pep440Version(get_redis_client().info()['redis_version'])
|
redis_version = Pep440Version(get_redis_client().info()['redis_version'])
|
||||||
except InvalidVersion:
|
except InvalidVersion:
|
||||||
errors.append(Warning("Unable to confirm Redis Version"))
|
errors.append(Warning("Unable to confirm Redis Version"))
|
||||||
return errors
|
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):
|
if redis_version.major == 7 and redis_version.minor == 2 and timezone.now() > timezone.datetime(year=2025, month=8, day=31, tzinfo=datetime.timezone.utc):
|
||||||
errors.append(Error(f"Redis {redis_version.public} in Security Support only, Updating Suggested", hint="https://allianceauth.readthedocs.io/en/latest/installation/allianceauth.html#redis-and-other-tools", id="allianceauth.checks.A001"))
|
errors.append(Error(f"Redis {redis_version.public} in Security Support only, Updating Suggested", hint="https://allianceauth.readthedocs.io/en/latest/installation/allianceauth.html#redis-and-other-tools", id="allianceauth.checks.A001"))
|
||||||
elif redis_version.major == 7 and redis_version.minor == 0:
|
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://allianceauth.readthedocs.io/en/latest/installation/allianceauth.html#redis-and-other-tools", id="allianceauth.checks.A002"))
|
errors.append(Warning(f"Redis {redis_version.public} in Security Support only, Updating Suggested", hint="https://allianceauth.readthedocs.io/en/latest/installation/allianceauth.html#redis-and-other-tools", id="allianceauth.checks.A002"))
|
||||||
@@ -54,8 +53,8 @@ def system_package_redis(app_configs, **kwargs) -> List[CheckMessage]:
|
|||||||
|
|
||||||
|
|
||||||
@register()
|
@register()
|
||||||
def system_package_mysql(app_configs, **kwargs) -> List[CheckMessage]:
|
def system_package_mysql(app_configs, **kwargs) -> list[CheckMessage]:
|
||||||
errors: List[CheckMessage] = []
|
errors: list[CheckMessage] = []
|
||||||
|
|
||||||
for connection in db.connections.all():
|
for connection in db.connections.all():
|
||||||
if connection.vendor == "mysql":
|
if connection.vendor == "mysql":
|
||||||
@@ -66,7 +65,7 @@ def system_package_mysql(app_configs, **kwargs) -> List[CheckMessage]:
|
|||||||
return errors
|
return errors
|
||||||
|
|
||||||
# MySQL 8
|
# 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):
|
if mysql_version.major == 8 and mysql_version.minor == 4 and timezone.now() > timezone.datetime(year=2032, month=4, day=30, tzinfo=datetime.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"))
|
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:
|
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"))
|
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"))
|
||||||
@@ -74,7 +73,7 @@ def system_package_mysql(app_configs, **kwargs) -> List[CheckMessage]:
|
|||||||
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"))
|
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:
|
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"))
|
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):
|
elif mysql_version.major == 8 and mysql_version.minor == 0 and timezone.now() > timezone.datetime(year=2026, month=4, day=30, tzinfo=datetime.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"))
|
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
|
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"))
|
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"))
|
||||||
@@ -82,8 +81,8 @@ def system_package_mysql(app_configs, **kwargs) -> List[CheckMessage]:
|
|||||||
|
|
||||||
|
|
||||||
@register()
|
@register()
|
||||||
def system_package_mariadb(app_configs, **kwargs) -> List[CheckMessage]:
|
def system_package_mariadb(app_configs, **kwargs) -> list[CheckMessage]:
|
||||||
errors: List[CheckMessage] = []
|
errors: list[CheckMessage] = []
|
||||||
|
|
||||||
for connection in db.connections.all():
|
for connection in db.connections.all():
|
||||||
if connection.vendor == "mysql": # Still to find a way to determine MySQL vs MariaDB
|
if connection.vendor == "mysql": # Still to find a way to determine MySQL vs MariaDB
|
||||||
@@ -94,25 +93,25 @@ def system_package_mariadb(app_configs, **kwargs) -> List[CheckMessage]:
|
|||||||
return errors
|
return errors
|
||||||
|
|
||||||
# MariaDB 11
|
# 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):
|
if mariadb_version.major == 11 and mariadb_version.minor == 4 and timezone.now() > timezone.datetime(year=2029, month=5, day=19, tzinfo=datetime.timezone.utc):
|
||||||
errors.append(Error(f"MariaDB {mariadb_version.public} EOL", hint="https://mariadb.org/download/?t=repo-config", id="allianceauth.checks.A010"))
|
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:
|
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"))
|
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):
|
if timezone.now() > timezone.datetime(year=2024, month=11, day=21, tzinfo=datetime.timezone.utc):
|
||||||
errors.append(Error(f"MariaDB {mariadb_version.public} EOL", hint="https://mariadb.org/download/?t=repo-config", id="allianceauth.checks.A011"))
|
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:
|
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"))
|
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):
|
if timezone.now() > timezone.datetime(year=2024, month=8, day=21, tzinfo=datetime.timezone.utc):
|
||||||
errors.append(Error(f"MariaDB {mariadb_version.public} EOL", hint="https://mariadb.org/download/?t=repo-config", id="allianceauth.checks.A012"))
|
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
|
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"))
|
errors.append(Error(f"MariaDB {mariadb_version.public} EOL", hint="https://mariadb.org/download/?t=repo-config.", id="allianceauth.checks.A013"))
|
||||||
|
|
||||||
# MariaDB 10
|
# 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):
|
elif mariadb_version.major == 10 and mariadb_version.minor == 11 and timezone.now() > timezone.datetime(year=2028, month=2, day=10, tzinfo=datetime.timezone.utc):
|
||||||
errors.append(Error(f"MariaDB {mariadb_version.public} EOL", hint="https://mariadb.org/download/?t=repo-config.", id="allianceauth.checks.A014"))
|
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):
|
elif mariadb_version.major == 10 and mariadb_version.minor == 6 and timezone.now() > timezone.datetime(year=2026, month=7, day=6, tzinfo=datetime.timezone.utc):
|
||||||
errors.append(Error(f"MariaDB {mariadb_version.public} EOL", hint="https://mariadb.org/download/?t=repo-config", id="allianceauth.checks.A0015"))
|
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):
|
elif mariadb_version.major == 10 and mariadb_version.minor == 5 and timezone.now() > timezone.datetime(year=2025, month=6, day=24, tzinfo=datetime.timezone.utc):
|
||||||
errors.append(Error(f"MariaDB {mariadb_version.public} EOL", hint="https://mariadb.org/download/?t=repo-config", id="allianceauth.checks.A016"))
|
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
|
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"))
|
errors.append(Error(f"MariaDB {mariadb_version.public} EOL", hint="https://mariadb.org/download/?t=repo-config", id="allianceauth.checks.A017"))
|
||||||
@@ -121,8 +120,8 @@ def system_package_mariadb(app_configs, **kwargs) -> List[CheckMessage]:
|
|||||||
|
|
||||||
|
|
||||||
@register()
|
@register()
|
||||||
def system_package_sqlite(app_configs, **kwargs) -> List[CheckMessage]:
|
def system_package_sqlite(app_configs, **kwargs) -> list[CheckMessage]:
|
||||||
errors: List[CheckMessage] = []
|
errors: list[CheckMessage] = []
|
||||||
for connection in db.connections.all():
|
for connection in db.connections.all():
|
||||||
if connection.vendor == "sqlite":
|
if connection.vendor == "sqlite":
|
||||||
try:
|
try:
|
||||||
@@ -136,8 +135,8 @@ def system_package_sqlite(app_configs, **kwargs) -> List[CheckMessage]:
|
|||||||
|
|
||||||
|
|
||||||
@register()
|
@register()
|
||||||
def sql_settings(app_configs, **kwargs) -> List[CheckMessage]:
|
def sql_settings(app_configs, **kwargs) -> list[CheckMessage]:
|
||||||
errors: List[CheckMessage] = []
|
errors: list[CheckMessage] = []
|
||||||
for connection in db.connections.all():
|
for connection in db.connections.all():
|
||||||
if connection.vendor == "mysql":
|
if connection.vendor == "mysql":
|
||||||
try:
|
try:
|
||||||
@@ -159,8 +158,8 @@ def sql_settings(app_configs, **kwargs) -> List[CheckMessage]:
|
|||||||
|
|
||||||
|
|
||||||
@register()
|
@register()
|
||||||
def celery_settings(app_configs, **kwargs) -> List[CheckMessage]:
|
def celery_settings(app_configs, **kwargs) -> list[CheckMessage]:
|
||||||
errors: List[CheckMessage] = []
|
errors: list[CheckMessage] = []
|
||||||
|
|
||||||
try:
|
try:
|
||||||
if current_app.conf.broker_transport_options != {'priority_steps': [0, 1, 2, 3, 4, 5, 6, 7, 8, 9], 'queue_order_strategy': 'priority'}:
|
if current_app.conf.broker_transport_options != {'priority_steps': [0, 1, 2, 3, 4, 5, 6, 7, 8, 9], 'queue_order_strategy': 'priority'}:
|
||||||
|
|||||||
@@ -235,7 +235,7 @@ class EveCharacter(models.Model):
|
|||||||
return self.corporation_id == DOOMHEIM_CORPORATION_ID
|
return self.corporation_id == DOOMHEIM_CORPORATION_ID
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def alliance(self) -> Union[EveAllianceInfo, None]:
|
def alliance(self) -> EveAllianceInfo | None:
|
||||||
"""
|
"""
|
||||||
Pseudo foreign key from alliance_id to EveAllianceInfo
|
Pseudo foreign key from alliance_id to EveAllianceInfo
|
||||||
:raises: EveAllianceInfo.DoesNotExist
|
:raises: EveAllianceInfo.DoesNotExist
|
||||||
@@ -255,7 +255,7 @@ class EveCharacter(models.Model):
|
|||||||
return EveCorporationInfo.objects.get(corporation_id=self.corporation_id)
|
return EveCorporationInfo.objects.get(corporation_id=self.corporation_id)
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def faction(self) -> Union[EveFactionInfo, None]:
|
def faction(self) -> EveFactionInfo | None:
|
||||||
"""
|
"""
|
||||||
Pseudo foreign key from faction_id to EveFactionInfo
|
Pseudo foreign key from faction_id to EveFactionInfo
|
||||||
:raises: EveFactionInfo.DoesNotExist
|
:raises: EveFactionInfo.DoesNotExist
|
||||||
|
|||||||
@@ -13,7 +13,7 @@ from allianceauth.framework.api.user import get_sentinel_user
|
|||||||
|
|
||||||
def get_main_character_from_evecharacter(
|
def get_main_character_from_evecharacter(
|
||||||
character: EveCharacter,
|
character: EveCharacter,
|
||||||
) -> Optional[EveCharacter]:
|
) -> EveCharacter | None:
|
||||||
"""
|
"""
|
||||||
Get the main character for a given EveCharacter or None when no main character is set
|
Get the main character for a given EveCharacter or None when no main character is set
|
||||||
|
|
||||||
|
|||||||
@@ -34,7 +34,7 @@ def get_all_characters_from_user(user: User) -> list:
|
|||||||
return characters
|
return characters
|
||||||
|
|
||||||
|
|
||||||
def get_main_character_from_user(user: User) -> Optional[EveCharacter]:
|
def get_main_character_from_user(user: User) -> EveCharacter | None:
|
||||||
"""
|
"""
|
||||||
Get the main character from a user
|
Get the main character from a user
|
||||||
|
|
||||||
|
|||||||
@@ -181,7 +181,7 @@ class AuthGroup(models.Model):
|
|||||||
def __str__(self):
|
def __str__(self):
|
||||||
return self.group.name
|
return self.group.name
|
||||||
|
|
||||||
def group_request_approvers(self) -> Set[User]:
|
def group_request_approvers(self) -> set[User]:
|
||||||
"""Return all users who can approve a group request."""
|
"""Return all users who can approve a group request."""
|
||||||
return set(
|
return set(
|
||||||
self.group_leaders.all()
|
self.group_leaders.all()
|
||||||
|
|||||||
@@ -5,7 +5,7 @@ from typing import Optional
|
|||||||
|
|
||||||
class ApplicationManager(models.Manager):
|
class ApplicationManager(models.Manager):
|
||||||
|
|
||||||
def pending_requests_count_for_user(self, user: User) -> Optional[int]:
|
def pending_requests_count_for_user(self, user: User) -> int | None:
|
||||||
"""Returns the number of pending group requests for the given user"""
|
"""Returns the number of pending group requests for the given user"""
|
||||||
if user.is_superuser:
|
if user.is_superuser:
|
||||||
return self.filter(approved__isnull=True).count()
|
return self.filter(approved__isnull=True).count()
|
||||||
|
|||||||
@@ -35,12 +35,12 @@ class MenuItemAdmin(admin.ModelAdmin):
|
|||||||
]
|
]
|
||||||
ordering = ["parent", "order", "text"]
|
ordering = ["parent", "order", "text"]
|
||||||
|
|
||||||
def get_form(self, request: HttpRequest, obj: Optional[MenuItem] = None, **kwargs):
|
def get_form(self, request: HttpRequest, obj: MenuItem | None = None, **kwargs):
|
||||||
kwargs["form"] = self._choose_form(request, obj)
|
kwargs["form"] = self._choose_form(request, obj)
|
||||||
return super().get_form(request, obj, **kwargs)
|
return super().get_form(request, obj, **kwargs)
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def _choose_form(cls, request: HttpRequest, obj: Optional[MenuItem]):
|
def _choose_form(cls, request: HttpRequest, obj: MenuItem | None):
|
||||||
"""Return the form for the current menu item type."""
|
"""Return the form for the current menu item type."""
|
||||||
if obj: # change
|
if obj: # change
|
||||||
if obj.hook_hash:
|
if obj.hook_hash:
|
||||||
@@ -104,7 +104,7 @@ class MenuItemAdmin(admin.ModelAdmin):
|
|||||||
@staticmethod
|
@staticmethod
|
||||||
def _type_from_request(
|
def _type_from_request(
|
||||||
request: HttpRequest, default=None
|
request: HttpRequest, default=None
|
||||||
) -> Optional[MenuItemType]:
|
) -> MenuItemType | None:
|
||||||
try:
|
try:
|
||||||
return MenuItemType(request.GET.get("type"))
|
return MenuItemType(request.GET.get("type"))
|
||||||
except ValueError:
|
except ValueError:
|
||||||
|
|||||||
@@ -14,8 +14,8 @@ class MenuItemHookCustom(MenuItemHook):
|
|||||||
text: str,
|
text: str,
|
||||||
classes: str,
|
classes: str,
|
||||||
url_name: str,
|
url_name: str,
|
||||||
order: Optional[int] = None,
|
order: int | None = None,
|
||||||
navactive: Optional[List[str]] = None,
|
navactive: list[str] | None = None,
|
||||||
):
|
):
|
||||||
super().__init__(text, classes, url_name, order, navactive)
|
super().__init__(text, classes, url_name, order, navactive)
|
||||||
self.url = ""
|
self.url = ""
|
||||||
|
|||||||
@@ -33,8 +33,8 @@ class MenuItemHook:
|
|||||||
text: str,
|
text: str,
|
||||||
classes: str,
|
classes: str,
|
||||||
url_name: str,
|
url_name: str,
|
||||||
order: Optional[int] = None,
|
order: int | None = None,
|
||||||
navactive: Optional[List[str]] = None,
|
navactive: list[str] | None = None,
|
||||||
):
|
):
|
||||||
self.text = text
|
self.text = text
|
||||||
self.classes = classes
|
self.classes = classes
|
||||||
|
|||||||
@@ -56,8 +56,8 @@ class RenderedMenuItem:
|
|||||||
|
|
||||||
menu_item: MenuItem
|
menu_item: MenuItem
|
||||||
|
|
||||||
children: List["RenderedMenuItem"] = field(default_factory=list)
|
children: list["RenderedMenuItem"] = field(default_factory=list)
|
||||||
count: Optional[int] = None
|
count: int | None = None
|
||||||
html: str = ""
|
html: str = ""
|
||||||
html_id: str = ""
|
html_id: str = ""
|
||||||
|
|
||||||
@@ -78,7 +78,7 @@ class RenderedMenuItem:
|
|||||||
self.html_id = hook_obj.html_id
|
self.html_id = hook_obj.html_id
|
||||||
|
|
||||||
|
|
||||||
def render_menu(request: HttpRequest) -> List[RenderedMenuItem]:
|
def render_menu(request: HttpRequest) -> list[RenderedMenuItem]:
|
||||||
"""Return the rendered side menu for including in a template.
|
"""Return the rendered side menu for including in a template.
|
||||||
|
|
||||||
This function is creating BS5 style menus.
|
This function is creating BS5 style menus.
|
||||||
@@ -88,7 +88,7 @@ def render_menu(request: HttpRequest) -> List[RenderedMenuItem]:
|
|||||||
# Menu items needs to be rendered with the new BS5 template
|
# Menu items needs to be rendered with the new BS5 template
|
||||||
bs5_template = "menu/menu-item-bs5.html"
|
bs5_template = "menu/menu-item-bs5.html"
|
||||||
|
|
||||||
rendered_items: Dict[int, RenderedMenuItem] = {}
|
rendered_items: dict[int, RenderedMenuItem] = {}
|
||||||
menu_items: QuerySet[MenuItem] = MenuItem.objects.order_by(
|
menu_items: QuerySet[MenuItem] = MenuItem.objects.order_by(
|
||||||
"parent", "order", "text"
|
"parent", "order", "text"
|
||||||
)
|
)
|
||||||
@@ -131,7 +131,7 @@ def render_menu(request: HttpRequest) -> List[RenderedMenuItem]:
|
|||||||
return list(rendered_items.values())
|
return list(rendered_items.values())
|
||||||
|
|
||||||
|
|
||||||
def _gather_menu_items_from_hooks() -> Dict[str, MenuItemHook]:
|
def _gather_menu_items_from_hooks() -> dict[str, MenuItemHook]:
|
||||||
hook_items = {}
|
hook_items = {}
|
||||||
for hook in get_hooks("menu_item_hook"):
|
for hook in get_hooks("menu_item_hook"):
|
||||||
f = hook()
|
f = hook()
|
||||||
@@ -161,14 +161,14 @@ def _render_link_item(
|
|||||||
|
|
||||||
|
|
||||||
def _render_folder_items(
|
def _render_folder_items(
|
||||||
request: HttpRequest, rendered_items: Dict[int, RenderedMenuItem], new_template: str
|
request: HttpRequest, rendered_items: dict[int, RenderedMenuItem], new_template: str
|
||||||
):
|
):
|
||||||
for item in rendered_items.values():
|
for item in rendered_items.values():
|
||||||
if item.menu_item.is_folder:
|
if item.menu_item.is_folder:
|
||||||
item.update_html(request=request, template=new_template)
|
item.update_html(request=request, template=new_template)
|
||||||
|
|
||||||
|
|
||||||
def _remove_empty_folders(rendered_items: Dict[int, RenderedMenuItem]):
|
def _remove_empty_folders(rendered_items: dict[int, RenderedMenuItem]):
|
||||||
ids_to_remove = []
|
ids_to_remove = []
|
||||||
for item_id, item in rendered_items.items():
|
for item_id, item in rendered_items.items():
|
||||||
if item.is_folder and not item.children:
|
if item.is_folder and not item.children:
|
||||||
|
|||||||
@@ -347,9 +347,9 @@ class TestRenderedMenuItem(TestCase):
|
|||||||
|
|
||||||
|
|
||||||
class _ParsedMenuItem(NamedTuple):
|
class _ParsedMenuItem(NamedTuple):
|
||||||
classes: List[str]
|
classes: list[str]
|
||||||
text: str
|
text: str
|
||||||
count: Optional[int]
|
count: int | None
|
||||||
|
|
||||||
|
|
||||||
def parse_html(obj: RenderedMenuItem) -> _ParsedMenuItem:
|
def parse_html(obj: RenderedMenuItem) -> _ParsedMenuItem:
|
||||||
|
|||||||
@@ -1,6 +1,7 @@
|
|||||||
from string import Formatter
|
from string import Formatter
|
||||||
from django.urls import include, re_path
|
from django.urls import include, re_path
|
||||||
from typing import Iterable, Optional
|
from typing import Optional
|
||||||
|
from collections.abc import Iterable
|
||||||
|
|
||||||
from django.conf import settings
|
from django.conf import settings
|
||||||
from django.core.exceptions import ObjectDoesNotExist
|
from django.core.exceptions import ObjectDoesNotExist
|
||||||
@@ -175,7 +176,7 @@ class UrlHook:
|
|||||||
urls,
|
urls,
|
||||||
namespace: str,
|
namespace: str,
|
||||||
base_url: str,
|
base_url: str,
|
||||||
excluded_views : Optional[Iterable[str]] = None
|
excluded_views : Iterable[str] | None = None
|
||||||
):
|
):
|
||||||
self.include_pattern = re_path(base_url, include(urls, namespace=namespace))
|
self.include_pattern = re_path(base_url, include(urls, namespace=namespace))
|
||||||
self.excluded_views = set(excluded_views or [])
|
self.excluded_views = set(excluded_views or [])
|
||||||
|
|||||||
@@ -28,7 +28,7 @@ from .models import DiscordUser # noqa
|
|||||||
__all__ = ["create_bot_client", "group_to_role", "server_name", "DiscordUser", "Role"]
|
__all__ = ["create_bot_client", "group_to_role", "server_name", "DiscordUser", "Role"]
|
||||||
|
|
||||||
|
|
||||||
def discord_guild_id() -> Optional[int]:
|
def discord_guild_id() -> int | None:
|
||||||
"""Guild ID of configured Discord server.
|
"""Guild ID of configured Discord server.
|
||||||
|
|
||||||
Returns:
|
Returns:
|
||||||
|
|||||||
@@ -36,7 +36,7 @@ def calculate_roles_for_user(
|
|||||||
client: DiscordClient,
|
client: DiscordClient,
|
||||||
discord_uid: int,
|
discord_uid: int,
|
||||||
state_name: str = None,
|
state_name: str = None,
|
||||||
) -> Tuple[RolesSet, Optional[bool]]:
|
) -> tuple[RolesSet, bool | None]:
|
||||||
"""Calculate current Discord roles for an Auth user.
|
"""Calculate current Discord roles for an Auth user.
|
||||||
|
|
||||||
Takes into account reserved groups and existing managed roles (e.g. nitro).
|
Takes into account reserved groups and existing managed roles (e.g. nitro).
|
||||||
@@ -68,7 +68,7 @@ def calculate_roles_for_user(
|
|||||||
return roles_calculated.union(roles_persistent), True
|
return roles_calculated.union(roles_persistent), True
|
||||||
|
|
||||||
|
|
||||||
def _user_group_names(user: User, state_name: str = None) -> List[str]:
|
def _user_group_names(user: User, state_name: str = None) -> list[str]:
|
||||||
"""Names of groups and state the given user is a member of."""
|
"""Names of groups and state the given user is a member of."""
|
||||||
if not state_name:
|
if not state_name:
|
||||||
state_name = user.profile.state.name
|
state_name = user.profile.state.name
|
||||||
@@ -77,7 +77,7 @@ def _user_group_names(user: User, state_name: str = None) -> List[str]:
|
|||||||
return group_names
|
return group_names
|
||||||
|
|
||||||
|
|
||||||
def user_formatted_nick(user: User) -> Optional[str]:
|
def user_formatted_nick(user: User) -> str | None:
|
||||||
"""Name of the given user's main character with name formatting applied.
|
"""Name of the given user's main character with name formatting applied.
|
||||||
|
|
||||||
Returns:
|
Returns:
|
||||||
@@ -90,7 +90,7 @@ def user_formatted_nick(user: User) -> Optional[str]:
|
|||||||
return None
|
return None
|
||||||
|
|
||||||
|
|
||||||
def group_to_role(group: Group) -> Optional[Role]:
|
def group_to_role(group: Group) -> Role | None:
|
||||||
"""Fetch the Discord role matching the given Django group by name.
|
"""Fetch the Discord role matching the given Django group by name.
|
||||||
|
|
||||||
Returns:
|
Returns:
|
||||||
|
|||||||
@@ -6,7 +6,8 @@ from enum import IntEnum
|
|||||||
from hashlib import md5
|
from hashlib import md5
|
||||||
from http import HTTPStatus
|
from http import HTTPStatus
|
||||||
from time import sleep
|
from time import sleep
|
||||||
from typing import Iterable, List, Optional, Set, Tuple
|
from typing import List, Optional, Set, Tuple
|
||||||
|
from collections.abc import Iterable
|
||||||
from urllib.parse import urljoin
|
from urllib.parse import urljoin
|
||||||
from uuid import uuid1
|
from uuid import uuid1
|
||||||
|
|
||||||
@@ -233,7 +234,7 @@ class DiscordClient:
|
|||||||
|
|
||||||
# guild roles
|
# guild roles
|
||||||
|
|
||||||
def guild_roles(self, guild_id: int, use_cache: bool = True) -> Set[Role]:
|
def guild_roles(self, guild_id: int, use_cache: bool = True) -> set[Role]:
|
||||||
"""Fetch all roles for this guild.
|
"""Fetch all roles for this guild.
|
||||||
|
|
||||||
Args:
|
Args:
|
||||||
@@ -268,7 +269,7 @@ class DiscordClient:
|
|||||||
|
|
||||||
def create_guild_role(
|
def create_guild_role(
|
||||||
self, guild_id: int, role_name: str, **kwargs
|
self, guild_id: int, role_name: str, **kwargs
|
||||||
) -> Optional[Role]:
|
) -> Role | None:
|
||||||
"""Create a new guild role with the given name.
|
"""Create a new guild role with the given name.
|
||||||
|
|
||||||
See official documentation for additional optional parameters.
|
See official documentation for additional optional parameters.
|
||||||
@@ -318,7 +319,7 @@ class DiscordClient:
|
|||||||
gen_key = cls._generate_hash(f'{guild_id}')
|
gen_key = cls._generate_hash(f'{guild_id}')
|
||||||
return f'{cls._KEYPREFIX_GUILD_ROLES}__{gen_key}'
|
return f'{cls._KEYPREFIX_GUILD_ROLES}__{gen_key}'
|
||||||
|
|
||||||
def match_role_from_name(self, guild_id: int, role_name: str) -> Optional[Role]:
|
def match_role_from_name(self, guild_id: int, role_name: str) -> Role | None:
|
||||||
"""Fetch Discord role matching the given name (cached).
|
"""Fetch Discord role matching the given name (cached).
|
||||||
|
|
||||||
Args:
|
Args:
|
||||||
@@ -333,7 +334,7 @@ class DiscordClient:
|
|||||||
|
|
||||||
def match_or_create_roles_from_names(
|
def match_or_create_roles_from_names(
|
||||||
self, guild_id: int, role_names: Iterable[str]
|
self, guild_id: int, role_names: Iterable[str]
|
||||||
) -> List[Tuple[Role, bool]]:
|
) -> list[tuple[Role, bool]]:
|
||||||
"""Fetch or create Discord roles matching the given names (cached).
|
"""Fetch or create Discord roles matching the given names (cached).
|
||||||
|
|
||||||
Will try to match with existing roles names
|
Will try to match with existing roles names
|
||||||
@@ -361,7 +362,7 @@ class DiscordClient:
|
|||||||
|
|
||||||
def match_or_create_role_from_name(
|
def match_or_create_role_from_name(
|
||||||
self, guild_id: int, role_name: str, guild_roles: RolesSet = None
|
self, guild_id: int, role_name: str, guild_roles: RolesSet = None
|
||||||
) -> Tuple[Role, bool]:
|
) -> tuple[Role, bool]:
|
||||||
"""Fetch or create Discord role matching the given name.
|
"""Fetch or create Discord role matching the given name.
|
||||||
|
|
||||||
Will try to match with existing roles names
|
Will try to match with existing roles names
|
||||||
@@ -418,7 +419,7 @@ class DiscordClient:
|
|||||||
access_token: str,
|
access_token: str,
|
||||||
role_ids: list = None,
|
role_ids: list = None,
|
||||||
nick: str = None
|
nick: str = None
|
||||||
) -> Optional[bool]:
|
) -> bool | None:
|
||||||
"""Adds a user to the guild.
|
"""Adds a user to the guild.
|
||||||
|
|
||||||
Returns:
|
Returns:
|
||||||
@@ -442,7 +443,7 @@ class DiscordClient:
|
|||||||
return None
|
return None
|
||||||
return False
|
return False
|
||||||
|
|
||||||
def guild_member(self, guild_id: int, user_id: int) -> Optional[GuildMember]:
|
def guild_member(self, guild_id: int, user_id: int) -> GuildMember | None:
|
||||||
"""Fetch info for a guild member.
|
"""Fetch info for a guild member.
|
||||||
|
|
||||||
Args:
|
Args:
|
||||||
@@ -461,8 +462,8 @@ class DiscordClient:
|
|||||||
return GuildMember.from_dict(r.json())
|
return GuildMember.from_dict(r.json())
|
||||||
|
|
||||||
def modify_guild_member(
|
def modify_guild_member(
|
||||||
self, guild_id: int, user_id: int, role_ids: List[int] = None, nick: str = None
|
self, guild_id: int, user_id: int, role_ids: list[int] = None, nick: str = None
|
||||||
) -> Optional[bool]:
|
) -> bool | None:
|
||||||
"""Set properties of a guild member.
|
"""Set properties of a guild member.
|
||||||
|
|
||||||
Args:
|
Args:
|
||||||
@@ -501,7 +502,7 @@ class DiscordClient:
|
|||||||
return True
|
return True
|
||||||
return False
|
return False
|
||||||
|
|
||||||
def remove_guild_member(self, guild_id: int, user_id: int) -> Optional[bool]:
|
def remove_guild_member(self, guild_id: int, user_id: int) -> bool | None:
|
||||||
"""Remove a member from a guild.
|
"""Remove a member from a guild.
|
||||||
|
|
||||||
Args:
|
Args:
|
||||||
@@ -529,7 +530,7 @@ class DiscordClient:
|
|||||||
|
|
||||||
def add_guild_member_role(
|
def add_guild_member_role(
|
||||||
self, guild_id: int, user_id: int, role_id: int
|
self, guild_id: int, user_id: int, role_id: int
|
||||||
) -> Optional[bool]:
|
) -> bool | None:
|
||||||
"""Adds a role to a guild member
|
"""Adds a role to a guild member
|
||||||
|
|
||||||
Returns:
|
Returns:
|
||||||
@@ -549,7 +550,7 @@ class DiscordClient:
|
|||||||
|
|
||||||
def remove_guild_member_role(
|
def remove_guild_member_role(
|
||||||
self, guild_id: int, user_id: int, role_id: int
|
self, guild_id: int, user_id: int, role_id: int
|
||||||
) -> Optional[bool]:
|
) -> bool | None:
|
||||||
"""Remove a role to a guild member
|
"""Remove a role to a guild member
|
||||||
|
|
||||||
Args:
|
Args:
|
||||||
@@ -572,7 +573,7 @@ class DiscordClient:
|
|||||||
return True
|
return True
|
||||||
return False
|
return False
|
||||||
|
|
||||||
def guild_member_roles(self, guild_id: int, user_id: int) -> Optional[RolesSet]:
|
def guild_member_roles(self, guild_id: int, user_id: int) -> RolesSet | None:
|
||||||
"""Fetch the current guild roles of a guild member.
|
"""Fetch the current guild roles of a guild member.
|
||||||
|
|
||||||
Args:
|
Args:
|
||||||
@@ -821,6 +822,6 @@ class DiscordClient:
|
|||||||
return md5(key.encode('utf-8')).hexdigest()
|
return md5(key.encode('utf-8')).hexdigest()
|
||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def _sanitize_role_ids(role_ids: Iterable[int]) -> List[int]:
|
def _sanitize_role_ids(role_ids: Iterable[int]) -> list[int]:
|
||||||
"""Sanitize a list of role IDs, i.e. make sure its a list of unique integers."""
|
"""Sanitize a list of role IDs, i.e. make sure its a list of unique integers."""
|
||||||
return [int(role_id) for role_id in set(role_ids)]
|
return [int(role_id) for role_id in set(role_ids)]
|
||||||
|
|||||||
@@ -1,5 +1,6 @@
|
|||||||
from copy import copy
|
from copy import copy
|
||||||
from typing import Iterable, List, Optional, Set, Tuple
|
from typing import List, Optional, Set, Tuple
|
||||||
|
from collections.abc import Iterable
|
||||||
|
|
||||||
from .models import Role
|
from .models import Role
|
||||||
|
|
||||||
@@ -50,7 +51,7 @@ class RolesSet:
|
|||||||
def __len__(self):
|
def __len__(self):
|
||||||
return len(self._roles.keys())
|
return len(self._roles.keys())
|
||||||
|
|
||||||
def has_roles(self, role_ids: Set[int]) -> bool:
|
def has_roles(self, role_ids: set[int]) -> bool:
|
||||||
"""True if this objects contains all roles defined by given role_ids
|
"""True if this objects contains all roles defined by given role_ids
|
||||||
incl. managed roles.
|
incl. managed roles.
|
||||||
"""
|
"""
|
||||||
@@ -58,7 +59,7 @@ class RolesSet:
|
|||||||
all_role_ids = self._roles.keys()
|
all_role_ids = self._roles.keys()
|
||||||
return role_ids.issubset(all_role_ids)
|
return role_ids.issubset(all_role_ids)
|
||||||
|
|
||||||
def ids(self) -> Set[int]:
|
def ids(self) -> set[int]:
|
||||||
"""Set of all role IDs."""
|
"""Set of all role IDs."""
|
||||||
return set(self._roles.keys())
|
return set(self._roles.keys())
|
||||||
|
|
||||||
@@ -114,7 +115,7 @@ class RolesSet:
|
|||||||
new_ids = self.ids().difference(other.ids())
|
new_ids = self.ids().difference(other.ids())
|
||||||
return self.subset(role_ids=new_ids)
|
return self.subset(role_ids=new_ids)
|
||||||
|
|
||||||
def role_by_name(self, role_name: str) -> Optional[Role]:
|
def role_by_name(self, role_name: str) -> Role | None:
|
||||||
"""Role if one with matching name is found else None."""
|
"""Role if one with matching name is found else None."""
|
||||||
role_name = Role.sanitize_name(role_name)
|
role_name = Role.sanitize_name(role_name)
|
||||||
if role_name in self._roles_by_name:
|
if role_name in self._roles_by_name:
|
||||||
@@ -123,7 +124,7 @@ class RolesSet:
|
|||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def create_from_matched_roles(
|
def create_from_matched_roles(
|
||||||
cls, matched_roles: List[Tuple[Role, bool]]
|
cls, matched_roles: list[tuple[Role, bool]]
|
||||||
) -> "RolesSet":
|
) -> "RolesSet":
|
||||||
"""Create new instance from the given list of matches roles.
|
"""Create new instance from the given list of matches roles.
|
||||||
|
|
||||||
|
|||||||
@@ -69,7 +69,7 @@ class Guild:
|
|||||||
|
|
||||||
id: int
|
id: int
|
||||||
name: str
|
name: str
|
||||||
roles: FrozenSet[Role]
|
roles: frozenset[Role]
|
||||||
|
|
||||||
def __post_init__(self):
|
def __post_init__(self):
|
||||||
object.__setattr__(self, "id", int(self.id))
|
object.__setattr__(self, "id", int(self.id))
|
||||||
@@ -95,7 +95,7 @@ class GuildMember:
|
|||||||
|
|
||||||
_NICK_MAX_CHARS = 32
|
_NICK_MAX_CHARS = 32
|
||||||
|
|
||||||
roles: FrozenSet[int]
|
roles: frozenset[int]
|
||||||
nick: str = None
|
nick: str = None
|
||||||
user: User = None
|
user: User = None
|
||||||
|
|
||||||
|
|||||||
@@ -98,7 +98,7 @@ class DiscordUser(models.Model):
|
|||||||
logger.warning('Failed to update nickname for %s', self.user)
|
logger.warning('Failed to update nickname for %s', self.user)
|
||||||
return success
|
return success
|
||||||
|
|
||||||
def update_groups(self, state_name: str = None) -> Optional[bool]:
|
def update_groups(self, state_name: str = None) -> bool | None:
|
||||||
"""update groups for a user based on his current group memberships.
|
"""update groups for a user based on his current group memberships.
|
||||||
Will add or remove roles of a user as needed.
|
Will add or remove roles of a user as needed.
|
||||||
|
|
||||||
@@ -134,7 +134,7 @@ class DiscordUser(models.Model):
|
|||||||
logger.info('No need to update roles for user %s', self.user)
|
logger.info('No need to update roles for user %s', self.user)
|
||||||
return True
|
return True
|
||||||
|
|
||||||
def update_username(self) -> Optional[bool]:
|
def update_username(self) -> bool | None:
|
||||||
"""Updates the username incl. the discriminator
|
"""Updates the username incl. the discriminator
|
||||||
from the Discord server and saves it
|
from the Discord server and saves it
|
||||||
|
|
||||||
@@ -159,7 +159,7 @@ class DiscordUser(models.Model):
|
|||||||
notify_user: bool = False,
|
notify_user: bool = False,
|
||||||
is_rate_limited: bool = True,
|
is_rate_limited: bool = True,
|
||||||
handle_api_exceptions: bool = False
|
handle_api_exceptions: bool = False
|
||||||
) -> Optional[bool]:
|
) -> bool | None:
|
||||||
"""Deletes the Discount user both on the server and locally
|
"""Deletes the Discount user both on the server and locally
|
||||||
|
|
||||||
Params:
|
Params:
|
||||||
|
|||||||
@@ -438,7 +438,7 @@ class TestUserHasAccount(NoSocketsTestCase):
|
|||||||
self.assertFalse(DiscordUser.objects.user_has_account(self.user))
|
self.assertFalse(DiscordUser.objects.user_has_account(self.user))
|
||||||
|
|
||||||
def test_return_false_if_user_does_not_exist(self):
|
def test_return_false_if_user_does_not_exist(self):
|
||||||
my_user = User(username='Dummy')
|
my_user = AuthUtils.create_user("test_return_false_if_user_does_not_exist")
|
||||||
self.assertFalse(DiscordUser.objects.user_has_account(my_user))
|
self.assertFalse(DiscordUser.objects.user_has_account(my_user))
|
||||||
|
|
||||||
def test_return_false_if_not_called_with_user_object(self):
|
def test_return_false_if_not_called_with_user_object(self):
|
||||||
|
|||||||
@@ -181,7 +181,7 @@ class SmfManager:
|
|||||||
return out
|
return out
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def add_user(cls, username, email_address, groups, main_character: EveCharacter) -> Tuple:
|
def add_user(cls, username, email_address, groups, main_character: EveCharacter) -> tuple:
|
||||||
"""
|
"""
|
||||||
Add a user to SMF
|
Add a user to SMF
|
||||||
:param username:
|
:param username:
|
||||||
|
|||||||
@@ -262,7 +262,7 @@ class AuthUtils:
|
|||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def add_permissions_to_user_by_name(
|
def add_permissions_to_user_by_name(
|
||||||
cls, perms: List[str], user: User, disconnect_signals: bool = True
|
cls, perms: list[str], user: User, disconnect_signals: bool = True
|
||||||
) -> User:
|
) -> User:
|
||||||
"""Add permissions given by name to a user
|
"""Add permissions given by name to a user
|
||||||
|
|
||||||
|
|||||||
@@ -10,12 +10,12 @@ class ThemeHook:
|
|||||||
def __init__(self,
|
def __init__(self,
|
||||||
name: str,
|
name: str,
|
||||||
description: str,
|
description: str,
|
||||||
css: List[dict],
|
css: list[dict],
|
||||||
js: List[dict],
|
js: list[dict],
|
||||||
css_template: Optional[str] = None,
|
css_template: str | None = None,
|
||||||
js_template: Optional[str] = None,
|
js_template: str | None = None,
|
||||||
html_tags: Optional[str] = "",
|
html_tags: str | None = "",
|
||||||
header_padding: Optional[str] = "4em"):
|
header_padding: str | None = "4em"):
|
||||||
"""
|
"""
|
||||||
:param name: Theme python name
|
:param name: Theme python name
|
||||||
:type name: str
|
:type name: str
|
||||||
|
|||||||
@@ -1,4 +1,5 @@
|
|||||||
from typing import List, Iterable, Callable
|
from typing import List
|
||||||
|
from collections.abc import Iterable, Callable
|
||||||
|
|
||||||
from django.urls import include
|
from django.urls import include
|
||||||
import esi.urls
|
import esi.urls
|
||||||
@@ -24,8 +25,8 @@ admin.site.site_header = NAME
|
|||||||
|
|
||||||
|
|
||||||
def urls_from_apps(
|
def urls_from_apps(
|
||||||
apps_hook_functions: Iterable[Callable], public_views_allowed: List[str]
|
apps_hook_functions: Iterable[Callable], public_views_allowed: list[str]
|
||||||
) -> List[URLPattern]:
|
) -> list[URLPattern]:
|
||||||
"""Return urls from apps and add default decorators."""
|
"""Return urls from apps and add default decorators."""
|
||||||
|
|
||||||
url_patterns = []
|
url_patterns = []
|
||||||
|
|||||||
@@ -22,7 +22,7 @@ class ItemCounter:
|
|||||||
DEFAULT_CACHE_TIMEOUT = 24 * 3600
|
DEFAULT_CACHE_TIMEOUT = 24 * 3600
|
||||||
|
|
||||||
def __init__(
|
def __init__(
|
||||||
self, name: str, minimum: Optional[int] = None, redis: Optional[Redis] = None
|
self, name: str, minimum: int | None = None, redis: Redis | None = None
|
||||||
) -> None:
|
) -> None:
|
||||||
if not name:
|
if not name:
|
||||||
raise ValueError("Must define a name")
|
raise ValueError("Must define a name")
|
||||||
@@ -60,6 +60,6 @@ class ItemCounter:
|
|||||||
except ValueError:
|
except ValueError:
|
||||||
pass
|
pass
|
||||||
|
|
||||||
def value(self) -> Optional[int]:
|
def value(self) -> int | None:
|
||||||
"""Return current value or None if not yet initialized."""
|
"""Return current value or None if not yet initialized."""
|
||||||
return cache.get(self._cache_key)
|
return cache.get(self._cache_key)
|
||||||
|
|||||||
@@ -18,7 +18,7 @@ MUMBLE_URL = "mumble.example.com"
|
|||||||
|
|
||||||
Add the following lines to your `.env` file
|
Add the following lines to your `.env` file
|
||||||
|
|
||||||
```env
|
```bash
|
||||||
# Mumble
|
# Mumble
|
||||||
MUMBLE_SUPERUSER_PASSWORD = superuser_password
|
MUMBLE_SUPERUSER_PASSWORD = superuser_password
|
||||||
MUMBLE_ICESECRETWRITE = icesecretwrite
|
MUMBLE_ICESECRETWRITE = icesecretwrite
|
||||||
|
|||||||
@@ -25,7 +25,7 @@ BROADCAST_SERVICE_NAME = "broadcast"
|
|||||||
|
|
||||||
Add the following lines to your `.env` file
|
Add the following lines to your `.env` file
|
||||||
|
|
||||||
```env
|
```bash
|
||||||
# Openfire
|
# Openfire
|
||||||
OPENFIRE_SECRET_KEY = superuser_password
|
OPENFIRE_SECRET_KEY = superuser_password
|
||||||
BROADCAST_USER_PASSWORD = icesecretwrite
|
BROADCAST_USER_PASSWORD = icesecretwrite
|
||||||
|
|||||||
@@ -34,7 +34,7 @@ CELERYBEAT_SCHEDULE['run_ts3_group_update'] = {
|
|||||||
|
|
||||||
Add the following lines to your `.env` file
|
Add the following lines to your `.env` file
|
||||||
|
|
||||||
```env
|
```bash
|
||||||
# Temspeak
|
# Temspeak
|
||||||
TEAMSPEAK3_SERVERQUERY_USER = "serverquery"
|
TEAMSPEAK3_SERVERQUERY_USER = "serverquery"
|
||||||
TEAMSPEAK3_SERVERQUERY_PASSWORD = ""
|
TEAMSPEAK3_SERVERQUERY_PASSWORD = ""
|
||||||
|
|||||||
@@ -29,7 +29,7 @@ Replace your docker-compose.yml with the contents of <https://gitlab.com/allianc
|
|||||||
|
|
||||||
V3.x installs likely used a dedicated database for Nginx Proxy Manager, you can either setup NPM again without a database, or uncomment the sections noted to maintain this configuration
|
V3.x installs likely used a dedicated database for Nginx Proxy Manager, you can either setup NPM again without a database, or uncomment the sections noted to maintain this configuration
|
||||||
|
|
||||||
```docker-compose
|
```docker
|
||||||
proxy:
|
proxy:
|
||||||
...
|
...
|
||||||
# Uncomment this section to use a dedicated database for Nginx Proxy Manager
|
# Uncomment this section to use a dedicated database for Nginx Proxy Manager
|
||||||
@@ -64,7 +64,7 @@ V3.x installs likely used a dedicated database for Nginx Proxy Manager, you can
|
|||||||
|
|
||||||
You will need to add some entries to your .env file
|
You will need to add some entries to your .env file
|
||||||
|
|
||||||
```env
|
```bash
|
||||||
AA_DB_CHARSET=utf8mb4
|
AA_DB_CHARSET=utf8mb4
|
||||||
GF_SECURITY_ADMIN_USERNAME=admin
|
GF_SECURITY_ADMIN_USERNAME=admin
|
||||||
```
|
```
|
||||||
|
|||||||
@@ -122,7 +122,7 @@ a2dissite 000-default.conf
|
|||||||
|
|
||||||
## Sample Config File
|
## Sample Config File
|
||||||
|
|
||||||
```ini
|
```apacheconf
|
||||||
<VirtualHost *:80>
|
<VirtualHost *:80>
|
||||||
ServerName auth.example.com
|
ServerName auth.example.com
|
||||||
|
|
||||||
|
|||||||
@@ -9,7 +9,7 @@ Tuning usually has benefits and costs and should only be performed by experience
|
|||||||
:::{toctree}
|
:::{toctree}
|
||||||
:maxdepth: 1
|
:maxdepth: 1
|
||||||
|
|
||||||
gunicorn
|
web
|
||||||
celery
|
celery
|
||||||
redis
|
redis
|
||||||
python
|
python
|
||||||
|
|||||||
@@ -99,7 +99,7 @@ Replace the nginx service in your docker-compose as follows
|
|||||||
|
|
||||||
Modify your nginx.conf as follows
|
Modify your nginx.conf as follows
|
||||||
|
|
||||||
```conf
|
```nginx
|
||||||
load_module modules/ngx_http_brotli_static_module.so;
|
load_module modules/ngx_http_brotli_static_module.so;
|
||||||
load_module modules/ngx_http_brotli_filter_module.so;
|
load_module modules/ngx_http_brotli_filter_module.so;
|
||||||
...
|
...
|
||||||
|
|||||||
@@ -15,7 +15,7 @@ license = { file = "LICENSE" }
|
|||||||
authors = [
|
authors = [
|
||||||
{ name = "Alliance Auth", email = "adarnof@gmail.com" },
|
{ name = "Alliance Auth", email = "adarnof@gmail.com" },
|
||||||
]
|
]
|
||||||
requires-python = ">=3.8"
|
requires-python = ">=3.10"
|
||||||
classifiers = [
|
classifiers = [
|
||||||
"Environment :: Web Environment",
|
"Environment :: Web Environment",
|
||||||
"Framework :: Celery",
|
"Framework :: Celery",
|
||||||
@@ -26,8 +26,6 @@ classifiers = [
|
|||||||
"Operating System :: POSIX :: Linux",
|
"Operating System :: POSIX :: Linux",
|
||||||
"Programming Language :: Python",
|
"Programming Language :: Python",
|
||||||
"Programming Language :: Python :: 3 :: Only",
|
"Programming Language :: Python :: 3 :: Only",
|
||||||
"Programming Language :: Python :: 3.8",
|
|
||||||
"Programming Language :: Python :: 3.9",
|
|
||||||
"Programming Language :: Python :: 3.10",
|
"Programming Language :: Python :: 3.10",
|
||||||
"Programming Language :: Python :: 3.11",
|
"Programming Language :: Python :: 3.11",
|
||||||
"Programming Language :: Python :: 3.12",
|
"Programming Language :: Python :: 3.12",
|
||||||
@@ -43,10 +41,10 @@ dependencies = [
|
|||||||
"beautifulsoup4",
|
"beautifulsoup4",
|
||||||
"celery<6,>=5.2",
|
"celery<6,>=5.2",
|
||||||
"celery-once>=3.0.1",
|
"celery-once>=3.0.1",
|
||||||
"django<5,>=4.2",
|
"django<5.2,>=5.1",
|
||||||
"django-bootstrap-form",
|
"django-bootstrap-form",
|
||||||
"django-bootstrap5>=23.3",
|
"django-bootstrap5>=23.3",
|
||||||
"django-celery-beat>=2.3",
|
"django-celery-beat>=2.7",
|
||||||
"django-esi>=5",
|
"django-esi>=5",
|
||||||
"django-redis>=5.2",
|
"django-redis>=5.2",
|
||||||
"django-registration<3.4,>=3.3",
|
"django-registration<3.4,>=3.3",
|
||||||
|
|||||||
7
tox.ini
7
tox.ini
@@ -2,18 +2,17 @@
|
|||||||
isolated_build = true
|
isolated_build = true
|
||||||
skipsdist = true
|
skipsdist = true
|
||||||
usedevelop = true
|
usedevelop = true
|
||||||
envlist = py{38,39,310,311,312}-{all,core}, docs
|
envlist = py{310,311,312,313}-{all,core}, docs
|
||||||
|
|
||||||
[testenv]
|
[testenv]
|
||||||
setenv =
|
setenv =
|
||||||
all: DJANGO_SETTINGS_MODULE = tests.settings_all
|
all: DJANGO_SETTINGS_MODULE = tests.settings_all
|
||||||
core: DJANGO_SETTINGS_MODULE = tests.settings_core
|
core: DJANGO_SETTINGS_MODULE = tests.settings_core
|
||||||
basepython =
|
basepython =
|
||||||
py38: python3.8
|
|
||||||
py39: python3.9
|
|
||||||
py310: python3.10
|
py310: python3.10
|
||||||
py311: python3.11
|
py311: python3.11
|
||||||
py312: python3.12
|
py312: python3.12
|
||||||
|
py313: python3.13
|
||||||
deps=
|
deps=
|
||||||
coverage
|
coverage
|
||||||
install_command = pip install -e ".[test]" -U {opts} {packages}
|
install_command = pip install -e ".[test]" -U {opts} {packages}
|
||||||
@@ -25,7 +24,7 @@ commands =
|
|||||||
|
|
||||||
[testenv:docs]
|
[testenv:docs]
|
||||||
description = invoke sphinx-build to build the HTML docs
|
description = invoke sphinx-build to build the HTML docs
|
||||||
basepython = python3.11
|
basepython = python3.12
|
||||||
install_command = pip install -e ".[docs]" -U {opts} {packages}
|
install_command = pip install -e ".[docs]" -U {opts} {packages}
|
||||||
commands =
|
commands =
|
||||||
sphinx-build -T -E -b html -d "{toxworkdir}/docs_doctree" -D language=en docs "{toxworkdir}/docs_out" {posargs}
|
sphinx-build -T -E -b html -d "{toxworkdir}/docs_doctree" -D language=en docs "{toxworkdir}/docs_out" {posargs}
|
||||||
|
|||||||
Reference in New Issue
Block a user