Files
openmlsys-zh/tools/assemble_docs_publish_tree.py
Yeqi Huang d953030747 feat: add v1/v2 versioning with language selector (#494)
* feat: add v1/v2 versioning and language selector for mdbook

- Copy current content to v1/ directory (1st Edition)
- Create v2/ directory with new TOC structure (2nd Edition) and placeholder chapters
- Add version selector (V1/V2) and language toggle (EN/ZH) in top-right nav bar
- Add build scripts: build_mdbook_v1.sh, build_mdbook_v2.sh
- Update assemble_docs_publish_tree.py to support v1/v2 deployment layout
- Fix mdbook preprocessor to use 'sections' key (v0.4.43 compatibility)
- Update .gitignore for new build artifact directories
- Deployment layout: / = v2 EN, /cn/ = v2 ZH, /v1/ = v1 EN, /v1/cn/ = v1 ZH

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

* build: update CI to build and verify all four books (v1/v2 x EN/ZH)

- Clarify step names: "Build v2 (EN + ZH)" and "Build v1 (EN + ZH)"
- Add verification step to check all four index.html outputs exist
- Deploy workflow assembles: / = v2 EN, /cn/ = v2 ZH, /v1/ = v1 EN, /v1/cn/ = v1 ZH

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

* fix: gracefully skip missing TOC entries instead of crashing

resolve_toc_target() now returns None for missing files instead of
raising FileNotFoundError. This fixes v1 EN build where chapter index
files reference TOC entry names that don't match actual filenames.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

---------

Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-12 13:37:42 +00:00

136 lines
3.9 KiB
Python

from __future__ import annotations
import argparse
import shutil
from pathlib import Path
def remove_path(path: Path) -> None:
if path.is_symlink() or path.is_file():
path.unlink()
return
if path.is_dir():
shutil.rmtree(path)
def copy_site(source: Path, destination: Path) -> None:
source = source.resolve()
if not source.is_dir():
raise FileNotFoundError(f"Site source does not exist or is not a directory: {source}")
remove_path(destination)
destination.parent.mkdir(parents=True, exist_ok=True)
shutil.copytree(source, destination)
def assemble_publish_tree(
destination_root: Path,
docs_subdir: str = "docs",
en_source: Path | None = None,
zh_source: Path | None = None,
v1_en_source: Path | None = None,
v1_zh_source: Path | None = None,
v2_en_source: Path | None = None,
v2_zh_source: Path | None = None,
) -> tuple[Path, Path | None]:
if en_source is None and zh_source is None and v2_en_source is None:
raise ValueError("At least one site source must be provided.")
destination_root = destination_root.resolve()
docs_root = (destination_root / docs_subdir).resolve()
remove_path(docs_root)
docs_root.parent.mkdir(parents=True, exist_ok=True)
# v2 (latest) is deployed at the root — /docs/
effective_en = v2_en_source or en_source
if effective_en is not None:
copy_site(effective_en, docs_root)
else:
docs_root.mkdir(parents=True, exist_ok=True)
zh_destination: Path | None = None
effective_zh = v2_zh_source or zh_source
if effective_zh is not None:
zh_destination = docs_root / "cn"
copy_site(effective_zh, zh_destination)
# v1 is deployed under /docs/v1/
if v1_en_source is not None:
v1_root = docs_root / "v1"
copy_site(v1_en_source, v1_root)
if v1_zh_source is not None:
copy_site(v1_zh_source, v1_root / "cn")
return docs_root, zh_destination
def parse_args() -> argparse.Namespace:
parser = argparse.ArgumentParser(
description="Assemble the publish tree expected by openmlsys.github.io."
)
parser.add_argument(
"--destination-root",
type=Path,
required=True,
help="Root of the checked-out deployment repository.",
)
parser.add_argument(
"--docs-subdir",
default="docs",
help="Subdirectory inside the destination root that hosts the site.",
)
parser.add_argument(
"--en-source",
type=Path,
help="Built site to publish at docs/ (legacy, use --v2-en-source instead).",
)
parser.add_argument(
"--zh-source",
type=Path,
help="Built site to publish at docs/cn/ (legacy, use --v2-zh-source instead).",
)
parser.add_argument(
"--v1-en-source",
type=Path,
help="Built v1 English site to publish at docs/v1/.",
)
parser.add_argument(
"--v1-zh-source",
type=Path,
help="Built v1 Chinese site to publish at docs/v1/cn/.",
)
parser.add_argument(
"--v2-en-source",
type=Path,
help="Built v2 English site to publish at docs/.",
)
parser.add_argument(
"--v2-zh-source",
type=Path,
help="Built v2 Chinese site to publish at docs/cn/.",
)
return parser.parse_args()
def main() -> int:
args = parse_args()
docs_root, zh_root = assemble_publish_tree(
destination_root=args.destination_root,
docs_subdir=args.docs_subdir,
en_source=args.en_source,
zh_source=args.zh_source,
v1_en_source=args.v1_en_source,
v1_zh_source=args.v1_zh_source,
v2_en_source=args.v2_en_source,
v2_zh_source=args.v2_zh_source,
)
print(f"Assembled root site at {docs_root}")
if zh_root is not None:
print(f"Assembled Chinese site at {zh_root}")
return 0
if __name__ == "__main__":
raise SystemExit(main())