diff --git a/.github/workflows/publish-docs.yml b/.github/workflows/publish-docs.yml index 93daaaf..b6282b6 100644 --- a/.github/workflows/publish-docs.yml +++ b/.github/workflows/publish-docs.yml @@ -39,7 +39,7 @@ jobs: - name: Build Jekyll source tree run: | - python scripts/build_pages_site.py --output _site_src + mkpages build docs/ --output _site_src - name: Build site with Jekyll uses: actions/jekyll-build-pages@v1 diff --git a/.gitignore b/.gitignore index abd4cb4..b0327cb 100644 --- a/.gitignore +++ b/.gitignore @@ -19,4 +19,5 @@ baketest.env out.env .codex .agents/ -_site* \ No newline at end of file +_site* +.mkpages/ \ No newline at end of file diff --git a/README.md b/README.md index b0fa662..10c0d58 100644 --- a/README.md +++ b/README.md @@ -161,4 +161,4 @@ pathbase match '/mnt/projects/demo/report.txt' Additional documentation lives in [docs/index.md](docs/index.md): -- [Examples](docs/examples.md) +- [Examples](docs/examples/index.md) diff --git a/docs/api.md b/docs/api.md index d5dee35..342a7b6 100644 --- a/docs/api.md +++ b/docs/api.md @@ -1,3 +1,9 @@ +--- +layout: default +title: Pathbase API +description: Python API patterns for formatting, parsing, and matching templates. +--- + # Pathbase API This guide focuses on the Python API surface that is most useful when diff --git a/docs/distribution.md b/docs/distribution.md index 581c3d8..75c1a2f 100644 --- a/docs/distribution.md +++ b/docs/distribution.md @@ -1,3 +1,9 @@ +--- +layout: default +title: Distribution +description: How pathbase example environments are distributed with distman. +--- + # Distribution `pathbase` includes a `dist.json` file for use with diff --git a/examples/animation/README.md b/docs/examples/animation/index.md similarity index 64% rename from examples/animation/README.md rename to docs/examples/animation/index.md index 037c25e..11f748f 100644 --- a/examples/animation/README.md +++ b/docs/examples/animation/index.md @@ -1,3 +1,9 @@ +--- +layout: default +title: Animation Example +description: Example animation asset and publish templates for pathbase. +--- + # Animation Example This example uses asset, department, and version-style naming common in @@ -15,6 +21,13 @@ Example filepath: /mnt/projects/bigbuckbunny/assets/bunny/model/bunny_publish_v003.usd ``` +Example env file: +[examples/animation/pathbase.env](https://github.com/rsgalloway/pathbase/blob/master/examples/animation/pathbase.env) + +```bash +curl -L https://raw.githubusercontent.com/rsgalloway/pathbase/master/examples/animation/pathbase.env -o pathbase.env +``` + Try it: ```bash diff --git a/examples/data-pipeline/README.md b/docs/examples/data-pipeline/index.md similarity index 61% rename from examples/data-pipeline/README.md rename to docs/examples/data-pipeline/index.md index 7ce2558..2552364 100644 --- a/examples/data-pipeline/README.md +++ b/docs/examples/data-pipeline/index.md @@ -1,3 +1,9 @@ +--- +layout: default +title: Data Pipeline Example +description: Example partitioned dataset templates for pathbase. +--- + # Data Pipeline Example This example shows a partitioned data layout for datasets, dates, and shard @@ -15,6 +21,13 @@ Example filepath: /mnt/data/orders/2026-07-26/region-us-west/part-0007.parquet ``` +Example env file: +[examples/data-pipeline/pathbase.env](https://github.com/rsgalloway/pathbase/blob/master/examples/data-pipeline/pathbase.env) + +```bash +curl -L https://raw.githubusercontent.com/rsgalloway/pathbase/master/examples/data-pipeline/pathbase.env -o pathbase.env +``` + Try it: ```bash diff --git a/docs/examples.md b/docs/examples/index.md similarity index 92% rename from docs/examples.md rename to docs/examples/index.md index ed8e86d..8dda737 100644 --- a/docs/examples.md +++ b/docs/examples/index.md @@ -1,16 +1,29 @@ +--- +layout: default +title: Pathbase Examples +description: Example env layouts and parsing patterns for common pathbase workflows. +--- + # Pathbase Examples +This section collects example `pathbase.env` layouts for different industries +and use cases. The example env files themselves still live under `examples/` in +the repository. + ## Example Flavors For concrete, domain-specific `pathbase.env` examples, see: -- [Examples Overview](/examples/README.md) -- [VFX](/examples/vfx/README.md) -- [Animation](/examples/animation/README.md) -- [Data Pipeline](/examples/data-pipeline/README.md) -- [Logs](/examples/logs/README.md) -- [ML Artifacts](/examples/ml-artifacts/README.md) -- [Overrides](/examples/overrides/README.md) +- [VFX](vfx/) +- [Animation](animation/) +- [Data Pipeline](data-pipeline/) +- [Logs](logs/) +- [ML Artifacts](ml-artifacts/) +- [Overrides](overrides/) + +These examples are not part of the `pathbase` core API. They are intended to +show different ways to model path templates without locking the library to one +domain. ## Parse a Filepath Without Knowing the Template @@ -47,7 +60,7 @@ without manually specifying the template name. The same auto-discovery model is available in Python API integrations via `match_template(path, env=...)` and `Template.from_path(path, env=...)`. See -[API](api.md) for examples. +[API](../api.md) for examples. If `envstack` is not installed, `pathbase` can still discover templates from plain environment variables: @@ -432,17 +445,16 @@ Expected output: ## Envstack Example -The repository includes several domain-specific env examples under -[examples/README.md](../examples/README.md), showing how template strings can -be supplied from environment configuration while keeping `pathbase` itself -dependency-free. +The repository includes several domain-specific env examples under this +section, showing how template strings can be supplied from environment +configuration while keeping `pathbase` itself dependency-free. For the shared-defaults plus project-overrides pattern, see -[Overrides](overrides.md). +[Overrides](../overrides.md). For `distman` target selection and deployment examples, including selecting an example flavor while always deploying to the same `pathbase.env` destination, see -[Distribution](distribution.md). +[Distribution](../distribution.md). ## CLI Examples diff --git a/examples/logs/README.md b/docs/examples/logs/index.md similarity index 60% rename from examples/logs/README.md rename to docs/examples/logs/index.md index 0034021..25954d5 100644 --- a/examples/logs/README.md +++ b/docs/examples/logs/index.md @@ -1,3 +1,9 @@ +--- +layout: default +title: Logs Example +description: Example service log storage templates for pathbase. +--- + # Logs Example This example models service log storage grouped by environment, service, date, @@ -15,6 +21,13 @@ Example filepath: /var/log/prod/render-api/2026-07-26/error.log ``` +Example env file: +[examples/logs/pathbase.env](https://github.com/rsgalloway/pathbase/blob/master/examples/logs/pathbase.env) + +```bash +curl -L https://raw.githubusercontent.com/rsgalloway/pathbase/master/examples/logs/pathbase.env -o pathbase.env +``` + Try it: ```bash diff --git a/examples/ml-artifacts/README.md b/docs/examples/ml-artifacts/index.md similarity index 60% rename from examples/ml-artifacts/README.md rename to docs/examples/ml-artifacts/index.md index b739479..f88c079 100644 --- a/examples/ml-artifacts/README.md +++ b/docs/examples/ml-artifacts/index.md @@ -1,3 +1,9 @@ +--- +layout: default +title: ML Artifacts Example +description: Example experiment artifact templates for pathbase. +--- + # ML Artifacts Example This example stores experiment outputs under project, experiment, run, and @@ -15,6 +21,13 @@ Example filepath: /mnt/ml/pathbase/baseline/run-001/metrics.json ``` +Example env file: +[examples/ml-artifacts/pathbase.env](https://github.com/rsgalloway/pathbase/blob/master/examples/ml-artifacts/pathbase.env) + +```bash +curl -L https://raw.githubusercontent.com/rsgalloway/pathbase/master/examples/ml-artifacts/pathbase.env -o pathbase.env +``` + Try it: ```bash diff --git a/examples/overrides/README.md b/docs/examples/overrides/index.md similarity index 56% rename from examples/overrides/README.md rename to docs/examples/overrides/index.md index d520f0f..cb12bbd 100644 --- a/examples/overrides/README.md +++ b/docs/examples/overrides/index.md @@ -1,10 +1,25 @@ +--- +layout: default +title: Overrides Example +description: Example shared and project-specific override templates for pathbase. +--- + # Overrides Example This folder demonstrates a shared `pathbase.env` plus a higher-priority project-specific override. -- [Shared](shared/pathbase.env) -- [Big Buck Bunny Override](bigbuckbunny/pathbase.env) +- Shared env file: `examples/overrides/shared/pathbase.env` +- Big Buck Bunny override: `examples/overrides/bigbuckbunny/pathbase.env` + +GitHub source: +- [examples/overrides/shared/pathbase.env](https://github.com/rsgalloway/pathbase/blob/master/examples/overrides/shared/pathbase.env) +- [examples/overrides/bigbuckbunny/pathbase.env](https://github.com/rsgalloway/pathbase/blob/master/examples/overrides/bigbuckbunny/pathbase.env) + +```bash +curl -L https://raw.githubusercontent.com/rsgalloway/pathbase/master/examples/overrides/shared/pathbase.env -o shared.pathbase.env +curl -L https://raw.githubusercontent.com/rsgalloway/pathbase/master/examples/overrides/bigbuckbunny/pathbase.env -o bigbuckbunny.pathbase.env +``` Shared template example: diff --git a/examples/vfx/README.md b/docs/examples/vfx/index.md similarity index 68% rename from examples/vfx/README.md rename to docs/examples/vfx/index.md index 522da9c..9fbdec7 100644 --- a/examples/vfx/README.md +++ b/docs/examples/vfx/index.md @@ -1,3 +1,9 @@ +--- +layout: default +title: VFX Example +description: Example visual effects path templates for pathbase. +--- + # VFX Example This example models a common visual effects layout with show, sequence, shot, @@ -15,6 +21,13 @@ Example filepath: /mnt/projects/bigbuckbunny/seq001/shot010/lighting/render_beauty_v001.1001.exr ``` +Example env file: +[examples/vfx/pathbase.env](https://github.com/rsgalloway/pathbase/blob/master/examples/vfx/pathbase.env) + +```bash +curl -L https://raw.githubusercontent.com/rsgalloway/pathbase/master/examples/vfx/pathbase.env -o pathbase.env +``` + Try it: ```bash diff --git a/docs/index.md b/docs/index.md index 224b7cf..86e371a 100644 --- a/docs/index.md +++ b/docs/index.md @@ -1,3 +1,9 @@ +--- +layout: default +title: Pathbase Docs +description: Overview and quick-start examples for pathbase path templates. +--- + # Pathbase Docs `pathbase` is a lightweight Python library for formatting paths, parsing paths diff --git a/docs/mkpages.yml b/docs/mkpages.yml new file mode 100644 index 0000000..998ba58 --- /dev/null +++ b/docs/mkpages.yml @@ -0,0 +1,18 @@ +title: pathbase +description: Lightweight bidirectional filesystem path templates for Python. +theme: default +navigation: + - label: Home + href: / + - label: API + href: /api/ + - label: Examples + href: /examples/ + - label: Distribution + href: /distribution/ + - label: Overrides + href: /overrides/ + - label: GitHub + href: https://github.com/rsgalloway/pathbase + - label: PyPI + href: https://pypi.org/project/pathbase/ diff --git a/docs/overrides.md b/docs/overrides.md index b929d29..ce7d349 100644 --- a/docs/overrides.md +++ b/docs/overrides.md @@ -1,3 +1,9 @@ +--- +layout: default +title: Overrides +description: Patterns for layering shared and project-specific pathbase templates. +--- + # Overrides `pathbase` works especially well with envstack-managed environments, where diff --git a/examples/README.md b/examples/README.md deleted file mode 100644 index 81b16db..0000000 --- a/examples/README.md +++ /dev/null @@ -1,17 +0,0 @@ -# Pathbase Example Flavors - -This folder contains example `pathbase.env` files for different industries and -use cases. - -Available examples: - -- [VFX](vfx/README.md) -- [Animation](animation/README.md) -- [Data Pipeline](data-pipeline/README.md) -- [Logs](logs/README.md) -- [ML Artifacts](ml-artifacts/README.md) -- [Overrides](overrides/README.md) - -These examples are not part of the `pathbase` core API. They are intended to -show different ways to model path templates without locking the library to one -domain. diff --git a/pyproject.toml b/pyproject.toml index a2a8a43..87b3206 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -39,7 +39,7 @@ classifiers = [ "Topic :: Software Development :: Libraries :: Python Modules", "Topic :: Utilities", ] -optional-dependencies = { dev = ["pytest", "flake8==7.1.1", "mccabe==0.7.0", "isort==5.13.2", "black==24.8.0"], test = ["pytest"] } +optional-dependencies = { dev = ["pytest", "flake8==7.1.1", "mccabe==0.7.0", "isort==5.13.2", "black==24.8.0", "mkpages"], test = ["pytest"] } [project.scripts] pathbase = "pathbase.cli:main" diff --git a/scripts/build_pages_site.py b/scripts/build_pages_site.py deleted file mode 100644 index 631eb33..0000000 --- a/scripts/build_pages_site.py +++ /dev/null @@ -1,429 +0,0 @@ -#!/usr/bin/env python3 -# -# Copyright (c) 2024-2026, Ryan Galloway (ryan@rsgalloway.com) -# - -"""Build a simple Jekyll-friendly docs site from repository markdown files. - -The published Pages routes map directly to repository markdown sources: - -- ``/`` comes from ``docs/index.md`` -- ``/docs//`` comes from ``docs/.md`` -- ``/examples/`` comes from ``examples/README.md`` -- ``/examples//`` comes from ``examples//README.md`` -""" - -import argparse -import re -import shutil -from pathlib import Path -from typing import Iterable - -README_LINK_RE = re.compile(r"\(([^)#]*?)README\.md(#.*?)?\)") -MARKDOWN_LINK_RE = re.compile(r"\(([^:)#][^)]*?)\.md(#.*?)?\)") - - -def rewrite_links(content: str) -> str: - """Rewrite local markdown links for generated HTML output.""" - updated = README_LINK_RE.sub(r"(\1index.html\2)", content) - updated = MARKDOWN_LINK_RE.sub(r"(\1.html\2)", updated) - return updated - - -def extract_title(content: str, fallback: str) -> str: - """Extract the first markdown H1 title or use a fallback.""" - for line in content.splitlines(): - if line.startswith("# "): - return line[2:].strip() - return fallback - - -def wrap_markdown(content: str, title: str) -> str: - """Add minimal Jekyll front matter to markdown content.""" - return f"---\nlayout: default\ntitle: {title}\n---\n\n{content}" - - -def write_markdown_page(src: Path, dst: Path, fallback_title: str) -> None: - """Copy a markdown file into the site tree with front matter and fixed links.""" - content = src.read_text(encoding="utf-8") - title = extract_title(content, fallback_title) - content = rewrite_links(content) - dst.parent.mkdir(parents=True, exist_ok=True) - dst.write_text(wrap_markdown(content, title), encoding="utf-8") - - -def write_site_config(output_dir: Path) -> None: - """Write a minimal Jekyll config file.""" - config = """title: pathbase -description: Lightweight bidirectional filesystem path templates for Python -markdown: kramdown -permalink: pretty -""" - (output_dir / "_config.yml").write_text(config, encoding="utf-8") - - -def write_layout(output_dir: Path) -> None: - """Write the shared Jekyll layout used by the generated docs site.""" - layout_dir = output_dir / "_layouts" - layout_dir.mkdir(parents=True, exist_ok=True) - template = """ - - - - - {% if page.title %}{{ page.title }} | {% endif %}{{ site.title }} - - - - -
- -
- {{ content }} -
-
- - - -""" - (layout_dir / "default.html").write_text(template, encoding="utf-8") - - -def write_stylesheet(output_dir: Path) -> None: - """Write a minimal light stylesheet for the generated docs site.""" - assets_dir = output_dir / "assets" - assets_dir.mkdir(parents=True, exist_ok=True) - css = """:root { - --bg: #f5f8fc; - --panel: #ffffff; - --panel-strong: #e8eef6; - --border: #c9d4e2; - --text: #122033; - --muted: #4f5f73; - --accent: #0b6bcb; - --accent-dark: #084f98; - --code-bg: #0f172a; - --code-border: #1e293b; - --code-text: #e2e8f0; - --code-muted: #93a4ba; -} - -* { box-sizing: border-box; } - -html, body { - margin: 0; - padding: 0; - background: var(--bg); - color: var(--text); - font-family: "Segoe UI", "Helvetica Neue", Arial, sans-serif; - line-height: 1.7; -} - -a { - color: var(--accent-dark); - text-decoration: none; -} - -a:hover { - color: var(--accent); -} - -.site-shell { - max-width: 1100px; - margin: 0 auto; - padding: 24px 24px 72px; -} - -.site-header { - display: flex; - flex-wrap: wrap; - gap: 16px 24px; - align-items: center; - justify-content: space-between; - margin: 12px 0 36px; - padding: 18px 22px; - background: rgba(255, 255, 255, 0.92); - border: 1px solid var(--border); - border-radius: 18px; - box-shadow: 0 14px 34px rgba(15, 23, 42, 0.06); - backdrop-filter: blur(8px); -} - -.site-brand { - color: var(--text); - font-size: 1rem; - font-weight: 700; - letter-spacing: 0.01em; -} - -.site-nav { - display: flex; - flex-wrap: wrap; - gap: 16px; -} - -.site-nav a { - color: var(--muted); - font-size: 0.95rem; -} - -.site-nav a:hover { - color: var(--text); -} - -.site-main { - background: var(--panel); - border: 1px solid var(--border); - border-radius: 28px; - box-shadow: 0 24px 56px rgba(15, 23, 42, 0.08); - padding: 42px 48px 56px; -} - -.site-main h1:first-child, -.site-main p:first-child img { - margin-top: 0; -} - -h1, h2, h3 { - color: var(--text); - line-height: 1.15; -} - -h1 { - font-size: 2.8rem; - margin: 0 0 1rem; -} - -h2 { - font-size: 1.5rem; - margin-top: 2.5rem; -} - -h3 { - font-size: 1.08rem; - margin-top: 1.5rem; -} - -p, li { - font-size: 1.02rem; -} - -code, pre { - font-family: "SFMono-Regular", Consolas, "Liberation Mono", monospace; -} - -code { - background: #eef4fb; - border: 1px solid #d8e3ef; - border-radius: 8px; - padding: 0.12rem 0.4rem; -} - -pre { - background: linear-gradient(180deg, #111b31 0%, #0f172a 100%); - border: 1px solid var(--code-border); - border-radius: 18px; - box-shadow: inset 0 1px 0 rgba(255, 255, 255, 0.04); - color: var(--code-text); - overflow-x: auto; - padding: 22px 22px 20px; - position: relative; -} - -pre code { - background: transparent; - border: 0; - color: var(--code-text); - padding: 0; -} - -.copy-button { - appearance: none; - background: rgba(148, 163, 184, 0.12); - border: 1px solid rgba(148, 163, 184, 0.28); - border-radius: 999px; - color: var(--code-text); - cursor: pointer; - font: inherit; - font-size: 0.82rem; - padding: 0.28rem 0.7rem; - position: absolute; - right: 14px; - top: 12px; -} - -.copy-button:hover { - background: rgba(148, 163, 184, 0.18); - border-color: rgba(148, 163, 184, 0.42); -} - -blockquote { - border-left: 4px solid #b8c6d6; - color: var(--muted); - margin: 1.5rem 0; - padding-left: 1rem; -} - -table { - border-collapse: collapse; - width: 100%; -} - -th, td { - border: 1px solid var(--border); - padding: 0.7rem 0.8rem; - text-align: left; -} - -th { - background: #f1f6fb; -} - -hr { - border: 0; - border-top: 1px solid var(--border); - margin: 2rem 0; -} - -@media (max-width: 720px) { - .site-shell { - padding: 18px 16px 56px; - } - - .site-header { - padding: 14px 16px; - } - - .site-main { - border-radius: 22px; - padding: 28px 20px 38px; - } - - h1 { - font-size: 2.15rem; - } -} -""" - (assets_dir / "site.css").write_text(css, encoding="utf-8") - - -def copy_markdown_tree( - root: Path, output_dir: Path, docs_files: Iterable[Path], examples_dir: Path -) -> None: - """Copy repository markdown files into the generated site tree.""" - write_markdown_page(root / "docs" / "index.md", output_dir / "index.md", "Docs") - write_markdown_page( - examples_dir / "README.md", output_dir / "examples" / "index.md", "Examples" - ) - - for src in docs_files: - if src.name == "index.md": - dst = output_dir / "docs" / "index.md" - write_markdown_page(src, dst, "Docs") - continue - dst = output_dir / "docs" / src.name - fallback = src.stem.replace("-", " ").title() - write_markdown_page(src, dst, fallback) - - for src in sorted(examples_dir.rglob("README.md")): - if src == examples_dir / "README.md": - continue - relative_parent = src.relative_to(examples_dir).parent - dst = output_dir / "examples" / relative_parent / "index.md" - fallback = relative_parent.name.replace("-", " ").title() - write_markdown_page(src, dst, fallback) - - -def build_site(output_dir: Path) -> None: - """Build the markdown source tree used by GitHub Pages.""" - root = Path(__file__).resolve().parents[1] - docs_dir = root / "docs" - examples_dir = root / "examples" - - if output_dir.exists(): - shutil.rmtree(output_dir) - output_dir.mkdir(parents=True, exist_ok=True) - - docs_files = sorted(docs_dir.glob("*.md")) - copy_markdown_tree(root, output_dir, docs_files, examples_dir) - write_site_config(output_dir) - write_layout(output_dir) - write_stylesheet(output_dir) - - -def parse_args() -> argparse.Namespace: - """Parse command-line arguments.""" - parser = argparse.ArgumentParser() - parser.add_argument("--output", default="_site_src", help="Output directory") - return parser.parse_args() - - -def main() -> None: - """Build the GitHub Pages source tree.""" - args = parse_args() - build_site(Path(args.output)) - - -if __name__ == "__main__": - main()