Co-authored-by: Google AI Agent <gemini@hermes.local> Co-committed-by: Google AI Agent <gemini@hermes.local>
164 lines
4.6 KiB
Python
Executable File
164 lines
4.6 KiB
Python
Executable File
#!/usr/bin/env python3
|
|
"""
|
|
The Testament — Book Compilation Pipeline
|
|
|
|
Compiles all chapters into a single manuscript and generates:
|
|
- PDF (print-ready)
|
|
- EPUB (e-reader)
|
|
|
|
Requirements:
|
|
- pandoc (brew install pandoc / apt install pandoc)
|
|
- TeX Live or similar for PDF (brew install --cask mactex / apt install texlive-full)
|
|
|
|
Usage:
|
|
python3 build/build.py # Build all formats
|
|
python3 build/build.py --pdf # PDF only
|
|
python3 build/build.py --epub # EPUB only
|
|
python3 build/build.py --md # Combined markdown only
|
|
"""
|
|
|
|
import subprocess
|
|
import sys
|
|
import os
|
|
from pathlib import Path
|
|
|
|
ROOT = Path(__file__).parent.parent
|
|
BUILD = ROOT / "build"
|
|
CHAPTERS_DIR = ROOT / "chapters"
|
|
OUTPUT_DIR = BUILD / "output"
|
|
|
|
def find_chapters():
|
|
"""Find all chapter files, sorted by number."""
|
|
chapters = sorted(CHAPTERS_DIR.glob("chapter-*.md"))
|
|
if not chapters:
|
|
print("ERROR: No chapter files found in", CHAPTERS_DIR)
|
|
sys.exit(1)
|
|
return chapters
|
|
|
|
|
|
def validate_chapters(chapters):
|
|
"""Perform basic validation on chapter content."""
|
|
print("Validating chapters...")
|
|
errors = 0
|
|
for ch in chapters:
|
|
content = ch.read_text()
|
|
# 1. Check for empty chapters
|
|
if len(content.strip()) < 100:
|
|
print(f" [WARN] {ch.name} seems too short ({len(content)} chars)")
|
|
|
|
# 2. Check for missing headers
|
|
if not content.startswith("# "):
|
|
print(f" [ERROR] {ch.name} is missing a primary H1 header")
|
|
errors += 1
|
|
|
|
# 3. Check for broken internal links (e.g. [Link](#missing))
|
|
# (Simplified check for now)
|
|
if "](#" in content:
|
|
print(f" [INFO] {ch.name} contains internal links. Verify anchors manually.")
|
|
|
|
return errors == 0
|
|
|
|
def main():
|
|
# ... existing logic ...
|
|
|
|
def combine_markdown(chapters):
|
|
"""Combine all parts into a single markdown file."""
|
|
parts = []
|
|
|
|
# Front matter
|
|
front = BUILD / "frontmatter.md"
|
|
if front.exists():
|
|
parts.append(front.read_text())
|
|
|
|
# Chapters
|
|
for ch in chapters:
|
|
parts.append(ch.read_text())
|
|
|
|
# Back matter
|
|
back = BUILD / "backmatter.md"
|
|
if back.exists():
|
|
parts.append(back.read_text())
|
|
|
|
combined = "\n\n\newpage\n\n".join(parts)
|
|
output = BUILD / "the-testament-full.md"
|
|
output.write_text(combined)
|
|
print(f"Combined markdown: {output} ({len(combined)} chars)")
|
|
return output
|
|
|
|
def build_pdf(md_file):
|
|
"""Build PDF using pandoc + LaTeX."""
|
|
OUTPUT_DIR.mkdir(parents=True, exist_ok=True)
|
|
output = OUTPUT_DIR / "the-testament.pdf"
|
|
metadata = BUILD / "metadata.yaml"
|
|
|
|
cmd = [
|
|
"pandoc",
|
|
str(md_file),
|
|
"-o", str(output),
|
|
"--metadata-file", str(metadata),
|
|
"--pdf-engine=xelatex",
|
|
"--highlight-style=tango",
|
|
"-V", "colorlinks=true",
|
|
"-V", "linkcolor=blue",
|
|
"-V", "urlcolor=blue",
|
|
]
|
|
|
|
print("Building PDF...")
|
|
result = subprocess.run(cmd, capture_output=True, text=True)
|
|
if result.returncode != 0:
|
|
print(f"PDF build failed:\n{result.stderr}")
|
|
return False
|
|
print(f"PDF: {output} ({output.stat().st_size / 1024:.0f} KB)")
|
|
return True
|
|
|
|
def build_epub(md_file):
|
|
"""Build EPUB using pandoc."""
|
|
OUTPUT_DIR.mkdir(parents=True, exist_ok=True)
|
|
output = OUTPUT_DIR / "the-testament.epub"
|
|
metadata = BUILD / "metadata.yaml"
|
|
|
|
cmd = [
|
|
"pandoc",
|
|
str(md_file),
|
|
"-o", str(output),
|
|
"--metadata-file", str(metadata),
|
|
"--toc",
|
|
"--epub-chapter-level=1",
|
|
]
|
|
|
|
print("Building EPUB...")
|
|
result = subprocess.run(cmd, capture_output=True, text=True)
|
|
if result.returncode != 0:
|
|
print(f"EPUB build failed:\n{result.stderr}")
|
|
return False
|
|
print(f"EPUB: {output} ({output.stat().st_size / 1024:.0f} KB)")
|
|
return True
|
|
|
|
def main():
|
|
args = set(sys.argv[1:])
|
|
build_all = not args or "--all" in args
|
|
|
|
chapters = find_chapters()
|
|
if not validate_chapters(chapters):
|
|
print("Validation failed. Aborting build.")
|
|
sys.exit(1)
|
|
print(f"Found {len(chapters)} chapters")
|
|
|
|
md_file = combine_markdown(chapters)
|
|
|
|
if build_all or "--md" in args:
|
|
print("Markdown combined successfully.")
|
|
|
|
if build_all or "--pdf" in args:
|
|
if not build_pdf(md_file):
|
|
print("PDF build failed (pandoc/LaTeX may not be installed). Skipping.")
|
|
|
|
if build_all or "--epub" in args:
|
|
if not build_epub(md_file):
|
|
print("EPUB build failed (pandoc may not be installed). Skipping.")
|
|
|
|
print("\nDone.")
|
|
|
|
if __name__ == "__main__":
|
|
main()
|