- Add Gitea Actions workflow to build and deploy docs on push to main - Generate Sphinx reference documentation for all apps and modules - Deploy versioned and latest docs via rsync over SSH
98 lines
3.3 KiB
Python
98 lines
3.3 KiB
Python
import os
|
|
import sys
|
|
import tomllib
|
|
|
|
# The Django package lives at <repo>/mnemosyne/<inner mnemosyne>/. Adding the
|
|
# outer mnemosyne/ directory to sys.path lets autodoc resolve every app
|
|
# (themis, library, llm_manager, mcp_server) and the project settings module.
|
|
sys.path.insert(0, os.path.abspath('../../mnemosyne'))
|
|
|
|
os.environ.setdefault('DJANGO_SETTINGS_MODULE', 'mnemosyne.settings')
|
|
|
|
# Load real .env if present (local dev). In CI there is none and that's fine —
|
|
# settings.py provides a default for every env var it reads, so the import
|
|
# succeeds either way.
|
|
_repo_root = os.path.abspath(os.path.join(os.path.dirname(__file__), '..', '..'))
|
|
_env_file = os.path.join(_repo_root, 'mnemosyne', '.env')
|
|
if os.path.exists(_env_file):
|
|
with open(_env_file) as _f:
|
|
for _line in _f:
|
|
_line = _line.strip()
|
|
if not _line or _line.startswith('#') or '=' not in _line:
|
|
continue
|
|
_key, _val = _line.split('=', 1)
|
|
os.environ.setdefault(_key.strip(), _val.strip())
|
|
|
|
import django # noqa: E402
|
|
|
|
django.setup()
|
|
|
|
# Sphinx autodoc calls repr() on every class attribute it documents. Django's
|
|
# QuerySet.__repr__ executes a SELECT against the database — which doc builds
|
|
# have no business doing. Intercept object_description so QuerySet instances
|
|
# render as a static string instead. Mnemosyne's themis app has at least one
|
|
# DRF viewset with a class-level queryset attribute, so this matters.
|
|
from django.db.models.query import QuerySet # noqa: E402
|
|
import sphinx.util.inspect as _sphinx_inspect # noqa: E402
|
|
|
|
_orig_object_description = _sphinx_inspect.object_description
|
|
|
|
|
|
def _safe_object_description(obj, *args, **kwargs):
|
|
if isinstance(obj, QuerySet):
|
|
return f'<QuerySet [{obj.model.__name__}]>'
|
|
return _orig_object_description(obj, *args, **kwargs)
|
|
|
|
|
|
_sphinx_inspect.object_description = _safe_object_description
|
|
|
|
# ── Sphinx configuration ──────────────────────────────────────────────────
|
|
project = 'Mnemosyne'
|
|
copyright = '2026, Mnemosyne Team'
|
|
author = 'Mnemosyne Team'
|
|
|
|
with open(os.path.join(_repo_root, 'pyproject.toml'), 'rb') as _f:
|
|
release = tomllib.load(_f)['project']['version']
|
|
|
|
extensions = [
|
|
'sphinx.ext.autodoc',
|
|
'sphinx.ext.viewcode',
|
|
'sphinx.ext.napoleon',
|
|
'sphinx.ext.intersphinx',
|
|
'sphinx_autodoc_typehints',
|
|
'sphinxcontrib.httpdomain',
|
|
'sphinxcontrib.mermaid',
|
|
'myst_parser',
|
|
]
|
|
|
|
source_suffix = {'.rst': 'restructuredtext', '.md': 'markdown'}
|
|
|
|
myst_enable_extensions = ['colon_fence', 'deflist', 'tasklist', 'attrs_inline']
|
|
myst_heading_anchors = 4
|
|
|
|
autodoc_default_options = {
|
|
'members': True,
|
|
'member-order': 'bysource',
|
|
'special-members': '__init__',
|
|
'undoc-members': True,
|
|
'exclude-members': '__weakref__',
|
|
}
|
|
autodoc_inherit_docstrings = False
|
|
napoleon_use_ivar = True
|
|
|
|
intersphinx_mapping = {
|
|
'python': ('https://docs.python.org/3', None),
|
|
'django': ('https://docs.djangoproject.com/en/stable/',
|
|
'https://docs.djangoproject.com/en/stable/_objects/'),
|
|
}
|
|
|
|
html_theme = 'sphinx_rtd_theme'
|
|
html_static_path = ['_static']
|
|
html_theme_options = {
|
|
'navigation_depth': 4,
|
|
'collapse_navigation': False,
|
|
'sticky_navigation': True,
|
|
'includehidden': True,
|
|
'titles_only': False,
|
|
}
|