mkdocs2confluence 0.11.0__tar.gz → 0.12.0__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- mkdocs2confluence-0.12.0/PKG-INFO +196 -0
- mkdocs2confluence-0.12.0/README.md +155 -0
- {mkdocs2confluence-0.11.0 → mkdocs2confluence-0.12.0}/pyproject.toml +1 -1
- mkdocs2confluence-0.12.0/src/mkdocs2confluence.egg-info/PKG-INFO +196 -0
- {mkdocs2confluence-0.11.0 → mkdocs2confluence-0.12.0}/src/mkdocs2confluence.egg-info/SOURCES.txt +1 -0
- {mkdocs2confluence-0.11.0 → mkdocs2confluence-0.12.0}/src/mkdocs_to_confluence/loader/config.py +25 -0
- {mkdocs2confluence-0.11.0 → mkdocs2confluence-0.12.0}/src/mkdocs_to_confluence/loader/nav.py +8 -1
- {mkdocs2confluence-0.11.0 → mkdocs2confluence-0.12.0}/src/mkdocs_to_confluence/publisher/client.py +75 -36
- mkdocs2confluence-0.12.0/src/mkdocs_to_confluence/publisher/http_retry.py +50 -0
- {mkdocs2confluence-0.11.0 → mkdocs2confluence-0.12.0}/src/mkdocs_to_confluence/transforms/assets.py +4 -3
- {mkdocs2confluence-0.11.0 → mkdocs2confluence-0.12.0}/tests/test_images.py +41 -0
- {mkdocs2confluence-0.11.0 → mkdocs2confluence-0.12.0}/tests/test_loader.py +70 -0
- {mkdocs2confluence-0.11.0 → mkdocs2confluence-0.12.0}/tests/test_publish_client.py +106 -0
- {mkdocs2confluence-0.11.0 → mkdocs2confluence-0.12.0}/tests/test_publish_config.py +71 -0
- mkdocs2confluence-0.11.0/PKG-INFO +0 -534
- mkdocs2confluence-0.11.0/README.md +0 -493
- mkdocs2confluence-0.11.0/src/mkdocs2confluence.egg-info/PKG-INFO +0 -534
- {mkdocs2confluence-0.11.0 → mkdocs2confluence-0.12.0}/LICENSE +0 -0
- {mkdocs2confluence-0.11.0 → mkdocs2confluence-0.12.0}/setup.cfg +0 -0
- {mkdocs2confluence-0.11.0 → mkdocs2confluence-0.12.0}/src/mkdocs2confluence.egg-info/dependency_links.txt +0 -0
- {mkdocs2confluence-0.11.0 → mkdocs2confluence-0.12.0}/src/mkdocs2confluence.egg-info/entry_points.txt +0 -0
- {mkdocs2confluence-0.11.0 → mkdocs2confluence-0.12.0}/src/mkdocs2confluence.egg-info/requires.txt +0 -0
- {mkdocs2confluence-0.11.0 → mkdocs2confluence-0.12.0}/src/mkdocs2confluence.egg-info/top_level.txt +0 -0
- {mkdocs2confluence-0.11.0 → mkdocs2confluence-0.12.0}/src/mkdocs_to_confluence/__init__.py +0 -0
- {mkdocs2confluence-0.11.0 → mkdocs2confluence-0.12.0}/src/mkdocs_to_confluence/cli.py +0 -0
- {mkdocs2confluence-0.11.0 → mkdocs2confluence-0.12.0}/src/mkdocs_to_confluence/emitter/__init__.py +0 -0
- {mkdocs2confluence-0.11.0 → mkdocs2confluence-0.12.0}/src/mkdocs_to_confluence/emitter/xhtml.py +0 -0
- {mkdocs2confluence-0.11.0 → mkdocs2confluence-0.12.0}/src/mkdocs_to_confluence/ir/__init__.py +0 -0
- {mkdocs2confluence-0.11.0 → mkdocs2confluence-0.12.0}/src/mkdocs_to_confluence/ir/document.py +0 -0
- {mkdocs2confluence-0.11.0 → mkdocs2confluence-0.12.0}/src/mkdocs_to_confluence/ir/nodes.py +0 -0
- {mkdocs2confluence-0.11.0 → mkdocs2confluence-0.12.0}/src/mkdocs_to_confluence/ir/treeutil.py +0 -0
- {mkdocs2confluence-0.11.0 → mkdocs2confluence-0.12.0}/src/mkdocs_to_confluence/loader/__init__.py +0 -0
- {mkdocs2confluence-0.11.0 → mkdocs2confluence-0.12.0}/src/mkdocs_to_confluence/loader/extra_css.py +0 -0
- {mkdocs2confluence-0.11.0 → mkdocs2confluence-0.12.0}/src/mkdocs_to_confluence/loader/page.py +0 -0
- {mkdocs2confluence-0.11.0 → mkdocs2confluence-0.12.0}/src/mkdocs_to_confluence/parser/__init__.py +0 -0
- {mkdocs2confluence-0.11.0 → mkdocs2confluence-0.12.0}/src/mkdocs_to_confluence/parser/markdown.py +0 -0
- {mkdocs2confluence-0.11.0 → mkdocs2confluence-0.12.0}/src/mkdocs_to_confluence/pdf/__init__.py +0 -0
- {mkdocs2confluence-0.11.0 → mkdocs2confluence-0.12.0}/src/mkdocs_to_confluence/pdf/generator.py +0 -0
- {mkdocs2confluence-0.11.0 → mkdocs2confluence-0.12.0}/src/mkdocs_to_confluence/pdf/render.py +0 -0
- {mkdocs2confluence-0.11.0 → mkdocs2confluence-0.12.0}/src/mkdocs_to_confluence/preprocess/__init__.py +0 -0
- {mkdocs2confluence-0.11.0 → mkdocs2confluence-0.12.0}/src/mkdocs_to_confluence/preprocess/abbrevs.py +0 -0
- {mkdocs2confluence-0.11.0 → mkdocs2confluence-0.12.0}/src/mkdocs_to_confluence/preprocess/fence.py +0 -0
- {mkdocs2confluence-0.11.0 → mkdocs2confluence-0.12.0}/src/mkdocs_to_confluence/preprocess/frontmatter.py +0 -0
- {mkdocs2confluence-0.11.0 → mkdocs2confluence-0.12.0}/src/mkdocs_to_confluence/preprocess/icons.py +0 -0
- {mkdocs2confluence-0.11.0 → mkdocs2confluence-0.12.0}/src/mkdocs_to_confluence/preprocess/includes.py +0 -0
- {mkdocs2confluence-0.11.0 → mkdocs2confluence-0.12.0}/src/mkdocs_to_confluence/preprocess/linkdefs.py +0 -0
- {mkdocs2confluence-0.11.0 → mkdocs2confluence-0.12.0}/src/mkdocs_to_confluence/preview/__init__.py +0 -0
- {mkdocs2confluence-0.11.0 → mkdocs2confluence-0.12.0}/src/mkdocs_to_confluence/preview/render.py +0 -0
- {mkdocs2confluence-0.11.0 → mkdocs2confluence-0.12.0}/src/mkdocs_to_confluence/preview/server.py +0 -0
- {mkdocs2confluence-0.11.0 → mkdocs2confluence-0.12.0}/src/mkdocs_to_confluence/publisher/__init__.py +0 -0
- {mkdocs2confluence-0.11.0 → mkdocs2confluence-0.12.0}/src/mkdocs_to_confluence/publisher/pipeline.py +0 -0
- {mkdocs2confluence-0.11.0 → mkdocs2confluence-0.12.0}/src/mkdocs_to_confluence/sync/__init__.py +0 -0
- {mkdocs2confluence-0.11.0 → mkdocs2confluence-0.12.0}/src/mkdocs_to_confluence/sync/anchoring.py +0 -0
- {mkdocs2confluence-0.11.0 → mkdocs2confluence-0.12.0}/src/mkdocs_to_confluence/sync/command.py +0 -0
- {mkdocs2confluence-0.11.0 → mkdocs2confluence-0.12.0}/src/mkdocs_to_confluence/sync/comments.py +0 -0
- {mkdocs2confluence-0.11.0 → mkdocs2confluence-0.12.0}/src/mkdocs_to_confluence/sync/github.py +0 -0
- {mkdocs2confluence-0.11.0 → mkdocs2confluence-0.12.0}/src/mkdocs_to_confluence/sync/platform.py +0 -0
- {mkdocs2confluence-0.11.0 → mkdocs2confluence-0.12.0}/src/mkdocs_to_confluence/sync/state.py +0 -0
- {mkdocs2confluence-0.11.0 → mkdocs2confluence-0.12.0}/src/mkdocs_to_confluence/transforms/__init__.py +0 -0
- {mkdocs2confluence-0.11.0 → mkdocs2confluence-0.12.0}/src/mkdocs_to_confluence/transforms/abbrevs.py +0 -0
- {mkdocs2confluence-0.11.0 → mkdocs2confluence-0.12.0}/src/mkdocs_to_confluence/transforms/editlink.py +0 -0
- {mkdocs2confluence-0.11.0 → mkdocs2confluence-0.12.0}/src/mkdocs_to_confluence/transforms/footer.py +0 -0
- {mkdocs2confluence-0.11.0 → mkdocs2confluence-0.12.0}/src/mkdocs_to_confluence/transforms/images.py +0 -0
- {mkdocs2confluence-0.11.0 → mkdocs2confluence-0.12.0}/src/mkdocs_to_confluence/transforms/internallinks.py +0 -0
- {mkdocs2confluence-0.11.0 → mkdocs2confluence-0.12.0}/src/mkdocs_to_confluence/transforms/mermaid.py +0 -0
- {mkdocs2confluence-0.11.0 → mkdocs2confluence-0.12.0}/tests/test_abbrevs.py +0 -0
- {mkdocs2confluence-0.11.0 → mkdocs2confluence-0.12.0}/tests/test_children_macro.py +0 -0
- {mkdocs2confluence-0.11.0 → mkdocs2confluence-0.12.0}/tests/test_cli.py +0 -0
- {mkdocs2confluence-0.11.0 → mkdocs2confluence-0.12.0}/tests/test_editlink.py +0 -0
- {mkdocs2confluence-0.11.0 → mkdocs2confluence-0.12.0}/tests/test_emitter.py +0 -0
- {mkdocs2confluence-0.11.0 → mkdocs2confluence-0.12.0}/tests/test_extra_css.py +0 -0
- {mkdocs2confluence-0.11.0 → mkdocs2confluence-0.12.0}/tests/test_footer.py +0 -0
- {mkdocs2confluence-0.11.0 → mkdocs2confluence-0.12.0}/tests/test_frontmatter.py +0 -0
- {mkdocs2confluence-0.11.0 → mkdocs2confluence-0.12.0}/tests/test_icons.py +0 -0
- {mkdocs2confluence-0.11.0 → mkdocs2confluence-0.12.0}/tests/test_internallinks.py +0 -0
- {mkdocs2confluence-0.11.0 → mkdocs2confluence-0.12.0}/tests/test_ir.py +0 -0
- {mkdocs2confluence-0.11.0 → mkdocs2confluence-0.12.0}/tests/test_linkdefs.py +0 -0
- {mkdocs2confluence-0.11.0 → mkdocs2confluence-0.12.0}/tests/test_mermaid.py +0 -0
- {mkdocs2confluence-0.11.0 → mkdocs2confluence-0.12.0}/tests/test_page_loader.py +0 -0
- {mkdocs2confluence-0.11.0 → mkdocs2confluence-0.12.0}/tests/test_parser.py +0 -0
- {mkdocs2confluence-0.11.0 → mkdocs2confluence-0.12.0}/tests/test_pdf.py +0 -0
- {mkdocs2confluence-0.11.0 → mkdocs2confluence-0.12.0}/tests/test_preprocess.py +0 -0
- {mkdocs2confluence-0.11.0 → mkdocs2confluence-0.12.0}/tests/test_preview.py +0 -0
- {mkdocs2confluence-0.11.0 → mkdocs2confluence-0.12.0}/tests/test_publish_pipeline.py +0 -0
- {mkdocs2confluence-0.11.0 → mkdocs2confluence-0.12.0}/tests/test_server.py +0 -0
- {mkdocs2confluence-0.11.0 → mkdocs2confluence-0.12.0}/tests/test_sync_anchoring.py +0 -0
- {mkdocs2confluence-0.11.0 → mkdocs2confluence-0.12.0}/tests/test_sync_command.py +0 -0
- {mkdocs2confluence-0.11.0 → mkdocs2confluence-0.12.0}/tests/test_sync_comments.py +0 -0
- {mkdocs2confluence-0.11.0 → mkdocs2confluence-0.12.0}/tests/test_sync_github.py +0 -0
- {mkdocs2confluence-0.11.0 → mkdocs2confluence-0.12.0}/tests/test_sync_state.py +0 -0
- {mkdocs2confluence-0.11.0 → mkdocs2confluence-0.12.0}/tests/test_treeutil.py +0 -0
|
@@ -0,0 +1,196 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: mkdocs2confluence
|
|
3
|
+
Version: 0.12.0
|
|
4
|
+
Summary: Publish MkDocs Material pages to Confluence Cloud — admonitions, Mermaid diagrams, tabs, page properties and more
|
|
5
|
+
Author: Anders Hybertz
|
|
6
|
+
License: GPL-3.0-or-later
|
|
7
|
+
Project-URL: Homepage, https://github.com/jeckyl2010/mkdocs2confluence
|
|
8
|
+
Project-URL: Repository, https://github.com/jeckyl2010/mkdocs2confluence
|
|
9
|
+
Project-URL: Issues, https://github.com/jeckyl2010/mkdocs2confluence/issues
|
|
10
|
+
Project-URL: Changelog, https://github.com/jeckyl2010/mkdocs2confluence/releases
|
|
11
|
+
Keywords: mkdocs,confluence,atlassian,documentation,publishing,material-for-mkdocs,markdown,storage-format
|
|
12
|
+
Classifier: Development Status :: 4 - Beta
|
|
13
|
+
Classifier: Environment :: Console
|
|
14
|
+
Classifier: Intended Audience :: Developers
|
|
15
|
+
Classifier: License :: OSI Approved :: GNU General Public License v3 or later (GPLv3+)
|
|
16
|
+
Classifier: Programming Language :: Python :: 3
|
|
17
|
+
Classifier: Programming Language :: Python :: 3.12
|
|
18
|
+
Classifier: Programming Language :: Python :: 3.13
|
|
19
|
+
Classifier: Topic :: Documentation
|
|
20
|
+
Classifier: Topic :: Software Development :: Documentation
|
|
21
|
+
Classifier: Topic :: Text Processing :: Markup
|
|
22
|
+
Classifier: Topic :: Utilities
|
|
23
|
+
Requires-Python: >=3.12
|
|
24
|
+
Description-Content-Type: text/markdown
|
|
25
|
+
License-File: LICENSE
|
|
26
|
+
Requires-Dist: PyYAML>=6.0.3
|
|
27
|
+
Requires-Dist: httpx>=0.27
|
|
28
|
+
Requires-Dist: tinycss2>=1.5.1
|
|
29
|
+
Provides-Extra: pdf
|
|
30
|
+
Requires-Dist: weasyprint>=60.0; extra == "pdf"
|
|
31
|
+
Provides-Extra: dev
|
|
32
|
+
Requires-Dist: pytest; extra == "dev"
|
|
33
|
+
Requires-Dist: pytest-cov; extra == "dev"
|
|
34
|
+
Requires-Dist: ruff; extra == "dev"
|
|
35
|
+
Requires-Dist: mypy; extra == "dev"
|
|
36
|
+
Requires-Dist: types-PyYAML; extra == "dev"
|
|
37
|
+
Requires-Dist: bandit; extra == "dev"
|
|
38
|
+
Requires-Dist: build; extra == "dev"
|
|
39
|
+
Requires-Dist: pre-commit; extra == "dev"
|
|
40
|
+
Dynamic: license-file
|
|
41
|
+
|
|
42
|
+
# mk2conf — MkDocs / Zensical to Confluence
|
|
43
|
+
|
|
44
|
+
[](https://www.gnu.org/licenses/gpl-3.0)
|
|
45
|
+
[](https://www.python.org/downloads/)
|
|
46
|
+
[](https://pypi.org/project/mkdocs2confluence/)
|
|
47
|
+
[](https://pypi.org/project/mkdocs2confluence/)
|
|
48
|
+
[](https://github.com/jeckyl2010/mkdocs2confluence/releases/latest)
|
|
49
|
+
[](https://github.com/jeckyl2010/mkdocs2confluence/actions/workflows/ci.yml)
|
|
50
|
+
[](https://github.com/jeckyl2010/mkdocs2confluence/actions/workflows/release.yml)
|
|
51
|
+
[](https://codecov.io/gh/jeckyl2010/mkdocs2confluence)
|
|
52
|
+
[](https://github.com/astral-sh/ruff)
|
|
53
|
+
[](https://mypy-lang.org/)
|
|
54
|
+
[](https://github.com/PyCQA/bandit)
|
|
55
|
+
[](https://slsa.dev)
|
|
56
|
+
[](https://securityscorecards.dev/viewer/?uri=github.com/jeckyl2010/mkdocs2confluence)
|
|
57
|
+
|
|
58
|
+
A Python CLI tool that compiles MkDocs-flavoured Markdown into **native Confluence storage XHTML** and publishes it directly to Confluence Cloud. It is a **compiler/transpiler**, not an HTML converter — every construct maps to its native Confluence equivalent, so pages look and behave like hand-authored Confluence content.
|
|
59
|
+
|
|
60
|
+
It also bridges the gap between Confluence reviewers and developers: the `sync-comments` command turns open Confluence page comments into GitHub pull request review threads, and auto-resolves them in Confluence when the PR is merged.
|
|
61
|
+
|
|
62
|
+
> **Zensical compatible** — [Zensical](https://zensical.org/) is the modern successor to MkDocs + Material for MkDocs. Since it uses the same `mkdocs.yml` format and Python Markdown extensions, your Zensical project works with mk2conf today with no changes required.
|
|
63
|
+
|
|
64
|
+
---
|
|
65
|
+
|
|
66
|
+
## Installation
|
|
67
|
+
|
|
68
|
+
Requires Python 3.12+. The PyPI package is `mkdocs2confluence`; the CLI command is `mk2conf`.
|
|
69
|
+
|
|
70
|
+
```bash
|
|
71
|
+
pip install mkdocs2confluence
|
|
72
|
+
# or, for an isolated install:
|
|
73
|
+
pipx install mkdocs2confluence
|
|
74
|
+
```
|
|
75
|
+
|
|
76
|
+
**From source** (see [Setup.md](Setup.md)):
|
|
77
|
+
|
|
78
|
+
```bash
|
|
79
|
+
git clone https://github.com/jeckyl2010/mkdocs2confluence.git
|
|
80
|
+
cd mkdocs2confluence && uv sync
|
|
81
|
+
```
|
|
82
|
+
|
|
83
|
+
---
|
|
84
|
+
|
|
85
|
+
## GitHub Actions
|
|
86
|
+
|
|
87
|
+
Publish docs automatically on every push — no local install needed:
|
|
88
|
+
|
|
89
|
+
```yaml
|
|
90
|
+
- name: Publish docs to Confluence
|
|
91
|
+
uses: jeckyl2010/mkdocs2confluence@v1
|
|
92
|
+
with:
|
|
93
|
+
token: ${{ secrets.CONFLUENCE_API_TOKEN }}
|
|
94
|
+
```
|
|
95
|
+
|
|
96
|
+
**Full workflow** — triggers on changes to `docs/` or `mkdocs.yml`:
|
|
97
|
+
|
|
98
|
+
```yaml
|
|
99
|
+
name: Publish docs
|
|
100
|
+
|
|
101
|
+
on:
|
|
102
|
+
push:
|
|
103
|
+
branches: [main]
|
|
104
|
+
paths: ['docs/**', 'mkdocs.yml']
|
|
105
|
+
|
|
106
|
+
jobs:
|
|
107
|
+
publish:
|
|
108
|
+
runs-on: ubuntu-latest
|
|
109
|
+
steps:
|
|
110
|
+
- uses: actions/checkout@v4
|
|
111
|
+
- uses: jeckyl2010/mkdocs2confluence@v1
|
|
112
|
+
with:
|
|
113
|
+
token: ${{ secrets.CONFLUENCE_API_TOKEN }}
|
|
114
|
+
prune: 'true'
|
|
115
|
+
```
|
|
116
|
+
|
|
117
|
+
Available inputs: `token` (required), `config`, `version`, `dry-run`, `section`, `page`, `prune`, `quiet`. See [docs/commands.md](docs/commands.md) for details.
|
|
118
|
+
|
|
119
|
+
---
|
|
120
|
+
|
|
121
|
+
## Quick start
|
|
122
|
+
|
|
123
|
+
```bash
|
|
124
|
+
# Preview a page locally (no Confluence API calls)
|
|
125
|
+
mk2conf preview --page index.md --watch
|
|
126
|
+
|
|
127
|
+
# Dry-run: see what would be published
|
|
128
|
+
mk2conf publish --dry-run
|
|
129
|
+
|
|
130
|
+
# Publish all nav pages
|
|
131
|
+
mk2conf publish
|
|
132
|
+
|
|
133
|
+
# Export a section to PDF
|
|
134
|
+
mk2conf pdf --section Guide --out guide.pdf
|
|
135
|
+
|
|
136
|
+
# Sync Confluence comments to GitHub PR review threads
|
|
137
|
+
mk2conf sync-comments
|
|
138
|
+
```
|
|
139
|
+
|
|
140
|
+
---
|
|
141
|
+
|
|
142
|
+
## Configuration
|
|
143
|
+
|
|
144
|
+
Add a `confluence:` block to your `mkdocs.yml`:
|
|
145
|
+
|
|
146
|
+
```yaml
|
|
147
|
+
confluence:
|
|
148
|
+
base_url: https://yourorg.atlassian.net
|
|
149
|
+
space_key: TECH
|
|
150
|
+
email: user@example.com
|
|
151
|
+
token: !ENV CONFLUENCE_API_TOKEN # never hardcode the token
|
|
152
|
+
parent_page_id: "123456" # optional root page
|
|
153
|
+
mermaid_render: kroki # "kroki" (default) | "kroki:https://your-kroki" | "none"
|
|
154
|
+
full_width: true # default: true
|
|
155
|
+
```
|
|
156
|
+
|
|
157
|
+
The `confluence:` block is also accepted under `extra:` for MkDocs strict-mode compatibility. The API token is read from `token:` in `mkdocs.yml`, then `CONFLUENCE_API_TOKEN`, then `MK2CONF_TOKEN`.
|
|
158
|
+
|
|
159
|
+
**Your first publish:**
|
|
160
|
+
|
|
161
|
+
```bash
|
|
162
|
+
export CONFLUENCE_API_TOKEN=your_api_token_here
|
|
163
|
+
mk2conf preview --page docs/index.md --watch # verify output locally
|
|
164
|
+
mk2conf publish --dry-run # check the plan
|
|
165
|
+
mk2conf publish # go live
|
|
166
|
+
```
|
|
167
|
+
|
|
168
|
+
---
|
|
169
|
+
|
|
170
|
+
## Documentation
|
|
171
|
+
|
|
172
|
+
| | |
|
|
173
|
+
|---|---|
|
|
174
|
+
| [docs/commands.md](docs/commands.md) | Full flag reference for all four commands |
|
|
175
|
+
| [docs/features.md](docs/features.md) | Supported Markdown / Material features and known limitations |
|
|
176
|
+
| [Setup.md](Setup.md) | Development environment setup |
|
|
177
|
+
|
|
178
|
+
---
|
|
179
|
+
|
|
180
|
+
## Architecture
|
|
181
|
+
|
|
182
|
+

|
|
183
|
+
|
|
184
|
+
Pipeline stages: **loader → preprocess → IR → transforms → emitter → publisher**. The plan phase makes all API read calls; the execute phase makes all write calls in nav order so parent pages always exist before their children.
|
|
185
|
+
|
|
186
|
+
---
|
|
187
|
+
|
|
188
|
+
## Development
|
|
189
|
+
|
|
190
|
+
```bash
|
|
191
|
+
uv run pytest -q
|
|
192
|
+
uv run ruff check src tests
|
|
193
|
+
uv run mypy src
|
|
194
|
+
uv run vulture src --min-confidence 80
|
|
195
|
+
```
|
|
196
|
+
|
|
@@ -0,0 +1,155 @@
|
|
|
1
|
+
# mk2conf — MkDocs / Zensical to Confluence
|
|
2
|
+
|
|
3
|
+
[](https://www.gnu.org/licenses/gpl-3.0)
|
|
4
|
+
[](https://www.python.org/downloads/)
|
|
5
|
+
[](https://pypi.org/project/mkdocs2confluence/)
|
|
6
|
+
[](https://pypi.org/project/mkdocs2confluence/)
|
|
7
|
+
[](https://github.com/jeckyl2010/mkdocs2confluence/releases/latest)
|
|
8
|
+
[](https://github.com/jeckyl2010/mkdocs2confluence/actions/workflows/ci.yml)
|
|
9
|
+
[](https://github.com/jeckyl2010/mkdocs2confluence/actions/workflows/release.yml)
|
|
10
|
+
[](https://codecov.io/gh/jeckyl2010/mkdocs2confluence)
|
|
11
|
+
[](https://github.com/astral-sh/ruff)
|
|
12
|
+
[](https://mypy-lang.org/)
|
|
13
|
+
[](https://github.com/PyCQA/bandit)
|
|
14
|
+
[](https://slsa.dev)
|
|
15
|
+
[](https://securityscorecards.dev/viewer/?uri=github.com/jeckyl2010/mkdocs2confluence)
|
|
16
|
+
|
|
17
|
+
A Python CLI tool that compiles MkDocs-flavoured Markdown into **native Confluence storage XHTML** and publishes it directly to Confluence Cloud. It is a **compiler/transpiler**, not an HTML converter — every construct maps to its native Confluence equivalent, so pages look and behave like hand-authored Confluence content.
|
|
18
|
+
|
|
19
|
+
It also bridges the gap between Confluence reviewers and developers: the `sync-comments` command turns open Confluence page comments into GitHub pull request review threads, and auto-resolves them in Confluence when the PR is merged.
|
|
20
|
+
|
|
21
|
+
> **Zensical compatible** — [Zensical](https://zensical.org/) is the modern successor to MkDocs + Material for MkDocs. Since it uses the same `mkdocs.yml` format and Python Markdown extensions, your Zensical project works with mk2conf today with no changes required.
|
|
22
|
+
|
|
23
|
+
---
|
|
24
|
+
|
|
25
|
+
## Installation
|
|
26
|
+
|
|
27
|
+
Requires Python 3.12+. The PyPI package is `mkdocs2confluence`; the CLI command is `mk2conf`.
|
|
28
|
+
|
|
29
|
+
```bash
|
|
30
|
+
pip install mkdocs2confluence
|
|
31
|
+
# or, for an isolated install:
|
|
32
|
+
pipx install mkdocs2confluence
|
|
33
|
+
```
|
|
34
|
+
|
|
35
|
+
**From source** (see [Setup.md](Setup.md)):
|
|
36
|
+
|
|
37
|
+
```bash
|
|
38
|
+
git clone https://github.com/jeckyl2010/mkdocs2confluence.git
|
|
39
|
+
cd mkdocs2confluence && uv sync
|
|
40
|
+
```
|
|
41
|
+
|
|
42
|
+
---
|
|
43
|
+
|
|
44
|
+
## GitHub Actions
|
|
45
|
+
|
|
46
|
+
Publish docs automatically on every push — no local install needed:
|
|
47
|
+
|
|
48
|
+
```yaml
|
|
49
|
+
- name: Publish docs to Confluence
|
|
50
|
+
uses: jeckyl2010/mkdocs2confluence@v1
|
|
51
|
+
with:
|
|
52
|
+
token: ${{ secrets.CONFLUENCE_API_TOKEN }}
|
|
53
|
+
```
|
|
54
|
+
|
|
55
|
+
**Full workflow** — triggers on changes to `docs/` or `mkdocs.yml`:
|
|
56
|
+
|
|
57
|
+
```yaml
|
|
58
|
+
name: Publish docs
|
|
59
|
+
|
|
60
|
+
on:
|
|
61
|
+
push:
|
|
62
|
+
branches: [main]
|
|
63
|
+
paths: ['docs/**', 'mkdocs.yml']
|
|
64
|
+
|
|
65
|
+
jobs:
|
|
66
|
+
publish:
|
|
67
|
+
runs-on: ubuntu-latest
|
|
68
|
+
steps:
|
|
69
|
+
- uses: actions/checkout@v4
|
|
70
|
+
- uses: jeckyl2010/mkdocs2confluence@v1
|
|
71
|
+
with:
|
|
72
|
+
token: ${{ secrets.CONFLUENCE_API_TOKEN }}
|
|
73
|
+
prune: 'true'
|
|
74
|
+
```
|
|
75
|
+
|
|
76
|
+
Available inputs: `token` (required), `config`, `version`, `dry-run`, `section`, `page`, `prune`, `quiet`. See [docs/commands.md](docs/commands.md) for details.
|
|
77
|
+
|
|
78
|
+
---
|
|
79
|
+
|
|
80
|
+
## Quick start
|
|
81
|
+
|
|
82
|
+
```bash
|
|
83
|
+
# Preview a page locally (no Confluence API calls)
|
|
84
|
+
mk2conf preview --page index.md --watch
|
|
85
|
+
|
|
86
|
+
# Dry-run: see what would be published
|
|
87
|
+
mk2conf publish --dry-run
|
|
88
|
+
|
|
89
|
+
# Publish all nav pages
|
|
90
|
+
mk2conf publish
|
|
91
|
+
|
|
92
|
+
# Export a section to PDF
|
|
93
|
+
mk2conf pdf --section Guide --out guide.pdf
|
|
94
|
+
|
|
95
|
+
# Sync Confluence comments to GitHub PR review threads
|
|
96
|
+
mk2conf sync-comments
|
|
97
|
+
```
|
|
98
|
+
|
|
99
|
+
---
|
|
100
|
+
|
|
101
|
+
## Configuration
|
|
102
|
+
|
|
103
|
+
Add a `confluence:` block to your `mkdocs.yml`:
|
|
104
|
+
|
|
105
|
+
```yaml
|
|
106
|
+
confluence:
|
|
107
|
+
base_url: https://yourorg.atlassian.net
|
|
108
|
+
space_key: TECH
|
|
109
|
+
email: user@example.com
|
|
110
|
+
token: !ENV CONFLUENCE_API_TOKEN # never hardcode the token
|
|
111
|
+
parent_page_id: "123456" # optional root page
|
|
112
|
+
mermaid_render: kroki # "kroki" (default) | "kroki:https://your-kroki" | "none"
|
|
113
|
+
full_width: true # default: true
|
|
114
|
+
```
|
|
115
|
+
|
|
116
|
+
The `confluence:` block is also accepted under `extra:` for MkDocs strict-mode compatibility. The API token is read from `token:` in `mkdocs.yml`, then `CONFLUENCE_API_TOKEN`, then `MK2CONF_TOKEN`.
|
|
117
|
+
|
|
118
|
+
**Your first publish:**
|
|
119
|
+
|
|
120
|
+
```bash
|
|
121
|
+
export CONFLUENCE_API_TOKEN=your_api_token_here
|
|
122
|
+
mk2conf preview --page docs/index.md --watch # verify output locally
|
|
123
|
+
mk2conf publish --dry-run # check the plan
|
|
124
|
+
mk2conf publish # go live
|
|
125
|
+
```
|
|
126
|
+
|
|
127
|
+
---
|
|
128
|
+
|
|
129
|
+
## Documentation
|
|
130
|
+
|
|
131
|
+
| | |
|
|
132
|
+
|---|---|
|
|
133
|
+
| [docs/commands.md](docs/commands.md) | Full flag reference for all four commands |
|
|
134
|
+
| [docs/features.md](docs/features.md) | Supported Markdown / Material features and known limitations |
|
|
135
|
+
| [Setup.md](Setup.md) | Development environment setup |
|
|
136
|
+
|
|
137
|
+
---
|
|
138
|
+
|
|
139
|
+
## Architecture
|
|
140
|
+
|
|
141
|
+

|
|
142
|
+
|
|
143
|
+
Pipeline stages: **loader → preprocess → IR → transforms → emitter → publisher**. The plan phase makes all API read calls; the execute phase makes all write calls in nav order so parent pages always exist before their children.
|
|
144
|
+
|
|
145
|
+
---
|
|
146
|
+
|
|
147
|
+
## Development
|
|
148
|
+
|
|
149
|
+
```bash
|
|
150
|
+
uv run pytest -q
|
|
151
|
+
uv run ruff check src tests
|
|
152
|
+
uv run mypy src
|
|
153
|
+
uv run vulture src --min-confidence 80
|
|
154
|
+
```
|
|
155
|
+
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
[project]
|
|
2
2
|
name = "mkdocs2confluence"
|
|
3
|
-
version = "0.
|
|
3
|
+
version = "0.12.0"
|
|
4
4
|
description = "Publish MkDocs Material pages to Confluence Cloud — admonitions, Mermaid diagrams, tabs, page properties and more"
|
|
5
5
|
readme = "README.md"
|
|
6
6
|
license = { text = "GPL-3.0-or-later" }
|
|
@@ -0,0 +1,196 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: mkdocs2confluence
|
|
3
|
+
Version: 0.12.0
|
|
4
|
+
Summary: Publish MkDocs Material pages to Confluence Cloud — admonitions, Mermaid diagrams, tabs, page properties and more
|
|
5
|
+
Author: Anders Hybertz
|
|
6
|
+
License: GPL-3.0-or-later
|
|
7
|
+
Project-URL: Homepage, https://github.com/jeckyl2010/mkdocs2confluence
|
|
8
|
+
Project-URL: Repository, https://github.com/jeckyl2010/mkdocs2confluence
|
|
9
|
+
Project-URL: Issues, https://github.com/jeckyl2010/mkdocs2confluence/issues
|
|
10
|
+
Project-URL: Changelog, https://github.com/jeckyl2010/mkdocs2confluence/releases
|
|
11
|
+
Keywords: mkdocs,confluence,atlassian,documentation,publishing,material-for-mkdocs,markdown,storage-format
|
|
12
|
+
Classifier: Development Status :: 4 - Beta
|
|
13
|
+
Classifier: Environment :: Console
|
|
14
|
+
Classifier: Intended Audience :: Developers
|
|
15
|
+
Classifier: License :: OSI Approved :: GNU General Public License v3 or later (GPLv3+)
|
|
16
|
+
Classifier: Programming Language :: Python :: 3
|
|
17
|
+
Classifier: Programming Language :: Python :: 3.12
|
|
18
|
+
Classifier: Programming Language :: Python :: 3.13
|
|
19
|
+
Classifier: Topic :: Documentation
|
|
20
|
+
Classifier: Topic :: Software Development :: Documentation
|
|
21
|
+
Classifier: Topic :: Text Processing :: Markup
|
|
22
|
+
Classifier: Topic :: Utilities
|
|
23
|
+
Requires-Python: >=3.12
|
|
24
|
+
Description-Content-Type: text/markdown
|
|
25
|
+
License-File: LICENSE
|
|
26
|
+
Requires-Dist: PyYAML>=6.0.3
|
|
27
|
+
Requires-Dist: httpx>=0.27
|
|
28
|
+
Requires-Dist: tinycss2>=1.5.1
|
|
29
|
+
Provides-Extra: pdf
|
|
30
|
+
Requires-Dist: weasyprint>=60.0; extra == "pdf"
|
|
31
|
+
Provides-Extra: dev
|
|
32
|
+
Requires-Dist: pytest; extra == "dev"
|
|
33
|
+
Requires-Dist: pytest-cov; extra == "dev"
|
|
34
|
+
Requires-Dist: ruff; extra == "dev"
|
|
35
|
+
Requires-Dist: mypy; extra == "dev"
|
|
36
|
+
Requires-Dist: types-PyYAML; extra == "dev"
|
|
37
|
+
Requires-Dist: bandit; extra == "dev"
|
|
38
|
+
Requires-Dist: build; extra == "dev"
|
|
39
|
+
Requires-Dist: pre-commit; extra == "dev"
|
|
40
|
+
Dynamic: license-file
|
|
41
|
+
|
|
42
|
+
# mk2conf — MkDocs / Zensical to Confluence
|
|
43
|
+
|
|
44
|
+
[](https://www.gnu.org/licenses/gpl-3.0)
|
|
45
|
+
[](https://www.python.org/downloads/)
|
|
46
|
+
[](https://pypi.org/project/mkdocs2confluence/)
|
|
47
|
+
[](https://pypi.org/project/mkdocs2confluence/)
|
|
48
|
+
[](https://github.com/jeckyl2010/mkdocs2confluence/releases/latest)
|
|
49
|
+
[](https://github.com/jeckyl2010/mkdocs2confluence/actions/workflows/ci.yml)
|
|
50
|
+
[](https://github.com/jeckyl2010/mkdocs2confluence/actions/workflows/release.yml)
|
|
51
|
+
[](https://codecov.io/gh/jeckyl2010/mkdocs2confluence)
|
|
52
|
+
[](https://github.com/astral-sh/ruff)
|
|
53
|
+
[](https://mypy-lang.org/)
|
|
54
|
+
[](https://github.com/PyCQA/bandit)
|
|
55
|
+
[](https://slsa.dev)
|
|
56
|
+
[](https://securityscorecards.dev/viewer/?uri=github.com/jeckyl2010/mkdocs2confluence)
|
|
57
|
+
|
|
58
|
+
A Python CLI tool that compiles MkDocs-flavoured Markdown into **native Confluence storage XHTML** and publishes it directly to Confluence Cloud. It is a **compiler/transpiler**, not an HTML converter — every construct maps to its native Confluence equivalent, so pages look and behave like hand-authored Confluence content.
|
|
59
|
+
|
|
60
|
+
It also bridges the gap between Confluence reviewers and developers: the `sync-comments` command turns open Confluence page comments into GitHub pull request review threads, and auto-resolves them in Confluence when the PR is merged.
|
|
61
|
+
|
|
62
|
+
> **Zensical compatible** — [Zensical](https://zensical.org/) is the modern successor to MkDocs + Material for MkDocs. Since it uses the same `mkdocs.yml` format and Python Markdown extensions, your Zensical project works with mk2conf today with no changes required.
|
|
63
|
+
|
|
64
|
+
---
|
|
65
|
+
|
|
66
|
+
## Installation
|
|
67
|
+
|
|
68
|
+
Requires Python 3.12+. The PyPI package is `mkdocs2confluence`; the CLI command is `mk2conf`.
|
|
69
|
+
|
|
70
|
+
```bash
|
|
71
|
+
pip install mkdocs2confluence
|
|
72
|
+
# or, for an isolated install:
|
|
73
|
+
pipx install mkdocs2confluence
|
|
74
|
+
```
|
|
75
|
+
|
|
76
|
+
**From source** (see [Setup.md](Setup.md)):
|
|
77
|
+
|
|
78
|
+
```bash
|
|
79
|
+
git clone https://github.com/jeckyl2010/mkdocs2confluence.git
|
|
80
|
+
cd mkdocs2confluence && uv sync
|
|
81
|
+
```
|
|
82
|
+
|
|
83
|
+
---
|
|
84
|
+
|
|
85
|
+
## GitHub Actions
|
|
86
|
+
|
|
87
|
+
Publish docs automatically on every push — no local install needed:
|
|
88
|
+
|
|
89
|
+
```yaml
|
|
90
|
+
- name: Publish docs to Confluence
|
|
91
|
+
uses: jeckyl2010/mkdocs2confluence@v1
|
|
92
|
+
with:
|
|
93
|
+
token: ${{ secrets.CONFLUENCE_API_TOKEN }}
|
|
94
|
+
```
|
|
95
|
+
|
|
96
|
+
**Full workflow** — triggers on changes to `docs/` or `mkdocs.yml`:
|
|
97
|
+
|
|
98
|
+
```yaml
|
|
99
|
+
name: Publish docs
|
|
100
|
+
|
|
101
|
+
on:
|
|
102
|
+
push:
|
|
103
|
+
branches: [main]
|
|
104
|
+
paths: ['docs/**', 'mkdocs.yml']
|
|
105
|
+
|
|
106
|
+
jobs:
|
|
107
|
+
publish:
|
|
108
|
+
runs-on: ubuntu-latest
|
|
109
|
+
steps:
|
|
110
|
+
- uses: actions/checkout@v4
|
|
111
|
+
- uses: jeckyl2010/mkdocs2confluence@v1
|
|
112
|
+
with:
|
|
113
|
+
token: ${{ secrets.CONFLUENCE_API_TOKEN }}
|
|
114
|
+
prune: 'true'
|
|
115
|
+
```
|
|
116
|
+
|
|
117
|
+
Available inputs: `token` (required), `config`, `version`, `dry-run`, `section`, `page`, `prune`, `quiet`. See [docs/commands.md](docs/commands.md) for details.
|
|
118
|
+
|
|
119
|
+
---
|
|
120
|
+
|
|
121
|
+
## Quick start
|
|
122
|
+
|
|
123
|
+
```bash
|
|
124
|
+
# Preview a page locally (no Confluence API calls)
|
|
125
|
+
mk2conf preview --page index.md --watch
|
|
126
|
+
|
|
127
|
+
# Dry-run: see what would be published
|
|
128
|
+
mk2conf publish --dry-run
|
|
129
|
+
|
|
130
|
+
# Publish all nav pages
|
|
131
|
+
mk2conf publish
|
|
132
|
+
|
|
133
|
+
# Export a section to PDF
|
|
134
|
+
mk2conf pdf --section Guide --out guide.pdf
|
|
135
|
+
|
|
136
|
+
# Sync Confluence comments to GitHub PR review threads
|
|
137
|
+
mk2conf sync-comments
|
|
138
|
+
```
|
|
139
|
+
|
|
140
|
+
---
|
|
141
|
+
|
|
142
|
+
## Configuration
|
|
143
|
+
|
|
144
|
+
Add a `confluence:` block to your `mkdocs.yml`:
|
|
145
|
+
|
|
146
|
+
```yaml
|
|
147
|
+
confluence:
|
|
148
|
+
base_url: https://yourorg.atlassian.net
|
|
149
|
+
space_key: TECH
|
|
150
|
+
email: user@example.com
|
|
151
|
+
token: !ENV CONFLUENCE_API_TOKEN # never hardcode the token
|
|
152
|
+
parent_page_id: "123456" # optional root page
|
|
153
|
+
mermaid_render: kroki # "kroki" (default) | "kroki:https://your-kroki" | "none"
|
|
154
|
+
full_width: true # default: true
|
|
155
|
+
```
|
|
156
|
+
|
|
157
|
+
The `confluence:` block is also accepted under `extra:` for MkDocs strict-mode compatibility. The API token is read from `token:` in `mkdocs.yml`, then `CONFLUENCE_API_TOKEN`, then `MK2CONF_TOKEN`.
|
|
158
|
+
|
|
159
|
+
**Your first publish:**
|
|
160
|
+
|
|
161
|
+
```bash
|
|
162
|
+
export CONFLUENCE_API_TOKEN=your_api_token_here
|
|
163
|
+
mk2conf preview --page docs/index.md --watch # verify output locally
|
|
164
|
+
mk2conf publish --dry-run # check the plan
|
|
165
|
+
mk2conf publish # go live
|
|
166
|
+
```
|
|
167
|
+
|
|
168
|
+
---
|
|
169
|
+
|
|
170
|
+
## Documentation
|
|
171
|
+
|
|
172
|
+
| | |
|
|
173
|
+
|---|---|
|
|
174
|
+
| [docs/commands.md](docs/commands.md) | Full flag reference for all four commands |
|
|
175
|
+
| [docs/features.md](docs/features.md) | Supported Markdown / Material features and known limitations |
|
|
176
|
+
| [Setup.md](Setup.md) | Development environment setup |
|
|
177
|
+
|
|
178
|
+
---
|
|
179
|
+
|
|
180
|
+
## Architecture
|
|
181
|
+
|
|
182
|
+

|
|
183
|
+
|
|
184
|
+
Pipeline stages: **loader → preprocess → IR → transforms → emitter → publisher**. The plan phase makes all API read calls; the execute phase makes all write calls in nav order so parent pages always exist before their children.
|
|
185
|
+
|
|
186
|
+
---
|
|
187
|
+
|
|
188
|
+
## Development
|
|
189
|
+
|
|
190
|
+
```bash
|
|
191
|
+
uv run pytest -q
|
|
192
|
+
uv run ruff check src tests
|
|
193
|
+
uv run mypy src
|
|
194
|
+
uv run vulture src --min-confidence 80
|
|
195
|
+
```
|
|
196
|
+
|
{mkdocs2confluence-0.11.0 → mkdocs2confluence-0.12.0}/src/mkdocs2confluence.egg-info/SOURCES.txt
RENAMED
|
@@ -37,6 +37,7 @@ src/mkdocs_to_confluence/preview/render.py
|
|
|
37
37
|
src/mkdocs_to_confluence/preview/server.py
|
|
38
38
|
src/mkdocs_to_confluence/publisher/__init__.py
|
|
39
39
|
src/mkdocs_to_confluence/publisher/client.py
|
|
40
|
+
src/mkdocs_to_confluence/publisher/http_retry.py
|
|
40
41
|
src/mkdocs_to_confluence/publisher/pipeline.py
|
|
41
42
|
src/mkdocs_to_confluence/sync/__init__.py
|
|
42
43
|
src/mkdocs_to_confluence/sync/anchoring.py
|
{mkdocs2confluence-0.11.0 → mkdocs2confluence-0.12.0}/src/mkdocs_to_confluence/loader/config.py
RENAMED
|
@@ -33,6 +33,7 @@ class ConfluenceConfig:
|
|
|
33
33
|
github_repo: str | None = None # "owner/repo" — required for sync-comments
|
|
34
34
|
github_token: str | None = None # GitHub PAT (falls back to GITHUB_TOKEN env var)
|
|
35
35
|
github_base_branch: str = "main" # base branch for review PRs
|
|
36
|
+
allow_any_host: bool = False # set True to allow non-Atlassian Cloud base_url hosts
|
|
36
37
|
|
|
37
38
|
|
|
38
39
|
@dataclass(frozen=True)
|
|
@@ -209,6 +210,29 @@ def load_config(mkdocs_yml: Path) -> MkDocsConfig:
|
|
|
209
210
|
if not isinstance(base_url, str) or not base_url.strip():
|
|
210
211
|
raise ConfigError("mkdocs.yml: 'confluence.base_url' is required and must be a non-empty string.")
|
|
211
212
|
|
|
213
|
+
# Security: require HTTPS so credentials are never sent in plaintext.
|
|
214
|
+
parsed_url = urlparse(base_url.strip())
|
|
215
|
+
if parsed_url.scheme != "https":
|
|
216
|
+
raise ConfigError(
|
|
217
|
+
"mkdocs.yml: 'confluence.base_url' must use HTTPS (got "
|
|
218
|
+
f"{parsed_url.scheme!r}). Plain HTTP would transmit credentials in "
|
|
219
|
+
"cleartext."
|
|
220
|
+
)
|
|
221
|
+
|
|
222
|
+
allow_any_host = bool(raw_conf.get("allow_any_host", False))
|
|
223
|
+
|
|
224
|
+
# Security: guard against a repo-controlled base_url redirecting credentials
|
|
225
|
+
# to an attacker host. Non-Atlassian Cloud hosts require an explicit opt-in.
|
|
226
|
+
host = parsed_url.hostname or ""
|
|
227
|
+
_is_atlassian = host == "atlassian.net" or host.endswith(".atlassian.net")
|
|
228
|
+
if not _is_atlassian and not allow_any_host:
|
|
229
|
+
raise ConfigError(
|
|
230
|
+
f"mkdocs.yml: 'confluence.base_url' host {host!r} is not an "
|
|
231
|
+
"Atlassian Cloud domain (*.atlassian.net). If you are using a "
|
|
232
|
+
"self-hosted Confluence instance, add 'allow_any_host: true' under "
|
|
233
|
+
"the 'confluence:' block to acknowledge this."
|
|
234
|
+
)
|
|
235
|
+
|
|
212
236
|
space_key: str | None = raw_conf.get("space_key") or None
|
|
213
237
|
if space_key:
|
|
214
238
|
space_key = space_key.strip() or None
|
|
@@ -248,6 +272,7 @@ def load_config(mkdocs_yml: Path) -> MkDocsConfig:
|
|
|
248
272
|
github_token=(str(raw_conf["github_token"]) if raw_conf.get("github_token")
|
|
249
273
|
else os.environ.get("GITHUB_TOKEN") or None),
|
|
250
274
|
github_base_branch=str(raw_conf.get("github_base_branch", "main")),
|
|
275
|
+
allow_any_host=allow_any_host,
|
|
251
276
|
)
|
|
252
277
|
|
|
253
278
|
# --- extra_css (optional) ---
|
{mkdocs2confluence-0.11.0 → mkdocs2confluence-0.12.0}/src/mkdocs_to_confluence/loader/nav.py
RENAMED
|
@@ -220,7 +220,14 @@ def _traverse(nav: list[Any], docs_dir: Path, level: int, nav_file: str = ".page
|
|
|
220
220
|
elif isinstance(value, str):
|
|
221
221
|
# Could be a page file or a directory reference (awesome-pages style)
|
|
222
222
|
target = (docs_dir / value).resolve()
|
|
223
|
-
|
|
223
|
+
docs_root = docs_dir.resolve()
|
|
224
|
+
if not target.is_relative_to(docs_root):
|
|
225
|
+
warnings.warn(
|
|
226
|
+
f"Nav page '{title}' resolves outside docs_dir ('{target}') — "
|
|
227
|
+
"it will be omitted from the resolved nav.",
|
|
228
|
+
stacklevel=4,
|
|
229
|
+
)
|
|
230
|
+
elif target.is_dir():
|
|
224
231
|
children = _resolve_nav_dir(target, docs_dir, level + 1, nav_file)
|
|
225
232
|
nodes.append(
|
|
226
233
|
NavNode(
|