diff --git a/.github/scripts/build-pages.py b/.github/scripts/build-pages.py new file mode 100644 index 0000000..743eb9b --- /dev/null +++ b/.github/scripts/build-pages.py @@ -0,0 +1,111 @@ +#!/usr/bin/env python3 +"""Build the docs Markdown files into a static GitHub Pages site. + +This intentionally avoids themed site generators and template files. Each Markdown +file is converted to a minimal standalone HTML page, and relative .md links are +rewritten to the generated .html filenames. +""" + +from __future__ import annotations + +import html +import re +import shutil +from pathlib import Path, PurePosixPath +from urllib.parse import urlsplit, urlunsplit + +import markdown + +ROOT = Path(__file__).resolve().parents[2] +DOCS_DIR = ROOT / "docs" +SITE_DIR = ROOT / "site" + +MARKDOWN_EXTENSIONS = ["fenced_code", "tables", "toc"] +HREF_RE = re.compile(r'href="([^"]+)"') + + +def output_path(source: Path) -> Path: + if source.name == "README.md": + return SITE_DIR / "index.html" + return SITE_DIR / f"{source.stem}.html" + + +def page_title(text: str, fallback: str) -> str: + for line in text.splitlines(): + if line.startswith("# "): + return line[2:].strip() + return fallback + + +def rewrite_markdown_links(rendered: str) -> str: + def replace(match: re.Match[str]) -> str: + href = html.unescape(match.group(1)) + parts = urlsplit(href) + if parts.scheme or parts.netloc or not parts.path.endswith(".md"): + return match.group(0) + + url_path = PurePosixPath(parts.path) + if url_path.name == "README.md": + new_path = str(url_path.with_name("index.html")) + else: + new_path = parts.path[:-3] + ".html" + + new_href = urlunsplit(("", "", new_path, parts.query, parts.fragment)) + return f'href="{html.escape(new_href, quote=True)}"' + + return HREF_RE.sub(replace, rendered) + + +def render_page(source: Path) -> str: + text = source.read_text(encoding="utf-8") + title = page_title(text, "Cassady docs") + body = markdown.markdown( + text, + extensions=MARKDOWN_EXTENSIONS, + output_format="html5", + ) + body = rewrite_markdown_links(body) + + return "\n".join( + [ + "", + '', + "", + ' ', + ' ', + f" {html.escape(title)}", + "", + "", + body, + "", + "", + "", + ] + ) + + +def copy_static_assets() -> None: + for item in DOCS_DIR.iterdir(): + if item.suffix == ".md": + continue + destination = SITE_DIR / item.name + if item.is_dir(): + shutil.copytree(item, destination) + elif item.is_file(): + shutil.copy2(item, destination) + + +def main() -> None: + if SITE_DIR.exists(): + shutil.rmtree(SITE_DIR) + SITE_DIR.mkdir(parents=True) + + for source in sorted(DOCS_DIR.glob("*.md")): + output_path(source).write_text(render_page(source), encoding="utf-8") + + copy_static_assets() + (SITE_DIR / ".nojekyll").write_text("", encoding="utf-8") + + +if __name__ == "__main__": + main() diff --git a/.github/workflows/pages.yml b/.github/workflows/pages.yml index c44362b..8eef3c2 100644 --- a/.github/workflows/pages.yml +++ b/.github/workflows/pages.yml @@ -1,4 +1,4 @@ -name: Publish MkDocs site +name: Publish Markdown site on: push: @@ -6,12 +6,12 @@ on: - main paths: - 'docs/**' - - 'docs/mkdocs.yml' + - '.github/scripts/build-pages.py' - '.github/workflows/pages.yml' pull_request: paths: - 'docs/**' - - 'docs/mkdocs.yml' + - '.github/scripts/build-pages.py' - '.github/workflows/pages.yml' workflow_dispatch: @@ -26,7 +26,7 @@ concurrency: jobs: build: - name: Build MkDocs site + name: Build Markdown site runs-on: ubuntu-latest steps: - name: Check out repository @@ -37,11 +37,11 @@ jobs: with: python-version: '3.x' - - name: Install MkDocs - run: python -m pip install mkdocs-material + - name: Install Markdown converter + run: python -m pip install Markdown - name: Build site - run: python -m mkdocs build --strict -f docs/mkdocs.yml + run: python .github/scripts/build-pages.py - name: Configure Pages if: github.event_name != 'pull_request' && github.ref == 'refs/heads/main' diff --git a/docs/mkdocs.yml b/docs/mkdocs.yml deleted file mode 100644 index 04c3af9..0000000 --- a/docs/mkdocs.yml +++ /dev/null @@ -1,37 +0,0 @@ -site_name: Cassady -site_url: "https://owenqwenstarsky.github.io/cassady/" -site_description: "Cassady ('cass') is a terminal coding agent written in Rust." -site_author: Owen Qwen -docs_dir: . -site_dir: ../site -repo_url: https://github.com/owenqwenstarsky/cassady -repo_name: owenqwenstarsky/cassady -nav: - - Home: README.md - - Getting Started: - - Workflows: workflows.md - - Platform Notes: platforms.md - - Setup and Configuration: - - Configuration: configuration.md - - Providers and Models: providers.md - - Usage Reference: - - Commands: commands.md - - Access Modes and Tool Safety: access-modes.md - - Glossary: glossary.md - - Experimental: - - Rust API: embedding.md - - Help: - - Troubleshooting: troubleshooting.md -extra: - version: 0.2.9 - social: - - icon: fontawesome/brands/github - link: https://github.com/owenqwenstarsky/cassady -theme: - name: material - favicon: img/favicon.ico - features: - - navigation.sections - - navigation.expand - - navigation.top - - toc.follow