diff --git a/CHANGELOG.md b/CHANGELOG.md index c9eca3c..2d055e1 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -11,11 +11,19 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 - `py.typed` marker for PEP 561 type information. - `.drone.yml` CI with a quality-gate step (black, ruff, mypy, pytest). - MIT `LICENSE`. +- `build --refresh` flag to rebaseline an existing `docs/mkdocs.{kind}.yml` + from the packaged templates. ### Changed - Standardized `pyproject.toml` (canonical packaging, lint tool config, extras). - Pinned the `mcp` extra to `mcp>=1.0.0,<2.0.0` to restore compatibility with the current MCP server API used by doc-forge. +- Per-kind MkDocs configs (`docs/mkdocs.{lib,api,wiki}.yml`) are now generated + only when absent; an existing config is used verbatim (the file wins). + Pass `--refresh` to regenerate it from the templates. +- The wiki template now uses the mongo-ops look: horizontal `navigation.tabs` + header, blue grey/teal palette, Roboto font, and additional material + feature flags. The lib and api templates are unchanged. ### Fixed - Stub fixes for typed API surfaces. \ No newline at end of file diff --git a/docforge/cli/commands.py b/docforge/cli/commands.py index 4b19b7f..0007b2f 100644 --- a/docforge/cli/commands.py +++ b/docforge/cli/commands.py @@ -44,6 +44,11 @@ def cli() -> None: @click.option( "--wiki", is_flag=True, help="Build a hand-written wiki as its own MkDocs site" ) +@click.option( + "--refresh", + is_flag=True, + help="Regenerate existing docs/mkdocs.{kind}.yml configs from templates", +) @click.option( "--module-is-source", is_flag=True, @@ -90,6 +95,7 @@ def build( mkdocs: bool, api: bool, wiki: bool, + refresh: bool, module_is_source: bool, module: str | None, openapi_spec: Path | None, @@ -123,6 +129,9 @@ def build( provided. - `--mkdocs`, `--api`, and `--wiki` emit independent MkDocs builds, while `--mcp` emits a machine-readable bundle. + - Configuration files are generated only when absent; an existing + `docs/mkdocs.{kind}.yml` is used as-is. Pass `--refresh` to + rebaseline it from the templates. Args: mcp (bool): @@ -137,6 +146,10 @@ def build( wiki (bool): Build a hand-written wiki directory as its own MkDocs site. + refresh (bool): + Regenerate ``docs/mkdocs.{kind}.yml`` from templates even when + it already exists. By default, existing configs are used as-is. + module_is_source (bool): Treat the specified module directory as the project root. @@ -221,6 +234,14 @@ def build( config_paths: list[Path] = [] for kind in kinds: + out = docs_dir / f"mkdocs.{kind}.yml" + if out.exists() and not refresh: + click.echo( + f"Using existing MkDocs config {out} (run --refresh to rebaseline)..." + ) + config_paths.append(out) + continue + kind_root = wiki_dir if kind == "wiki" else docs_dir / kind site_name_kind = site_name or module or Path.cwd().name @@ -238,7 +259,6 @@ def build( else: nav_block = [{"API Reference": "index.md"}] - out = docs_dir / f"mkdocs.{kind}.yml" click.echo(f"Generating MkDocs config {out}...") mkdocs_utils.generate_site_config( kind, diff --git a/docforge/templates/mkdocs.wiki.yml b/docforge/templates/mkdocs.wiki.yml index a035a5b..210e0f8 100644 --- a/docforge/templates/mkdocs.wiki.yml +++ b/docforge/templates/mkdocs.wiki.yml @@ -1,2 +1,25 @@ +theme: + features: + # Horizontal tab navigation (mongo-ops style) + - navigation.tabs + # Navigation UX + - toc.integrate + - header.autohide + - announce.dismiss + - footer.social + # Content & Code + - content.code.select + - content.code.line_numbers + - content.tooltips + palette: + scheme: slate + primary: blue grey + accent: teal + font: + text: Roboto + code: JetBrains Mono + icon: + logo: material/database + repo: fontawesome/brands/github plugins: - search \ No newline at end of file diff --git a/docs/lib/docforge/cli/api_utils.md b/docs/lib/docforge/cli/api_utils.md new file mode 100644 index 0000000..62040b8 --- /dev/null +++ b/docs/lib/docforge/cli/api_utils.md @@ -0,0 +1,3 @@ +# Api Utils + +::: docforge.cli.api_utils diff --git a/docs/lib/docforge/cli/commands.md b/docs/lib/docforge/cli/commands.md new file mode 100644 index 0000000..75f0d75 --- /dev/null +++ b/docs/lib/docforge/cli/commands.md @@ -0,0 +1,3 @@ +# Commands + +::: docforge.cli.commands diff --git a/docs/lib/docforge/cli/index.md b/docs/lib/docforge/cli/index.md new file mode 100644 index 0000000..314c649 --- /dev/null +++ b/docs/lib/docforge/cli/index.md @@ -0,0 +1,8 @@ +# Cli + +::: docforge.cli +- [Api Utils](api_utils.md) +- [Commands](commands.md) +- [Main](main.md) +- [Mcp Utils](mcp_utils.md) +- [Mkdocs Utils](mkdocs_utils.md) diff --git a/docs/lib/docforge/cli/main.md b/docs/lib/docforge/cli/main.md new file mode 100644 index 0000000..b22fa07 --- /dev/null +++ b/docs/lib/docforge/cli/main.md @@ -0,0 +1,3 @@ +# Main + +::: docforge.cli.main diff --git a/docs/lib/docforge/cli/mcp_utils.md b/docs/lib/docforge/cli/mcp_utils.md new file mode 100644 index 0000000..7987ffd --- /dev/null +++ b/docs/lib/docforge/cli/mcp_utils.md @@ -0,0 +1,3 @@ +# Mcp Utils + +::: docforge.cli.mcp_utils diff --git a/docs/lib/docforge/cli/mkdocs_utils.md b/docs/lib/docforge/cli/mkdocs_utils.md new file mode 100644 index 0000000..2f2d3d9 --- /dev/null +++ b/docs/lib/docforge/cli/mkdocs_utils.md @@ -0,0 +1,3 @@ +# Mkdocs Utils + +::: docforge.cli.mkdocs_utils diff --git a/docs/lib/docforge/index.md b/docs/lib/docforge/index.md new file mode 100644 index 0000000..d6800dc --- /dev/null +++ b/docs/lib/docforge/index.md @@ -0,0 +1,9 @@ +# Docforge + +::: docforge +- [Cli](cli/) +- [Loaders](loaders/) +- [Models](models/) +- [Nav](nav/) +- [Renderers](renderers/) +- [Servers](servers/) diff --git a/docs/lib/docforge/loaders/griffe_loader.md b/docs/lib/docforge/loaders/griffe_loader.md new file mode 100644 index 0000000..21d8766 --- /dev/null +++ b/docs/lib/docforge/loaders/griffe_loader.md @@ -0,0 +1,3 @@ +# Griffe Loader + +::: docforge.loaders.griffe_loader diff --git a/docs/lib/docforge/loaders/index.md b/docs/lib/docforge/loaders/index.md new file mode 100644 index 0000000..2cbb874 --- /dev/null +++ b/docs/lib/docforge/loaders/index.md @@ -0,0 +1,4 @@ +# Loaders + +::: docforge.loaders +- [Griffe Loader](griffe_loader.md) diff --git a/docs/lib/docforge/models/index.md b/docs/lib/docforge/models/index.md new file mode 100644 index 0000000..f16cc48 --- /dev/null +++ b/docs/lib/docforge/models/index.md @@ -0,0 +1,6 @@ +# Models + +::: docforge.models +- [Module](module.md) +- [Object](object.md) +- [Project](project.md) diff --git a/docs/lib/docforge/models/module.md b/docs/lib/docforge/models/module.md new file mode 100644 index 0000000..945994c --- /dev/null +++ b/docs/lib/docforge/models/module.md @@ -0,0 +1,3 @@ +# Module + +::: docforge.models.module diff --git a/docs/lib/docforge/models/object.md b/docs/lib/docforge/models/object.md new file mode 100644 index 0000000..8e9a603 --- /dev/null +++ b/docs/lib/docforge/models/object.md @@ -0,0 +1,3 @@ +# Object + +::: docforge.models.object diff --git a/docs/lib/docforge/models/project.md b/docs/lib/docforge/models/project.md new file mode 100644 index 0000000..64db98e --- /dev/null +++ b/docs/lib/docforge/models/project.md @@ -0,0 +1,3 @@ +# Project + +::: docforge.models.project diff --git a/docs/lib/docforge/nav/index.md b/docs/lib/docforge/nav/index.md new file mode 100644 index 0000000..1740108 --- /dev/null +++ b/docs/lib/docforge/nav/index.md @@ -0,0 +1,7 @@ +# Nav + +::: docforge.nav +- [Mkdocs](mkdocs.md) +- [Resolver](resolver.md) +- [Spec](spec.md) +- [Wiki](wiki.md) diff --git a/docs/lib/docforge/nav/mkdocs.md b/docs/lib/docforge/nav/mkdocs.md new file mode 100644 index 0000000..b783fec --- /dev/null +++ b/docs/lib/docforge/nav/mkdocs.md @@ -0,0 +1,3 @@ +# Mkdocs + +::: docforge.nav.mkdocs diff --git a/docs/lib/docforge/nav/resolver.md b/docs/lib/docforge/nav/resolver.md new file mode 100644 index 0000000..4f30dc2 --- /dev/null +++ b/docs/lib/docforge/nav/resolver.md @@ -0,0 +1,3 @@ +# Resolver + +::: docforge.nav.resolver diff --git a/docs/lib/docforge/nav/spec.md b/docs/lib/docforge/nav/spec.md new file mode 100644 index 0000000..924864b --- /dev/null +++ b/docs/lib/docforge/nav/spec.md @@ -0,0 +1,3 @@ +# Spec + +::: docforge.nav.spec diff --git a/docs/lib/docforge/nav/wiki.md b/docs/lib/docforge/nav/wiki.md new file mode 100644 index 0000000..c60c750 --- /dev/null +++ b/docs/lib/docforge/nav/wiki.md @@ -0,0 +1,3 @@ +# Wiki + +::: docforge.nav.wiki diff --git a/docs/lib/docforge/renderers/base.md b/docs/lib/docforge/renderers/base.md new file mode 100644 index 0000000..196d183 --- /dev/null +++ b/docs/lib/docforge/renderers/base.md @@ -0,0 +1,3 @@ +# Base + +::: docforge.renderers.base diff --git a/docs/lib/docforge/renderers/index.md b/docs/lib/docforge/renderers/index.md new file mode 100644 index 0000000..28d61c4 --- /dev/null +++ b/docs/lib/docforge/renderers/index.md @@ -0,0 +1,6 @@ +# Renderers + +::: docforge.renderers +- [Base](base.md) +- [Mcp Renderer](mcp_renderer.md) +- [Mkdocs Renderer](mkdocs_renderer.md) diff --git a/docs/lib/docforge/renderers/mcp_renderer.md b/docs/lib/docforge/renderers/mcp_renderer.md new file mode 100644 index 0000000..02f97d1 --- /dev/null +++ b/docs/lib/docforge/renderers/mcp_renderer.md @@ -0,0 +1,3 @@ +# Mcp Renderer + +::: docforge.renderers.mcp_renderer diff --git a/docs/lib/docforge/renderers/mkdocs_renderer.md b/docs/lib/docforge/renderers/mkdocs_renderer.md new file mode 100644 index 0000000..f60486e --- /dev/null +++ b/docs/lib/docforge/renderers/mkdocs_renderer.md @@ -0,0 +1,3 @@ +# Mkdocs Renderer + +::: docforge.renderers.mkdocs_renderer diff --git a/docs/lib/docforge/servers/index.md b/docs/lib/docforge/servers/index.md new file mode 100644 index 0000000..22e4b69 --- /dev/null +++ b/docs/lib/docforge/servers/index.md @@ -0,0 +1,4 @@ +# Servers + +::: docforge.servers +- [Mcp Server](mcp_server.md) diff --git a/docs/lib/docforge/servers/mcp_server.md b/docs/lib/docforge/servers/mcp_server.md new file mode 100644 index 0000000..0f20e6b --- /dev/null +++ b/docs/lib/docforge/servers/mcp_server.md @@ -0,0 +1,3 @@ +# Mcp Server + +::: docforge.servers.mcp_server diff --git a/docs/lib/index.md b/docs/lib/index.md index 96e8919..fd38149 100644 --- a/docs/lib/index.md +++ b/docs/lib/index.md @@ -1,3 +1,4 @@ # docforge ::: docforge +- [Docforge](docforge/) diff --git a/docs/mkdocs.wiki.yml b/docs/mkdocs.wiki.yml index f490763..fee5b50 100644 --- a/docs/mkdocs.wiki.yml +++ b/docs/mkdocs.wiki.yml @@ -1,11 +1,11 @@ theme: name: material palette: - - scheme: slate - primary: deep purple - accent: cyan + scheme: slate + primary: blue grey + accent: teal font: - text: Inter + text: Roboto code: JetBrains Mono features: - navigation.sections @@ -21,6 +21,14 @@ theme: - search.highlight - search.share - search.suggest + - navigation.tabs + - toc.integrate + - header.autohide + - announce.dismiss + - footer.social + - content.code.select + - content.code.line_numbers + - content.tooltips icon: logo: material/file-document-multiple repo: fontawesome/brands/github @@ -48,7 +56,7 @@ extra_css: - https://unpkg.com/dracula-prism/dist/css/dracula-prism.css plugins: - search -site_name: docforge +site_name: doc-forge docs_dir: wiki site_dir: ../site/wiki nav: @@ -62,3 +70,4 @@ nav: - Quality Gates: 05_development/02_quality_gates.md - Gsdfc Guide: 05_development/03_gsdfc_guide.md - Mcp Guide: 05_development/04_mcp_guide.md + - Mkdocs Configs: 05_development/05_mkdocs_configs.md diff --git a/docs/wiki/05_development/05_mkdocs_configs.md b/docs/wiki/05_development/05_mkdocs_configs.md new file mode 100644 index 0000000..1f5fbec --- /dev/null +++ b/docs/wiki/05_development/05_mkdocs_configs.md @@ -0,0 +1,48 @@ +# MkDocs Configuration Model + +Each documentation kind (lib, api, wiki) is built by its own MkDocs site. A +repo that documents kind `{kind}` carries a config at `docs/mkdocs.{kind}.yml` +that sets `docs_dir` to the kind's source directory and `site_dir` to +`../site/{kind}`. + +## File wins + +`docs/mkdocs.{kind}.yml` is a repo-owned file, not a build byproduct: + +- **Absent** — `doc-forge build --{kind}` materializes a copy from the + packaged templates (see below) and then builds `site/{kind}`. +- **Present** — the build uses the file verbatim. Edits to the theme, palette, + plugins, markdown extensions, nav, or `site_dir` are respected as-is. +- **`--refresh`** — force-regenerates the file from the templates, + replacing any local changes. + +Because a present file is never rewritten, rerunning a build is a no-op for +versioned configs: the working tree stays clean. + +## Templates + +The built-in defaults live in `docforge/templates/`: + +| File | Role | +|-----------------------|-----------------------------------------------| +| `mkdocs.common.yml` | Shared theme + markdown extensions | +| `mkdocs.lib.yml` | `mkdocstrings` handler for library reference | +| `mkdocs.api.yml` | Swagger/OpenAPI plugins and API-side features | +| `mkdocs.wiki.yml` | Wiki look: horizontal `navigation.tabs` header, blue grey/teal palette, Roboto | + +A per-kind config is generated by deep-merging the common template with the +kind fragment, then filling in generation-time values (`site_name`, +`docs_dir`, `site_dir`, `nav`, API `site_description`/`site_author`, and the +theme `icon` from `docforge.nav.yml`). Pass `--template ` to replace the +built-in templates entirely. + +## Supported layouts + +- **Per-kind config** — the standard layout; each kind builds to + `site/{kind}` and is served under `//{kind}/`. +- **Legacy root config** — a repo with a single kind may instead author root + `mkdocs.yml` (for example mongo-ops, whose wiki uses `docs_dir: docs/wiki`). + doc-forge never reads or writes root `mkdocs.yml`; the docs service maps the + kind to the root `site/` dir in this case. + +Commit `docs/mkdocs.{kind}.yml` so served docs are reproducible from source. \ No newline at end of file diff --git a/tests/cli/test_build_mkdocs.py b/tests/cli/test_build_mkdocs.py index 368110d..b65489e 100644 --- a/tests/cli/test_build_mkdocs.py +++ b/tests/cli/test_build_mkdocs.py @@ -90,3 +90,73 @@ def test_mkdocs_build_without_site_name_uses_module_as_default_full_flow( # Docs must be generated under the nested docs/lib dir assert (cwd / "docs" / "lib" / "testpkg" / "mod.md").exists() + + +def test_existing_lib_config_used_verbatim_without_refresh( + cli_runner, + mock_mkdocs_build, + mock_mkdocs_load_config, +): + with cli_runner.isolated_filesystem(): + cwd = Path.cwd() + pkg = cwd / "testpkg" + pkg.mkdir() + (pkg / "__init__.py").write_text("") + (pkg / "mod.py").write_text("def f(): ...\n") + + nav_file = cwd / "docforge.nav.yml" + nav_file.write_text( + "home: lib/testpkg/index.md\ngroups: {}\n", + encoding="utf-8", + ) + + config = cwd / "docs" / "mkdocs.lib.yml" + config.parent.mkdir(parents=True) + sentinel = "site_name: Custom Lib\nnav: []\n" + config.write_text(sentinel, encoding="utf-8") + + result = cli_runner.invoke( + cli, + ["build", "--mkdocs", "--module", "testpkg"], + ) + + assert result.exit_code == 0, result.output + assert mock_mkdocs_build() is True + assert config.read_text(encoding="utf-8") == sentinel + assert "Using existing MkDocs config" in result.output + + +def test_refresh_rebases_existing_lib_config( + cli_runner, + mock_mkdocs_build, + mock_mkdocs_load_config, +): + with cli_runner.isolated_filesystem(): + cwd = Path.cwd() + pkg = cwd / "testpkg" + pkg.mkdir() + (pkg / "__init__.py").write_text("") + (pkg / "mod.py").write_text("def f(): ...\n") + + nav_file = cwd / "docforge.nav.yml" + nav_file.write_text( + "home: lib/testpkg/index.md\ngroups: {}\n", + encoding="utf-8", + ) + + config = cwd / "docs" / "mkdocs.lib.yml" + config.parent.mkdir(parents=True) + config.write_text("site_name: Stale\nnav: []\n", encoding="utf-8") + + result = cli_runner.invoke( + cli, + ["build", "--mkdocs", "--module", "testpkg", "--refresh"], + ) + + assert result.exit_code == 0, result.output + assert mock_mkdocs_build() is True + content = config.read_text(encoding="utf-8") + assert "site_name: testpkg" in content + assert "docs_dir: lib" in content + assert "site_dir: ../site/lib" in content + assert "Home: testpkg/index.md" in content diff --git a/tests/cli/test_build_wiki.py b/tests/cli/test_build_wiki.py index 60a8f04..811babf 100644 --- a/tests/cli/test_build_wiki.py +++ b/tests/cli/test_build_wiki.py @@ -111,6 +111,33 @@ def test_wiki_without_module_flag_ok( assert mock_mkdocs_build() is True +def test_wiki_existing_config_used_verbatim_without_refresh( + cli_runner, + mock_mkdocs_build, + mock_mkdocs_load_config, +): + with cli_runner.isolated_filesystem(): + cwd = Path.cwd() + + wiki = cwd / "docs" / "wiki" + wiki.mkdir(parents=True) + (wiki / "index.md").write_text("# Home", encoding="utf-8") + + config = cwd / "docs" / "mkdocs.wiki.yml" + sentinel = "site_name: Custom Wiki\nnav: []\n" + config.write_text(sentinel, encoding="utf-8") + + result = cli_runner.invoke( + cli, + ["build", "--wiki", "--site-name", "Wiki Site"], + ) + + assert result.exit_code == 0, result.output + assert mock_mkdocs_build() is True + assert config.read_text(encoding="utf-8") == sentinel + assert "Using existing MkDocs config" in result.output + + def test_missing_wiki_dir_errors(cli_runner, mock_mkdocs_build): with cli_runner.isolated_filesystem(): cwd = Path.cwd()