WIP
This commit is contained in:
parent
e70a0584c9
commit
a7a5b5c8ea
43 changed files with 5531 additions and 9 deletions
|
|
@ -0,0 +1,3 @@
|
|||
from config.celery import app as celery_app
|
||||
|
||||
__all__ = ("celery_app",)
|
||||
11
config/celery.py
Normal file
11
config/celery.py
Normal file
|
|
@ -0,0 +1,11 @@
|
|||
from __future__ import annotations
|
||||
|
||||
import os
|
||||
|
||||
from celery import Celery
|
||||
|
||||
os.environ.setdefault("DJANGO_SETTINGS_MODULE", "config.settings")
|
||||
|
||||
app = Celery("tussilago")
|
||||
app.config_from_object("django.conf:settings", namespace="CELERY")
|
||||
app.autodiscover_tasks()
|
||||
|
|
@ -39,9 +39,7 @@ def check_required_dev_commands(*_: object, **__: object) -> list[CheckMessage]:
|
|||
if not (settings.DEBUG or getattr(settings, "TESTING", False)):
|
||||
return []
|
||||
|
||||
missing_commands: list[str] = [
|
||||
command for command in REQUIRED_DEV_COMMANDS if shutil.which(command) is None
|
||||
]
|
||||
missing_commands: list[str] = [command for command in REQUIRED_DEV_COMMANDS if shutil.which(command) is None]
|
||||
|
||||
if not missing_commands:
|
||||
return []
|
||||
|
|
|
|||
112
config/dev_autoreload.py
Normal file
112
config/dev_autoreload.py
Normal file
|
|
@ -0,0 +1,112 @@
|
|||
from __future__ import annotations
|
||||
|
||||
from pathlib import Path
|
||||
from typing import TYPE_CHECKING
|
||||
|
||||
import django_watchfiles
|
||||
from django.utils import autoreload
|
||||
|
||||
if TYPE_CHECKING:
|
||||
from collections.abc import Iterable
|
||||
from collections.abc import Sequence
|
||||
|
||||
from watchfiles import Change
|
||||
|
||||
IGNORED_PROJECT_ROOT_NAMES: frozenset[str] = frozenset({
|
||||
".git",
|
||||
".venv",
|
||||
"__pycache__",
|
||||
"firecracker",
|
||||
})
|
||||
|
||||
|
||||
def _resolve_path(path: Path) -> Path:
|
||||
try:
|
||||
return path.resolve()
|
||||
except OSError:
|
||||
return path.absolute()
|
||||
|
||||
|
||||
def _is_relative_to(path: Path, parent: Path) -> bool:
|
||||
try:
|
||||
path.relative_to(parent)
|
||||
except ValueError:
|
||||
return False
|
||||
return True
|
||||
|
||||
|
||||
def build_project_watch_roots(
|
||||
project_root: Path,
|
||||
*,
|
||||
ignored_root_names: Sequence[str] = tuple(IGNORED_PROJECT_ROOT_NAMES),
|
||||
) -> tuple[Path, ...]:
|
||||
"""Return top-level project directories worth watching during development."""
|
||||
ignored_names = set(ignored_root_names)
|
||||
resolved_project_root = _resolve_path(project_root)
|
||||
return tuple(
|
||||
sorted(
|
||||
_resolve_path(child)
|
||||
for child in resolved_project_root.iterdir()
|
||||
if child.is_dir() and not child.name.startswith(".") and child.name not in ignored_names
|
||||
),
|
||||
)
|
||||
|
||||
|
||||
class TussilagoWatchfilesReloader(django_watchfiles.WatchfilesReloader):
|
||||
"""Use child directories instead of repo root for dev autoreload watches."""
|
||||
|
||||
def __init__(
|
||||
self,
|
||||
*,
|
||||
project_root: Path,
|
||||
ignored_paths: Sequence[Path] | None = None,
|
||||
) -> None:
|
||||
"""Store project-specific watch settings before watcher startup."""
|
||||
self.project_root = _resolve_path(project_root)
|
||||
self.ignored_paths = tuple(
|
||||
_resolve_path(path) for path in (ignored_paths or (self.project_root / "firecracker",))
|
||||
)
|
||||
self.project_watch_roots = build_project_watch_roots(self.project_root)
|
||||
super().__init__()
|
||||
|
||||
def file_filter(self, change: Change, filename: str) -> bool:
|
||||
"""Drop file events from ignored paths such as the firecracker tree.
|
||||
|
||||
Returns:
|
||||
True when the file event should still be watched.
|
||||
"""
|
||||
resolved_path = _resolve_path(Path(filename))
|
||||
if any(_is_relative_to(resolved_path, ignored_path) for ignored_path in self.ignored_paths):
|
||||
return False
|
||||
return super().file_filter(change, filename)
|
||||
|
||||
def watched_roots(self, watched_files: Iterable[Path]) -> frozenset[Path]:
|
||||
"""Replace repo-root watches with top-level child directories.
|
||||
|
||||
Returns:
|
||||
Watch roots with the project root expanded into child directories.
|
||||
"""
|
||||
roots = {_resolve_path(root) for root in super().watched_roots(watched_files)}
|
||||
filtered_roots = {
|
||||
root
|
||||
for root in roots
|
||||
if not any(_is_relative_to(root, ignored_path) for ignored_path in self.ignored_paths)
|
||||
}
|
||||
if self.project_root in filtered_roots:
|
||||
filtered_roots.remove(self.project_root)
|
||||
filtered_roots.update(self.project_watch_roots)
|
||||
return frozenset(filtered_roots)
|
||||
|
||||
|
||||
def install_watchfiles_reloader_patch(*, project_root: Path) -> None:
|
||||
"""Install project-specific watchfiles reloader for dev and test runtimes."""
|
||||
resolved_project_root = _resolve_path(project_root)
|
||||
|
||||
class ProjectWatchfilesReloader(TussilagoWatchfilesReloader):
|
||||
def __init__(self) -> None:
|
||||
super().__init__(project_root=resolved_project_root)
|
||||
|
||||
def replaced_get_reloader() -> autoreload.BaseReloader:
|
||||
return ProjectWatchfilesReloader()
|
||||
|
||||
autoreload.get_reloader = replaced_get_reloader
|
||||
|
|
@ -70,6 +70,7 @@ if DEBUG:
|
|||
|
||||
INSTALLED_APPS: list[str] = [
|
||||
"config.apps.TussilagoConfig",
|
||||
"control_plane.apps.ControlPlaneConfig",
|
||||
"django.contrib.admin",
|
||||
"django.contrib.auth",
|
||||
"django.contrib.contenttypes",
|
||||
|
|
@ -122,6 +123,24 @@ DATABASES: dict[str, dict[str, str | Path | dict[str, str | int]]] = {
|
|||
},
|
||||
}
|
||||
|
||||
DISABLE_SERVER_SIDE_CURSORS: bool = True
|
||||
|
||||
CELERY_BROKER_URL: str = os.getenv(
|
||||
"TUSSILAGO_CELERY_BROKER_URL",
|
||||
"memory://" if DEBUG or TESTING else "",
|
||||
)
|
||||
CELERY_RESULT_BACKEND: str = os.getenv(
|
||||
"TUSSILAGO_CELERY_RESULT_BACKEND",
|
||||
"cache+memory://" if DEBUG or TESTING else "",
|
||||
)
|
||||
CELERY_ACCEPT_CONTENT: list[str] = ["json"]
|
||||
CELERY_TASK_SERIALIZER: str = "json"
|
||||
CELERY_RESULT_SERIALIZER: str = "json"
|
||||
CELERY_TIMEZONE: str = TIME_ZONE
|
||||
CELERY_TASK_ALWAYS_EAGER: bool = TESTING
|
||||
CELERY_TASK_EAGER_PROPAGATES: bool = TESTING
|
||||
CELERY_TASK_DEFAULT_QUEUE: str = "control-plane"
|
||||
|
||||
AUTH_PASSWORD_VALIDATORS: list[dict[str, str]] = [
|
||||
{
|
||||
"NAME": "django.contrib.auth.password_validation.UserAttributeSimilarityValidator",
|
||||
|
|
@ -186,5 +205,9 @@ if DEBUG or TESTING:
|
|||
"django_browser_reload",
|
||||
))
|
||||
|
||||
from config.dev_autoreload import install_watchfiles_reloader_patch
|
||||
|
||||
install_watchfiles_reloader_patch(project_root=BASE_DIR)
|
||||
|
||||
# Customize the config to support htmx boosting.
|
||||
DEBUG_TOOLBAR_CONFIG: dict[str, str] = {"ROOT_TAG_EXTRA_ATTRS": "hx-preserve"}
|
||||
|
|
|
|||
|
|
@ -11,6 +11,7 @@ if TYPE_CHECKING:
|
|||
from django.urls.resolvers import URLResolver
|
||||
|
||||
urlpatterns: list[URLPattern | URLResolver] = [
|
||||
path(route="", view=include(arg="control_plane.urls")),
|
||||
path(route="admin/", view=admin.site.urls),
|
||||
]
|
||||
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue