init
This commit is contained in:
179
build.py
Normal file
179
build.py
Normal file
@@ -0,0 +1,179 @@
|
||||
"""
|
||||
build.py - Generate static documentation site from sunvpy wiki markdown files.
|
||||
|
||||
Usage:
|
||||
python build.py [--version VERSION_ID] [--output DIR]
|
||||
"""
|
||||
|
||||
import json
|
||||
import os
|
||||
import re
|
||||
import shutil
|
||||
import sys
|
||||
from pathlib import Path
|
||||
|
||||
BASE_DIR = Path(__file__).parent
|
||||
WIKI_DIR = BASE_DIR / "wiki"
|
||||
VERSIONS_DIR = WIKI_DIR / "versions"
|
||||
|
||||
|
||||
def get_latest_version():
|
||||
"""Auto-detect latest version by sorting version directory names."""
|
||||
versions = sorted(VERSIONS_DIR.iterdir())
|
||||
if not versions:
|
||||
print("Error: No versions found in wiki/versions/", file=sys.stderr)
|
||||
sys.exit(1)
|
||||
return versions[-1].name
|
||||
|
||||
|
||||
def load_wiki_manifest(version_dir: Path) -> dict:
|
||||
"""Load wiki.json manifest."""
|
||||
manifest_path = version_dir / "wiki.json"
|
||||
if not manifest_path.exists():
|
||||
print(f"Error: wiki.json not found at {manifest_path}", file=sys.stderr)
|
||||
sys.exit(1)
|
||||
with open(manifest_path, "r", encoding="utf-8") as f:
|
||||
return json.load(f)
|
||||
|
||||
|
||||
def build_nav(pages: list) -> dict:
|
||||
"""Build navigation tree from wiki.json pages, grouped by section -> group."""
|
||||
sections_ordered = []
|
||||
sections_map = {}
|
||||
|
||||
for page in pages:
|
||||
section_title = page["section"]
|
||||
group_title = page.get("group", "")
|
||||
|
||||
if section_title not in sections_map:
|
||||
section_entry = {"id": section_title, "title": section_title}
|
||||
if group_title:
|
||||
section_entry["groups"] = []
|
||||
else:
|
||||
section_entry["pages"] = []
|
||||
sections_map[section_title] = section_entry
|
||||
sections_ordered.append(section_title)
|
||||
|
||||
section = sections_map[section_title]
|
||||
page_entry = {
|
||||
"slug": page["slug"],
|
||||
"title": page["title"],
|
||||
"file": page["file"],
|
||||
"level": page["level"],
|
||||
}
|
||||
|
||||
if group_title:
|
||||
# Find or create the group within this section
|
||||
found = None
|
||||
for g in section["groups"]:
|
||||
if g["title"] == group_title:
|
||||
found = g
|
||||
break
|
||||
if found is None:
|
||||
found = {"title": group_title, "pages": []}
|
||||
section["groups"].append(found)
|
||||
found["pages"].append(page_entry)
|
||||
else:
|
||||
section["pages"].append(page_entry)
|
||||
|
||||
nav = {"sections": [sections_map[s] for s in sections_ordered]}
|
||||
return nav
|
||||
|
||||
|
||||
def build_search_index(pages: list, version_dir: Path) -> list:
|
||||
"""Build search index from markdown files."""
|
||||
index = []
|
||||
heading_re = re.compile(r"^#{2,3}\s+(.+)$", re.MULTILINE)
|
||||
sources_re = re.compile(r"^Sources:.*$", re.MULTILINE)
|
||||
|
||||
for page in pages:
|
||||
md_path = version_dir / page["file"]
|
||||
if not md_path.exists():
|
||||
print(f" Warning: {page['file']} not found, skipping", file=sys.stderr)
|
||||
continue
|
||||
|
||||
with open(md_path, "r", encoding="utf-8") as f:
|
||||
content = f.read()
|
||||
|
||||
headings = [m.group(1).strip() for m in heading_re.finditer(content)]
|
||||
cleaned = sources_re.sub("", content).strip()
|
||||
# Truncate to control index size
|
||||
if len(cleaned) > 2000:
|
||||
cleaned = cleaned[:2000]
|
||||
|
||||
index.append({
|
||||
"slug": page["slug"],
|
||||
"title": page["title"],
|
||||
"section": page["section"],
|
||||
"headings": headings,
|
||||
"content": cleaned,
|
||||
})
|
||||
|
||||
return index
|
||||
|
||||
|
||||
def copy_content(pages: list, version_dir: Path, output_dir: Path):
|
||||
"""Copy markdown files to output/content/."""
|
||||
content_dir = output_dir / "content"
|
||||
content_dir.mkdir(parents=True, exist_ok=True)
|
||||
|
||||
for page in pages:
|
||||
src = version_dir / page["file"]
|
||||
dst = content_dir / page["file"]
|
||||
if src.exists():
|
||||
shutil.copy2(src, dst)
|
||||
|
||||
print(f" Copied {len(pages)} markdown files")
|
||||
|
||||
|
||||
def write_json(data, path: Path):
|
||||
"""Write JSON with pretty formatting."""
|
||||
path.parent.mkdir(parents=True, exist_ok=True)
|
||||
with open(path, "w", encoding="utf-8") as f:
|
||||
json.dump(data, f, ensure_ascii=False, indent=2)
|
||||
|
||||
|
||||
def main():
|
||||
import argparse
|
||||
parser = argparse.ArgumentParser(description="Build sunvpy docs site")
|
||||
parser.add_argument("--version", default=None, help="Version ID (default: latest)")
|
||||
parser.add_argument("--output", default=None, help="Output directory (default: .zread/docs/)")
|
||||
args = parser.parse_args()
|
||||
|
||||
version_id = args.version or get_latest_version()
|
||||
version_dir = VERSIONS_DIR / version_id
|
||||
output_dir = Path(args.output) if args.output else BASE_DIR / "docs"
|
||||
|
||||
print(f"Building docs from version: {version_id}")
|
||||
print(f"Source: {version_dir}")
|
||||
print(f"Output: {output_dir}")
|
||||
|
||||
# Clean output directory (only data and content, not css/js/html)
|
||||
for subdir in ["data", "content"]:
|
||||
d = output_dir / subdir
|
||||
if d.exists():
|
||||
shutil.rmtree(d)
|
||||
|
||||
# Load manifest
|
||||
manifest = load_wiki_manifest(version_dir)
|
||||
pages = manifest["pages"]
|
||||
print(f"Found {len(pages)} pages")
|
||||
|
||||
# Build nav.json
|
||||
nav = build_nav(pages)
|
||||
write_json(nav, output_dir / "data" / "nav.json")
|
||||
print(" Generated data/nav.json")
|
||||
|
||||
# Build search index
|
||||
search_index = build_search_index(pages, version_dir)
|
||||
write_json(search_index, output_dir / "data" / "search-index.json")
|
||||
print(f" Generated data/search-index.json ({len(search_index)} entries)")
|
||||
|
||||
# Copy markdown content
|
||||
copy_content(pages, version_dir, output_dir)
|
||||
|
||||
print("Build complete!")
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
Reference in New Issue
Block a user