mirror of
https://github.com/jokob-sk/NetAlertX.git
synced 2026-09-13 06:36:58 -04:00
152 lines
6.2 KiB
Python
152 lines
6.2 KiB
Python
"""Generate docs pages for each plugin from its own README.md (and extras).
|
|
|
|
Runs at `mkdocs build`/`mkdocs serve` time via the `gen-files` plugin
|
|
(see mkdocs.yml). Every server/plugins/<name>/README.md becomes a virtual
|
|
page at plugins/<name>.md, so the docs site always mirrors the current
|
|
README instead of the two drifting out of sync or docs linking out to
|
|
GitHub. A plugins/SUMMARY.md is generated alongside them for the
|
|
`literate-nav` plugin, which turns it into the "Plugins reference" nav
|
|
section referenced from mkdocs.yml (`plugins/`).
|
|
|
|
Two things beyond the README text itself get carried over, since a plugin
|
|
folder can contain more than just README.md:
|
|
- Sibling images (screenshots) are copied to plugins/<slug>/<file>, and
|
|
any reference to them in the README - absolute repo path, `./relative`,
|
|
or bare filename - is rewritten to point at the copied location.
|
|
- Any other *.md file in the same folder (e.g. a provider-specific
|
|
sub-guide) gets its own generated page at plugins/<slug>/<file>.md,
|
|
added to the nav under its own first H1 heading (or the filename if it
|
|
has none).
|
|
|
|
To exclude a plugin from the generated reference (e.g. a dev scaffold,
|
|
not a real plugin), prefix its folder name with double underscores -
|
|
see the __template skip below.
|
|
"""
|
|
|
|
import json
|
|
import re
|
|
from pathlib import Path
|
|
|
|
import mkdocs_gen_files
|
|
|
|
REPO_ROOT = Path(__file__).resolve().parent.parent
|
|
PLUGINS_DIR = REPO_ROOT / "server" / "plugins"
|
|
GITHUB_BLOB_BASE = "https://github.com/netalertx/NetAlertX/blob/main"
|
|
IMAGE_EXTENSIONS = {".png", ".jpg", ".jpeg", ".gif", ".svg", ".webp"}
|
|
|
|
nav = mkdocs_gen_files.Nav()
|
|
index_entries = []
|
|
|
|
|
|
def generated_note(source_rel_to_repo):
|
|
return (
|
|
"!!! note \"Generated page\"\n"
|
|
f" This page mirrors [`{source_rel_to_repo}`]"
|
|
f"({GITHUB_BLOB_BASE}/{source_rel_to_repo}) and is regenerated on every docs build.\n\n"
|
|
)
|
|
|
|
|
|
for readme_path in sorted(PLUGINS_DIR.glob("*/README.md")):
|
|
plugin_dir = readme_path.parent
|
|
slug = plugin_dir.name
|
|
|
|
if slug.startswith("__"):
|
|
continue # dev scaffolding (e.g. __template), not a real plugin
|
|
|
|
config_path = plugin_dir / "config.json"
|
|
display_name = slug
|
|
unique_prefix = None
|
|
if config_path.exists():
|
|
try:
|
|
config = json.loads(config_path.read_text(encoding="utf-8"))
|
|
except (ValueError, OSError):
|
|
config = {}
|
|
names = config.get("display_name") or []
|
|
for entry in names:
|
|
if entry.get("language_code") == "en_us" and entry.get("string"):
|
|
display_name = entry["string"]
|
|
break
|
|
unique_prefix = config.get("unique_prefix")
|
|
|
|
title = f"{display_name} ({unique_prefix})" if unique_prefix else display_name
|
|
|
|
doc_path = f"{slug}.md" # relative to plugins/SUMMARY.md
|
|
full_doc_path = f"plugins/{doc_path}"
|
|
source_rel_to_repo = readme_path.relative_to(REPO_ROOT).as_posix()
|
|
|
|
nav[title] = doc_path
|
|
index_entries.append((title, doc_path))
|
|
|
|
readme_text = readme_path.read_text(encoding="utf-8")
|
|
|
|
# Copy sibling image assets and rewrite whatever form the README uses to
|
|
# reference them (absolute repo path, ./relative, or bare filename) to
|
|
# the copied location - only the README's *text* is otherwise pulled
|
|
# into the site, so a plugin's local screenshots would 404 silently.
|
|
for asset_path in sorted(plugin_dir.iterdir()):
|
|
if asset_path.suffix.lower() not in IMAGE_EXTENSIONS:
|
|
continue
|
|
asset_name = asset_path.name
|
|
with mkdocs_gen_files.open(f"plugins/{slug}/{asset_name}", "wb") as out:
|
|
out.write(asset_path.read_bytes())
|
|
readme_text = re.sub(
|
|
rf'\]\((?:\./|/server/plugins/{re.escape(slug)}/)?{re.escape(asset_name)}\)',
|
|
f']({slug}/{asset_name})',
|
|
readme_text,
|
|
)
|
|
|
|
with mkdocs_gen_files.open(full_doc_path, "w") as f:
|
|
f.write(f"# {title}\n\n")
|
|
f.write(generated_note(source_rel_to_repo))
|
|
f.write(readme_text)
|
|
|
|
# Point the theme's "edit this page" button at the real source file
|
|
# instead of the virtual doc path, which doesn't exist in the repo.
|
|
mkdocs_gen_files.set_edit_path(full_doc_path, f"../{source_rel_to_repo}")
|
|
|
|
# Any other markdown file alongside the README is a plugin-specific
|
|
# sub-guide (e.g. dhcp_leases/ASUS_ROUTERS.md) - give it its own page
|
|
# too, rather than leaving it undiscoverable outside GitHub.
|
|
for extra_md in sorted(plugin_dir.glob("*.md")):
|
|
if extra_md.name == "README.md":
|
|
continue
|
|
|
|
extra_text = extra_md.read_text(encoding="utf-8")
|
|
lines = extra_text.lstrip("\n").split("\n", 1)
|
|
first_line = lines[0]
|
|
if first_line.startswith("# "):
|
|
extra_title = first_line[2:].strip()
|
|
body = lines[1].lstrip("\n") if len(lines) > 1 else ""
|
|
else:
|
|
extra_title = extra_md.stem.replace("_", " ").title()
|
|
body = extra_text
|
|
|
|
extra_doc_path = f"{slug}/{extra_md.stem}.md"
|
|
extra_full_doc_path = f"plugins/{extra_doc_path}"
|
|
extra_source_rel = extra_md.relative_to(REPO_ROOT).as_posix()
|
|
|
|
nav[extra_title] = extra_doc_path
|
|
|
|
with mkdocs_gen_files.open(extra_full_doc_path, "w") as f:
|
|
f.write(f"# {extra_title}\n\n")
|
|
f.write(generated_note(extra_source_rel))
|
|
f.write(body)
|
|
|
|
# edit_path is concatenated directly after the fixed `edit_uri: blob/main/docs/`
|
|
# prefix (it is NOT relative to this generated file's own directory), so it
|
|
# always needs exactly one `../` to escape "docs/" regardless of how deeply
|
|
# nested the virtual page itself is - same as the top-level README case above.
|
|
mkdocs_gen_files.set_edit_path(extra_full_doc_path, f"../{extra_source_rel}")
|
|
|
|
with mkdocs_gen_files.open("plugins/SUMMARY.md", "w") as nav_file:
|
|
nav_file.writelines(nav.build_literate_nav())
|
|
|
|
with mkdocs_gen_files.open("plugins/index.md", "w") as index_file:
|
|
index_file.write("# Plugins reference\n\n")
|
|
index_file.write(
|
|
"Generated automatically from each plugin's `README.md`. "
|
|
"See [Plugins](../PLUGINS_OVERVIEW.md) for the type/feature legend.\n\n"
|
|
)
|
|
for title, doc_path in index_entries:
|
|
index_file.write(f"- [{title}]({doc_path})\n")
|