mkdocs2confluence 0.7.37__tar.gz → 0.8.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.7.37 → mkdocs2confluence-0.8.0}/PKG-INFO +43 -1
- {mkdocs2confluence-0.7.37 → mkdocs2confluence-0.8.0}/README.md +42 -0
- {mkdocs2confluence-0.7.37 → mkdocs2confluence-0.8.0}/pyproject.toml +1 -1
- {mkdocs2confluence-0.7.37 → mkdocs2confluence-0.8.0}/src/mkdocs2confluence.egg-info/PKG-INFO +43 -1
- {mkdocs2confluence-0.7.37 → mkdocs2confluence-0.8.0}/src/mkdocs2confluence.egg-info/SOURCES.txt +12 -0
- {mkdocs2confluence-0.7.37 → mkdocs2confluence-0.8.0}/src/mkdocs_to_confluence/cli.py +123 -0
- {mkdocs2confluence-0.7.37 → mkdocs2confluence-0.8.0}/src/mkdocs_to_confluence/loader/config.py +7 -0
- {mkdocs2confluence-0.7.37 → mkdocs2confluence-0.8.0}/src/mkdocs_to_confluence/publisher/client.py +82 -0
- mkdocs2confluence-0.8.0/src/mkdocs_to_confluence/sync/__init__.py +1 -0
- mkdocs2confluence-0.8.0/src/mkdocs_to_confluence/sync/anchoring.py +25 -0
- mkdocs2confluence-0.8.0/src/mkdocs_to_confluence/sync/command.py +236 -0
- mkdocs2confluence-0.8.0/src/mkdocs_to_confluence/sync/comments.py +89 -0
- mkdocs2confluence-0.8.0/src/mkdocs_to_confluence/sync/github.py +143 -0
- mkdocs2confluence-0.8.0/src/mkdocs_to_confluence/sync/platform.py +50 -0
- mkdocs2confluence-0.8.0/src/mkdocs_to_confluence/sync/state.py +45 -0
- {mkdocs2confluence-0.7.37 → mkdocs2confluence-0.8.0}/tests/test_publish_pipeline.py +77 -0
- mkdocs2confluence-0.8.0/tests/test_sync_anchoring.py +56 -0
- mkdocs2confluence-0.8.0/tests/test_sync_command.py +283 -0
- mkdocs2confluence-0.8.0/tests/test_sync_comments.py +137 -0
- mkdocs2confluence-0.8.0/tests/test_sync_github.py +191 -0
- mkdocs2confluence-0.8.0/tests/test_sync_state.py +75 -0
- {mkdocs2confluence-0.7.37 → mkdocs2confluence-0.8.0}/LICENSE +0 -0
- {mkdocs2confluence-0.7.37 → mkdocs2confluence-0.8.0}/setup.cfg +0 -0
- {mkdocs2confluence-0.7.37 → mkdocs2confluence-0.8.0}/src/mkdocs2confluence.egg-info/dependency_links.txt +0 -0
- {mkdocs2confluence-0.7.37 → mkdocs2confluence-0.8.0}/src/mkdocs2confluence.egg-info/entry_points.txt +0 -0
- {mkdocs2confluence-0.7.37 → mkdocs2confluence-0.8.0}/src/mkdocs2confluence.egg-info/requires.txt +0 -0
- {mkdocs2confluence-0.7.37 → mkdocs2confluence-0.8.0}/src/mkdocs2confluence.egg-info/top_level.txt +0 -0
- {mkdocs2confluence-0.7.37 → mkdocs2confluence-0.8.0}/src/mkdocs_to_confluence/__init__.py +0 -0
- {mkdocs2confluence-0.7.37 → mkdocs2confluence-0.8.0}/src/mkdocs_to_confluence/emitter/__init__.py +0 -0
- {mkdocs2confluence-0.7.37 → mkdocs2confluence-0.8.0}/src/mkdocs_to_confluence/emitter/xhtml.py +0 -0
- {mkdocs2confluence-0.7.37 → mkdocs2confluence-0.8.0}/src/mkdocs_to_confluence/ir/__init__.py +0 -0
- {mkdocs2confluence-0.7.37 → mkdocs2confluence-0.8.0}/src/mkdocs_to_confluence/ir/document.py +0 -0
- {mkdocs2confluence-0.7.37 → mkdocs2confluence-0.8.0}/src/mkdocs_to_confluence/ir/nodes.py +0 -0
- {mkdocs2confluence-0.7.37 → mkdocs2confluence-0.8.0}/src/mkdocs_to_confluence/ir/treeutil.py +0 -0
- {mkdocs2confluence-0.7.37 → mkdocs2confluence-0.8.0}/src/mkdocs_to_confluence/loader/__init__.py +0 -0
- {mkdocs2confluence-0.7.37 → mkdocs2confluence-0.8.0}/src/mkdocs_to_confluence/loader/extra_css.py +0 -0
- {mkdocs2confluence-0.7.37 → mkdocs2confluence-0.8.0}/src/mkdocs_to_confluence/loader/nav.py +0 -0
- {mkdocs2confluence-0.7.37 → mkdocs2confluence-0.8.0}/src/mkdocs_to_confluence/loader/page.py +0 -0
- {mkdocs2confluence-0.7.37 → mkdocs2confluence-0.8.0}/src/mkdocs_to_confluence/parser/__init__.py +0 -0
- {mkdocs2confluence-0.7.37 → mkdocs2confluence-0.8.0}/src/mkdocs_to_confluence/parser/markdown.py +0 -0
- {mkdocs2confluence-0.7.37 → mkdocs2confluence-0.8.0}/src/mkdocs_to_confluence/pdf/__init__.py +0 -0
- {mkdocs2confluence-0.7.37 → mkdocs2confluence-0.8.0}/src/mkdocs_to_confluence/pdf/generator.py +0 -0
- {mkdocs2confluence-0.7.37 → mkdocs2confluence-0.8.0}/src/mkdocs_to_confluence/pdf/render.py +0 -0
- {mkdocs2confluence-0.7.37 → mkdocs2confluence-0.8.0}/src/mkdocs_to_confluence/preprocess/__init__.py +0 -0
- {mkdocs2confluence-0.7.37 → mkdocs2confluence-0.8.0}/src/mkdocs_to_confluence/preprocess/abbrevs.py +0 -0
- {mkdocs2confluence-0.7.37 → mkdocs2confluence-0.8.0}/src/mkdocs_to_confluence/preprocess/fence.py +0 -0
- {mkdocs2confluence-0.7.37 → mkdocs2confluence-0.8.0}/src/mkdocs_to_confluence/preprocess/frontmatter.py +0 -0
- {mkdocs2confluence-0.7.37 → mkdocs2confluence-0.8.0}/src/mkdocs_to_confluence/preprocess/icons.py +0 -0
- {mkdocs2confluence-0.7.37 → mkdocs2confluence-0.8.0}/src/mkdocs_to_confluence/preprocess/includes.py +0 -0
- {mkdocs2confluence-0.7.37 → mkdocs2confluence-0.8.0}/src/mkdocs_to_confluence/preprocess/linkdefs.py +0 -0
- {mkdocs2confluence-0.7.37 → mkdocs2confluence-0.8.0}/src/mkdocs_to_confluence/preview/__init__.py +0 -0
- {mkdocs2confluence-0.7.37 → mkdocs2confluence-0.8.0}/src/mkdocs_to_confluence/preview/render.py +0 -0
- {mkdocs2confluence-0.7.37 → mkdocs2confluence-0.8.0}/src/mkdocs_to_confluence/preview/server.py +0 -0
- {mkdocs2confluence-0.7.37 → mkdocs2confluence-0.8.0}/src/mkdocs_to_confluence/publisher/__init__.py +0 -0
- {mkdocs2confluence-0.7.37 → mkdocs2confluence-0.8.0}/src/mkdocs_to_confluence/publisher/pipeline.py +0 -0
- {mkdocs2confluence-0.7.37 → mkdocs2confluence-0.8.0}/src/mkdocs_to_confluence/transforms/__init__.py +0 -0
- {mkdocs2confluence-0.7.37 → mkdocs2confluence-0.8.0}/src/mkdocs_to_confluence/transforms/abbrevs.py +0 -0
- {mkdocs2confluence-0.7.37 → mkdocs2confluence-0.8.0}/src/mkdocs_to_confluence/transforms/assets.py +0 -0
- {mkdocs2confluence-0.7.37 → mkdocs2confluence-0.8.0}/src/mkdocs_to_confluence/transforms/editlink.py +0 -0
- {mkdocs2confluence-0.7.37 → mkdocs2confluence-0.8.0}/src/mkdocs_to_confluence/transforms/images.py +0 -0
- {mkdocs2confluence-0.7.37 → mkdocs2confluence-0.8.0}/src/mkdocs_to_confluence/transforms/internallinks.py +0 -0
- {mkdocs2confluence-0.7.37 → mkdocs2confluence-0.8.0}/src/mkdocs_to_confluence/transforms/mermaid.py +0 -0
- {mkdocs2confluence-0.7.37 → mkdocs2confluence-0.8.0}/tests/test_abbrevs.py +0 -0
- {mkdocs2confluence-0.7.37 → mkdocs2confluence-0.8.0}/tests/test_cli.py +0 -0
- {mkdocs2confluence-0.7.37 → mkdocs2confluence-0.8.0}/tests/test_editlink.py +0 -0
- {mkdocs2confluence-0.7.37 → mkdocs2confluence-0.8.0}/tests/test_emitter.py +0 -0
- {mkdocs2confluence-0.7.37 → mkdocs2confluence-0.8.0}/tests/test_extra_css.py +0 -0
- {mkdocs2confluence-0.7.37 → mkdocs2confluence-0.8.0}/tests/test_frontmatter.py +0 -0
- {mkdocs2confluence-0.7.37 → mkdocs2confluence-0.8.0}/tests/test_icons.py +0 -0
- {mkdocs2confluence-0.7.37 → mkdocs2confluence-0.8.0}/tests/test_images.py +0 -0
- {mkdocs2confluence-0.7.37 → mkdocs2confluence-0.8.0}/tests/test_internallinks.py +0 -0
- {mkdocs2confluence-0.7.37 → mkdocs2confluence-0.8.0}/tests/test_ir.py +0 -0
- {mkdocs2confluence-0.7.37 → mkdocs2confluence-0.8.0}/tests/test_linkdefs.py +0 -0
- {mkdocs2confluence-0.7.37 → mkdocs2confluence-0.8.0}/tests/test_loader.py +0 -0
- {mkdocs2confluence-0.7.37 → mkdocs2confluence-0.8.0}/tests/test_mermaid.py +0 -0
- {mkdocs2confluence-0.7.37 → mkdocs2confluence-0.8.0}/tests/test_page_loader.py +0 -0
- {mkdocs2confluence-0.7.37 → mkdocs2confluence-0.8.0}/tests/test_parser.py +0 -0
- {mkdocs2confluence-0.7.37 → mkdocs2confluence-0.8.0}/tests/test_pdf.py +0 -0
- {mkdocs2confluence-0.7.37 → mkdocs2confluence-0.8.0}/tests/test_preprocess.py +0 -0
- {mkdocs2confluence-0.7.37 → mkdocs2confluence-0.8.0}/tests/test_preview.py +0 -0
- {mkdocs2confluence-0.7.37 → mkdocs2confluence-0.8.0}/tests/test_publish_client.py +0 -0
- {mkdocs2confluence-0.7.37 → mkdocs2confluence-0.8.0}/tests/test_publish_config.py +0 -0
- {mkdocs2confluence-0.7.37 → mkdocs2confluence-0.8.0}/tests/test_server.py +0 -0
- {mkdocs2confluence-0.7.37 → mkdocs2confluence-0.8.0}/tests/test_treeutil.py +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: mkdocs2confluence
|
|
3
|
-
Version: 0.
|
|
3
|
+
Version: 0.8.0
|
|
4
4
|
Summary: Publish MkDocs Material pages to Confluence Cloud — admonitions, Mermaid diagrams, tabs, page properties and more
|
|
5
5
|
Author: Anders Hybertz
|
|
6
6
|
License: GPL-3.0-or-later
|
|
@@ -270,6 +270,48 @@ The PDF includes a **cover page**, **table of contents** with page numbers, and
|
|
|
270
270
|
|
|
271
271
|
---
|
|
272
272
|
|
|
273
|
+
### `mk2conf sync-comments`
|
|
274
|
+
|
|
275
|
+
Bridge Confluence page/inline comments to GitHub pull request review threads. Non-technical reviewers comment in Confluence; developers address feedback on a GitHub feature branch; Confluence comments are auto-resolved when the PR is merged.
|
|
276
|
+
|
|
277
|
+
```
|
|
278
|
+
mk2conf sync-comments [--config PATH] [--check-merges] [--force] [--dry-run] [--quiet]
|
|
279
|
+
```
|
|
280
|
+
|
|
281
|
+
| Flag | Default | Description |
|
|
282
|
+
|---|---|---|
|
|
283
|
+
| `--config PATH` | `./mkdocs.yml` | Path to `mkdocs.yml` |
|
|
284
|
+
| `--check-merges` | off | Check tracked PRs for merges and auto-resolve Confluence comments |
|
|
285
|
+
| `--force` | off | Re-sync pages that already have an open review PR |
|
|
286
|
+
| `--dry-run` | off | Print what would be synced without making any API calls |
|
|
287
|
+
| `--quiet` | off | Suppress progress output |
|
|
288
|
+
|
|
289
|
+
**Required config** (under `extra.confluence` in `mkdocs.yml`):
|
|
290
|
+
|
|
291
|
+
```yaml
|
|
292
|
+
extra:
|
|
293
|
+
confluence:
|
|
294
|
+
github_repo: owner/repo # required for sync-comments
|
|
295
|
+
github_token: ${GITHUB_TOKEN} # falls back to GITHUB_TOKEN env var
|
|
296
|
+
github_base_branch: main # default: main
|
|
297
|
+
```
|
|
298
|
+
|
|
299
|
+
**Workflow:**
|
|
300
|
+
|
|
301
|
+
1. Run `mk2conf publish` first — generates `.mk2conf-pages.json` mapping source files to Confluence page IDs.
|
|
302
|
+
2. Run `mk2conf sync-comments` — for each page with open Confluence comments, creates a `mk2conf/review/{slug}` branch and PR, then posts each comment as a GitHub review thread. Inline comments with a text selection are anchored to the matching source line; page-level comments fall back to file-level review threads. Every thread body includes a **View in Confluence** deep-link that opens Confluence focused on the exact comment.
|
|
303
|
+
3. Developer addresses feedback on the branch, pushes changes, and merges the PR.
|
|
304
|
+
4. Run `mk2conf sync-comments --check-merges` — detects merged PRs, adds a resolution reply to each Confluence comment with the commit info, and marks the comments as resolved.
|
|
305
|
+
|
|
306
|
+
**State files** (add to `.gitignore`):
|
|
307
|
+
|
|
308
|
+
| File | Purpose |
|
|
309
|
+
|---|---|
|
|
310
|
+
| `.mk2conf-pages.json` | Source path → Confluence page ID map, written after each `publish` |
|
|
311
|
+
| `.mk2conf-sync-state.json` | Tracks open/merged review PRs and their associated comment IDs |
|
|
312
|
+
|
|
313
|
+
---
|
|
314
|
+
|
|
273
315
|
## Supported Markdown features
|
|
274
316
|
|
|
275
317
|
### Block elements
|
|
@@ -230,6 +230,48 @@ The PDF includes a **cover page**, **table of contents** with page numbers, and
|
|
|
230
230
|
|
|
231
231
|
---
|
|
232
232
|
|
|
233
|
+
### `mk2conf sync-comments`
|
|
234
|
+
|
|
235
|
+
Bridge Confluence page/inline comments to GitHub pull request review threads. Non-technical reviewers comment in Confluence; developers address feedback on a GitHub feature branch; Confluence comments are auto-resolved when the PR is merged.
|
|
236
|
+
|
|
237
|
+
```
|
|
238
|
+
mk2conf sync-comments [--config PATH] [--check-merges] [--force] [--dry-run] [--quiet]
|
|
239
|
+
```
|
|
240
|
+
|
|
241
|
+
| Flag | Default | Description |
|
|
242
|
+
|---|---|---|
|
|
243
|
+
| `--config PATH` | `./mkdocs.yml` | Path to `mkdocs.yml` |
|
|
244
|
+
| `--check-merges` | off | Check tracked PRs for merges and auto-resolve Confluence comments |
|
|
245
|
+
| `--force` | off | Re-sync pages that already have an open review PR |
|
|
246
|
+
| `--dry-run` | off | Print what would be synced without making any API calls |
|
|
247
|
+
| `--quiet` | off | Suppress progress output |
|
|
248
|
+
|
|
249
|
+
**Required config** (under `extra.confluence` in `mkdocs.yml`):
|
|
250
|
+
|
|
251
|
+
```yaml
|
|
252
|
+
extra:
|
|
253
|
+
confluence:
|
|
254
|
+
github_repo: owner/repo # required for sync-comments
|
|
255
|
+
github_token: ${GITHUB_TOKEN} # falls back to GITHUB_TOKEN env var
|
|
256
|
+
github_base_branch: main # default: main
|
|
257
|
+
```
|
|
258
|
+
|
|
259
|
+
**Workflow:**
|
|
260
|
+
|
|
261
|
+
1. Run `mk2conf publish` first — generates `.mk2conf-pages.json` mapping source files to Confluence page IDs.
|
|
262
|
+
2. Run `mk2conf sync-comments` — for each page with open Confluence comments, creates a `mk2conf/review/{slug}` branch and PR, then posts each comment as a GitHub review thread. Inline comments with a text selection are anchored to the matching source line; page-level comments fall back to file-level review threads. Every thread body includes a **View in Confluence** deep-link that opens Confluence focused on the exact comment.
|
|
263
|
+
3. Developer addresses feedback on the branch, pushes changes, and merges the PR.
|
|
264
|
+
4. Run `mk2conf sync-comments --check-merges` — detects merged PRs, adds a resolution reply to each Confluence comment with the commit info, and marks the comments as resolved.
|
|
265
|
+
|
|
266
|
+
**State files** (add to `.gitignore`):
|
|
267
|
+
|
|
268
|
+
| File | Purpose |
|
|
269
|
+
|---|---|
|
|
270
|
+
| `.mk2conf-pages.json` | Source path → Confluence page ID map, written after each `publish` |
|
|
271
|
+
| `.mk2conf-sync-state.json` | Tracks open/merged review PRs and their associated comment IDs |
|
|
272
|
+
|
|
273
|
+
---
|
|
274
|
+
|
|
233
275
|
## Supported Markdown features
|
|
234
276
|
|
|
235
277
|
### Block elements
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
[project]
|
|
2
2
|
name = "mkdocs2confluence"
|
|
3
|
-
version = "0.
|
|
3
|
+
version = "0.8.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" }
|
{mkdocs2confluence-0.7.37 → mkdocs2confluence-0.8.0}/src/mkdocs2confluence.egg-info/PKG-INFO
RENAMED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: mkdocs2confluence
|
|
3
|
-
Version: 0.
|
|
3
|
+
Version: 0.8.0
|
|
4
4
|
Summary: Publish MkDocs Material pages to Confluence Cloud — admonitions, Mermaid diagrams, tabs, page properties and more
|
|
5
5
|
Author: Anders Hybertz
|
|
6
6
|
License: GPL-3.0-or-later
|
|
@@ -270,6 +270,48 @@ The PDF includes a **cover page**, **table of contents** with page numbers, and
|
|
|
270
270
|
|
|
271
271
|
---
|
|
272
272
|
|
|
273
|
+
### `mk2conf sync-comments`
|
|
274
|
+
|
|
275
|
+
Bridge Confluence page/inline comments to GitHub pull request review threads. Non-technical reviewers comment in Confluence; developers address feedback on a GitHub feature branch; Confluence comments are auto-resolved when the PR is merged.
|
|
276
|
+
|
|
277
|
+
```
|
|
278
|
+
mk2conf sync-comments [--config PATH] [--check-merges] [--force] [--dry-run] [--quiet]
|
|
279
|
+
```
|
|
280
|
+
|
|
281
|
+
| Flag | Default | Description |
|
|
282
|
+
|---|---|---|
|
|
283
|
+
| `--config PATH` | `./mkdocs.yml` | Path to `mkdocs.yml` |
|
|
284
|
+
| `--check-merges` | off | Check tracked PRs for merges and auto-resolve Confluence comments |
|
|
285
|
+
| `--force` | off | Re-sync pages that already have an open review PR |
|
|
286
|
+
| `--dry-run` | off | Print what would be synced without making any API calls |
|
|
287
|
+
| `--quiet` | off | Suppress progress output |
|
|
288
|
+
|
|
289
|
+
**Required config** (under `extra.confluence` in `mkdocs.yml`):
|
|
290
|
+
|
|
291
|
+
```yaml
|
|
292
|
+
extra:
|
|
293
|
+
confluence:
|
|
294
|
+
github_repo: owner/repo # required for sync-comments
|
|
295
|
+
github_token: ${GITHUB_TOKEN} # falls back to GITHUB_TOKEN env var
|
|
296
|
+
github_base_branch: main # default: main
|
|
297
|
+
```
|
|
298
|
+
|
|
299
|
+
**Workflow:**
|
|
300
|
+
|
|
301
|
+
1. Run `mk2conf publish` first — generates `.mk2conf-pages.json` mapping source files to Confluence page IDs.
|
|
302
|
+
2. Run `mk2conf sync-comments` — for each page with open Confluence comments, creates a `mk2conf/review/{slug}` branch and PR, then posts each comment as a GitHub review thread. Inline comments with a text selection are anchored to the matching source line; page-level comments fall back to file-level review threads. Every thread body includes a **View in Confluence** deep-link that opens Confluence focused on the exact comment.
|
|
303
|
+
3. Developer addresses feedback on the branch, pushes changes, and merges the PR.
|
|
304
|
+
4. Run `mk2conf sync-comments --check-merges` — detects merged PRs, adds a resolution reply to each Confluence comment with the commit info, and marks the comments as resolved.
|
|
305
|
+
|
|
306
|
+
**State files** (add to `.gitignore`):
|
|
307
|
+
|
|
308
|
+
| File | Purpose |
|
|
309
|
+
|---|---|
|
|
310
|
+
| `.mk2conf-pages.json` | Source path → Confluence page ID map, written after each `publish` |
|
|
311
|
+
| `.mk2conf-sync-state.json` | Tracks open/merged review PRs and their associated comment IDs |
|
|
312
|
+
|
|
313
|
+
---
|
|
314
|
+
|
|
273
315
|
## Supported Markdown features
|
|
274
316
|
|
|
275
317
|
### Block elements
|
{mkdocs2confluence-0.7.37 → mkdocs2confluence-0.8.0}/src/mkdocs2confluence.egg-info/SOURCES.txt
RENAMED
|
@@ -38,6 +38,13 @@ 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
40
|
src/mkdocs_to_confluence/publisher/pipeline.py
|
|
41
|
+
src/mkdocs_to_confluence/sync/__init__.py
|
|
42
|
+
src/mkdocs_to_confluence/sync/anchoring.py
|
|
43
|
+
src/mkdocs_to_confluence/sync/command.py
|
|
44
|
+
src/mkdocs_to_confluence/sync/comments.py
|
|
45
|
+
src/mkdocs_to_confluence/sync/github.py
|
|
46
|
+
src/mkdocs_to_confluence/sync/platform.py
|
|
47
|
+
src/mkdocs_to_confluence/sync/state.py
|
|
41
48
|
src/mkdocs_to_confluence/transforms/__init__.py
|
|
42
49
|
src/mkdocs_to_confluence/transforms/abbrevs.py
|
|
43
50
|
src/mkdocs_to_confluence/transforms/assets.py
|
|
@@ -67,4 +74,9 @@ tests/test_publish_client.py
|
|
|
67
74
|
tests/test_publish_config.py
|
|
68
75
|
tests/test_publish_pipeline.py
|
|
69
76
|
tests/test_server.py
|
|
77
|
+
tests/test_sync_anchoring.py
|
|
78
|
+
tests/test_sync_command.py
|
|
79
|
+
tests/test_sync_comments.py
|
|
80
|
+
tests/test_sync_github.py
|
|
81
|
+
tests/test_sync_state.py
|
|
70
82
|
tests/test_treeutil.py
|
|
@@ -223,6 +223,48 @@ def _build_parser() -> argparse.ArgumentParser:
|
|
|
223
223
|
help="Suppress per-item progress output.",
|
|
224
224
|
)
|
|
225
225
|
|
|
226
|
+
# --- sync-comments ---
|
|
227
|
+
sc = sub.add_parser(
|
|
228
|
+
"sync-comments",
|
|
229
|
+
help="Sync open Confluence comments to GitHub review PRs.",
|
|
230
|
+
formatter_class=argparse.RawDescriptionHelpFormatter,
|
|
231
|
+
epilog=(
|
|
232
|
+
"Requires github_repo and github_token (or GITHUB_TOKEN env var) in mkdocs.yml.\n"
|
|
233
|
+
"\n"
|
|
234
|
+
"Examples:\n"
|
|
235
|
+
" mk2conf sync-comments # sync new comments → PRs\n"
|
|
236
|
+
" mk2conf sync-comments --check-merges # resolve merged PRs in Confluence\n"
|
|
237
|
+
" mk2conf sync-comments --dry-run # preview without making changes\n"
|
|
238
|
+
),
|
|
239
|
+
)
|
|
240
|
+
sc.add_argument(
|
|
241
|
+
"--config",
|
|
242
|
+
metavar="PATH",
|
|
243
|
+
default="mkdocs.yml",
|
|
244
|
+
help="Path to mkdocs.yml (default: ./mkdocs.yml).",
|
|
245
|
+
)
|
|
246
|
+
sc.add_argument(
|
|
247
|
+
"--check-merges",
|
|
248
|
+
action="store_true",
|
|
249
|
+
help="Check tracked PRs for merges and resolve their Confluence comments.",
|
|
250
|
+
)
|
|
251
|
+
sc.add_argument(
|
|
252
|
+
"--force",
|
|
253
|
+
action="store_true",
|
|
254
|
+
help="Re-sync pages that already have an open review PR.",
|
|
255
|
+
)
|
|
256
|
+
sc.add_argument(
|
|
257
|
+
"--dry-run",
|
|
258
|
+
action="store_true",
|
|
259
|
+
help="Print what would be synced without creating branches, PRs, or review threads.",
|
|
260
|
+
)
|
|
261
|
+
sc.add_argument(
|
|
262
|
+
"--quiet",
|
|
263
|
+
"-q",
|
|
264
|
+
action="store_true",
|
|
265
|
+
help="Suppress progress output.",
|
|
266
|
+
)
|
|
267
|
+
|
|
226
268
|
return parser
|
|
227
269
|
|
|
228
270
|
|
|
@@ -244,6 +286,8 @@ def main(argv: list[str] | None = None) -> None:
|
|
|
244
286
|
_cmd_publish(args)
|
|
245
287
|
elif args.command == "pdf":
|
|
246
288
|
_cmd_pdf(args)
|
|
289
|
+
elif args.command == "sync-comments":
|
|
290
|
+
_cmd_sync_comments(args)
|
|
247
291
|
except (ValueError, FileNotFoundError) as exc:
|
|
248
292
|
print(f"error: {exc}", file=sys.stderr)
|
|
249
293
|
sys.exit(1)
|
|
@@ -503,6 +547,27 @@ def _cmd_publish(args: argparse.Namespace) -> None:
|
|
|
503
547
|
|
|
504
548
|
print(str(report))
|
|
505
549
|
|
|
550
|
+
# Write page map so sync-comments can match source files to Confluence pages.
|
|
551
|
+
if not (getattr(args, "page", None) or getattr(args, "section", None)):
|
|
552
|
+
import json as _json_pm
|
|
553
|
+
try:
|
|
554
|
+
repo_root = config_path.parent
|
|
555
|
+
try:
|
|
556
|
+
docs_rel = config.docs_dir.relative_to(repo_root)
|
|
557
|
+
except ValueError:
|
|
558
|
+
docs_rel = Path("docs")
|
|
559
|
+
page_map = {
|
|
560
|
+
str(docs_rel / action.node.docs_path): action.page_id
|
|
561
|
+
for action in plan
|
|
562
|
+
if action.node.docs_path and action.page_id and not action.is_folder
|
|
563
|
+
}
|
|
564
|
+
pm_path = repo_root / ".mk2conf-pages.json"
|
|
565
|
+
pm_path.write_text(_json_pm.dumps(page_map, indent=2), encoding="utf-8")
|
|
566
|
+
if not getattr(args, "quiet", False):
|
|
567
|
+
print(f"Page map: {len(page_map)} page(s) → {pm_path.name}")
|
|
568
|
+
except Exception as exc:
|
|
569
|
+
print(f" [warn] could not write page map: {exc}", file=sys.stderr)
|
|
570
|
+
|
|
506
571
|
if getattr(args, "report", None):
|
|
507
572
|
import json as _json
|
|
508
573
|
|
|
@@ -612,3 +677,61 @@ def _cmd_pdf(args: argparse.Namespace) -> None:
|
|
|
612
677
|
sys.exit(1)
|
|
613
678
|
|
|
614
679
|
print(f"PDF written to {out_path}")
|
|
680
|
+
|
|
681
|
+
|
|
682
|
+
def _cmd_sync_comments(args: argparse.Namespace) -> None:
|
|
683
|
+
from mkdocs_to_confluence.publisher.client import ConfluenceClient, ConfluenceError
|
|
684
|
+
from mkdocs_to_confluence.sync.command import check_and_resolve_merges, run_sync_comments
|
|
685
|
+
from mkdocs_to_confluence.sync.github import GitHubReviewClient
|
|
686
|
+
|
|
687
|
+
config_path = Path(args.config).resolve()
|
|
688
|
+
config = load_config(config_path)
|
|
689
|
+
|
|
690
|
+
conf = config.confluence
|
|
691
|
+
if conf is None:
|
|
692
|
+
print("error: no 'confluence:' section in mkdocs.yml", file=sys.stderr)
|
|
693
|
+
sys.exit(1)
|
|
694
|
+
|
|
695
|
+
if not conf.token:
|
|
696
|
+
print("error: Confluence API token not set. Set CONFLUENCE_API_TOKEN env var.", file=sys.stderr)
|
|
697
|
+
sys.exit(1)
|
|
698
|
+
|
|
699
|
+
if not conf.github_repo:
|
|
700
|
+
print(
|
|
701
|
+
"error: 'confluence.github_repo' is required for sync-comments (e.g. 'owner/repo').",
|
|
702
|
+
file=sys.stderr,
|
|
703
|
+
)
|
|
704
|
+
sys.exit(1)
|
|
705
|
+
|
|
706
|
+
if not conf.github_token:
|
|
707
|
+
print(
|
|
708
|
+
"error: GitHub token not set. Set GITHUB_TOKEN env var or 'confluence.github_token' in mkdocs.yml.",
|
|
709
|
+
file=sys.stderr,
|
|
710
|
+
)
|
|
711
|
+
sys.exit(1)
|
|
712
|
+
|
|
713
|
+
review_client = GitHubReviewClient(conf.github_repo, conf.github_token)
|
|
714
|
+
config_dir = config_path.parent
|
|
715
|
+
|
|
716
|
+
try:
|
|
717
|
+
with ConfluenceClient(conf) as confluence_client:
|
|
718
|
+
if args.check_merges:
|
|
719
|
+
check_and_resolve_merges(
|
|
720
|
+
config_dir=config_dir,
|
|
721
|
+
confluence_client=confluence_client,
|
|
722
|
+
review_client=review_client,
|
|
723
|
+
quiet=args.quiet,
|
|
724
|
+
)
|
|
725
|
+
else:
|
|
726
|
+
run_sync_comments(
|
|
727
|
+
config=config,
|
|
728
|
+
config_dir=config_dir,
|
|
729
|
+
confluence_client=confluence_client,
|
|
730
|
+
review_client=review_client,
|
|
731
|
+
force=args.force,
|
|
732
|
+
dry_run=args.dry_run,
|
|
733
|
+
quiet=args.quiet,
|
|
734
|
+
)
|
|
735
|
+
except ConfluenceError as exc:
|
|
736
|
+
print(f"error: {exc}", file=sys.stderr)
|
|
737
|
+
sys.exit(1)
|
{mkdocs2confluence-0.7.37 → mkdocs2confluence-0.8.0}/src/mkdocs_to_confluence/loader/config.py
RENAMED
|
@@ -30,6 +30,9 @@ class ConfluenceConfig:
|
|
|
30
30
|
full_width: bool = True # set full-width layout on every published page
|
|
31
31
|
nav_file: str = ".pages" # awesome-pages filename (default: .pages, can be .nav etc.)
|
|
32
32
|
mermaid_render: str = "kroki" # "kroki", "kroki:<url>", or "none"
|
|
33
|
+
github_repo: str | None = None # "owner/repo" — required for sync-comments
|
|
34
|
+
github_token: str | None = None # GitHub PAT (falls back to GITHUB_TOKEN env var)
|
|
35
|
+
github_base_branch: str = "main" # base branch for review PRs
|
|
33
36
|
|
|
34
37
|
|
|
35
38
|
@dataclass(frozen=True)
|
|
@@ -241,6 +244,10 @@ def load_config(mkdocs_yml: Path) -> MkDocsConfig:
|
|
|
241
244
|
full_width=bool(raw_conf.get("full_width", True)),
|
|
242
245
|
nav_file=str(raw_conf.get("nav_file", ".pages")),
|
|
243
246
|
mermaid_render=str(raw_conf.get("mermaid_render", "kroki")),
|
|
247
|
+
github_repo=str(raw_conf["github_repo"]) if raw_conf.get("github_repo") else None,
|
|
248
|
+
github_token=(str(raw_conf["github_token"]) if raw_conf.get("github_token")
|
|
249
|
+
else os.environ.get("GITHUB_TOKEN") or None),
|
|
250
|
+
github_base_branch=str(raw_conf.get("github_base_branch", "main")),
|
|
244
251
|
)
|
|
245
252
|
|
|
246
253
|
# --- extra_css (optional) ---
|
{mkdocs2confluence-0.7.37 → mkdocs2confluence-0.8.0}/src/mkdocs_to_confluence/publisher/client.py
RENAMED
|
@@ -538,3 +538,85 @@ class ConfluenceClient:
|
|
|
538
538
|
"""Permanently delete *page_id* from Confluence."""
|
|
539
539
|
resp = self._http.delete(self._v2(f"/pages/{page_id}"))
|
|
540
540
|
self._raise_for_status(resp, f"delete_page({page_id!r})")
|
|
541
|
+
|
|
542
|
+
# ── Comments ───────────────────────────────────────────────────────────────
|
|
543
|
+
|
|
544
|
+
def get_page_inline_comments(self, page_id: str) -> list[dict[str, Any]]:
|
|
545
|
+
"""Return all open inline comments for *page_id* (paginated)."""
|
|
546
|
+
results: list[dict[str, Any]] = []
|
|
547
|
+
url = self._v2(f"/pages/{page_id}/inline-comments")
|
|
548
|
+
params: dict[str, Any] = {"resolution-status": "open", "body-format": "storage", "limit": 250}
|
|
549
|
+
while True:
|
|
550
|
+
resp = self._http.get(url, params=params)
|
|
551
|
+
self._raise_for_status(resp, f"get_page_inline_comments({page_id!r})")
|
|
552
|
+
data = resp.json()
|
|
553
|
+
results.extend(data.get("results", []))
|
|
554
|
+
next_url = data.get("_links", {}).get("next")
|
|
555
|
+
if not next_url:
|
|
556
|
+
break
|
|
557
|
+
url = self._v2(f"/pages/{page_id}/inline-comments")
|
|
558
|
+
params = {"resolution-status": "open", "body-format": "storage",
|
|
559
|
+
"limit": 250, "cursor": _extract_cursor(next_url)}
|
|
560
|
+
return results
|
|
561
|
+
|
|
562
|
+
def get_page_footer_comments(self, page_id: str) -> list[dict[str, Any]]:
|
|
563
|
+
"""Return all open footer comments for *page_id* (paginated)."""
|
|
564
|
+
results: list[dict[str, Any]] = []
|
|
565
|
+
url = self._v2(f"/pages/{page_id}/footer-comments")
|
|
566
|
+
params: dict[str, Any] = {"resolution-status": "open", "body-format": "storage", "limit": 250}
|
|
567
|
+
while True:
|
|
568
|
+
resp = self._http.get(url, params=params)
|
|
569
|
+
self._raise_for_status(resp, f"get_page_footer_comments({page_id!r})")
|
|
570
|
+
data = resp.json()
|
|
571
|
+
results.extend(data.get("results", []))
|
|
572
|
+
next_url = data.get("_links", {}).get("next")
|
|
573
|
+
if not next_url:
|
|
574
|
+
break
|
|
575
|
+
url = self._v2(f"/pages/{page_id}/footer-comments")
|
|
576
|
+
params = {"resolution-status": "open", "body-format": "storage",
|
|
577
|
+
"limit": 250, "cursor": _extract_cursor(next_url)}
|
|
578
|
+
return results
|
|
579
|
+
|
|
580
|
+
def add_comment_reply(self, comment_id: str, reply_text: str) -> None:
|
|
581
|
+
"""Post a reply to *comment_id* using the v1 content API."""
|
|
582
|
+
url = self._v1(f"/content/{comment_id}/child/comment")
|
|
583
|
+
resp = self._http.post(url, json={
|
|
584
|
+
"type": "comment",
|
|
585
|
+
"body": {
|
|
586
|
+
"storage": {
|
|
587
|
+
"value": f"<p>{reply_text}</p>",
|
|
588
|
+
"representation": "storage",
|
|
589
|
+
}
|
|
590
|
+
},
|
|
591
|
+
})
|
|
592
|
+
self._raise_for_status(resp, f"add_comment_reply({comment_id!r})")
|
|
593
|
+
|
|
594
|
+
def resolve_inline_comment(self, comment_id: str) -> None:
|
|
595
|
+
"""Resolve an inline comment by setting *resolved=true*."""
|
|
596
|
+
url = self._v2(f"/inline-comments/{comment_id}")
|
|
597
|
+
get_resp = self._http.get(url, params={"body-format": "storage"})
|
|
598
|
+
self._raise_for_status(get_resp, f"get_inline_comment({comment_id!r})")
|
|
599
|
+
data = get_resp.json()
|
|
600
|
+
version = data.get("version", {}).get("number", 1)
|
|
601
|
+
body_value = data.get("body", {}).get("storage", {}).get("value", "")
|
|
602
|
+
resp = self._http.put(url, json={
|
|
603
|
+
"version": {"number": version + 1},
|
|
604
|
+
"resolved": True,
|
|
605
|
+
"body": {"representation": "storage", "value": body_value},
|
|
606
|
+
})
|
|
607
|
+
self._raise_for_status(resp, f"resolve_inline_comment({comment_id!r})")
|
|
608
|
+
|
|
609
|
+
def resolve_footer_comment(self, comment_id: str) -> None:
|
|
610
|
+
"""Resolve a footer comment by setting *resolved=true*."""
|
|
611
|
+
url = self._v2(f"/footer-comments/{comment_id}")
|
|
612
|
+
get_resp = self._http.get(url, params={"body-format": "storage"})
|
|
613
|
+
self._raise_for_status(get_resp, f"get_footer_comment({comment_id!r})")
|
|
614
|
+
data = get_resp.json()
|
|
615
|
+
version = data.get("version", {}).get("number", 1)
|
|
616
|
+
body_value = data.get("body", {}).get("storage", {}).get("value", "")
|
|
617
|
+
resp = self._http.put(url, json={
|
|
618
|
+
"version": {"number": version + 1},
|
|
619
|
+
"resolved": True,
|
|
620
|
+
"body": {"representation": "storage", "value": body_value},
|
|
621
|
+
})
|
|
622
|
+
self._raise_for_status(resp, f"resolve_footer_comment({comment_id!r})")
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
"""Confluence ↔ GitHub comment synchronisation."""
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
"""Map Confluence inlineOriginalSelection text to a source file line number."""
|
|
2
|
+
|
|
3
|
+
from __future__ import annotations
|
|
4
|
+
|
|
5
|
+
from pathlib import Path
|
|
6
|
+
|
|
7
|
+
|
|
8
|
+
def find_anchor_line(source_path: Path, selection_text: str) -> int | None:
|
|
9
|
+
"""Return the 1-based line number of the first line containing *selection_text*.
|
|
10
|
+
|
|
11
|
+
Returns ``None`` when:
|
|
12
|
+
- *selection_text* is empty
|
|
13
|
+
- the file cannot be read
|
|
14
|
+
- no line contains the text
|
|
15
|
+
"""
|
|
16
|
+
if not selection_text:
|
|
17
|
+
return None
|
|
18
|
+
try:
|
|
19
|
+
lines = source_path.read_text(encoding="utf-8").splitlines()
|
|
20
|
+
except OSError:
|
|
21
|
+
return None
|
|
22
|
+
for i, line in enumerate(lines, start=1):
|
|
23
|
+
if selection_text in line:
|
|
24
|
+
return i
|
|
25
|
+
return None
|