mkdocs2confluence 0.7.26__tar.gz → 0.7.27__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.
Files changed (72) hide show
  1. {mkdocs2confluence-0.7.26 → mkdocs2confluence-0.7.27}/PKG-INFO +1 -1
  2. {mkdocs2confluence-0.7.26 → mkdocs2confluence-0.7.27}/pyproject.toml +1 -1
  3. {mkdocs2confluence-0.7.26 → mkdocs2confluence-0.7.27}/src/mkdocs2confluence.egg-info/PKG-INFO +1 -1
  4. {mkdocs2confluence-0.7.26 → mkdocs2confluence-0.7.27}/src/mkdocs_to_confluence/publisher/client.py +6 -7
  5. {mkdocs2confluence-0.7.26 → mkdocs2confluence-0.7.27}/src/mkdocs_to_confluence/publisher/pipeline.py +4 -6
  6. {mkdocs2confluence-0.7.26 → mkdocs2confluence-0.7.27}/tests/test_publish_pipeline.py +97 -0
  7. {mkdocs2confluence-0.7.26 → mkdocs2confluence-0.7.27}/LICENSE +0 -0
  8. {mkdocs2confluence-0.7.26 → mkdocs2confluence-0.7.27}/README.md +0 -0
  9. {mkdocs2confluence-0.7.26 → mkdocs2confluence-0.7.27}/setup.cfg +0 -0
  10. {mkdocs2confluence-0.7.26 → mkdocs2confluence-0.7.27}/src/mkdocs2confluence.egg-info/SOURCES.txt +0 -0
  11. {mkdocs2confluence-0.7.26 → mkdocs2confluence-0.7.27}/src/mkdocs2confluence.egg-info/dependency_links.txt +0 -0
  12. {mkdocs2confluence-0.7.26 → mkdocs2confluence-0.7.27}/src/mkdocs2confluence.egg-info/entry_points.txt +0 -0
  13. {mkdocs2confluence-0.7.26 → mkdocs2confluence-0.7.27}/src/mkdocs2confluence.egg-info/requires.txt +0 -0
  14. {mkdocs2confluence-0.7.26 → mkdocs2confluence-0.7.27}/src/mkdocs2confluence.egg-info/top_level.txt +0 -0
  15. {mkdocs2confluence-0.7.26 → mkdocs2confluence-0.7.27}/src/mkdocs_to_confluence/__init__.py +0 -0
  16. {mkdocs2confluence-0.7.26 → mkdocs2confluence-0.7.27}/src/mkdocs_to_confluence/cli.py +0 -0
  17. {mkdocs2confluence-0.7.26 → mkdocs2confluence-0.7.27}/src/mkdocs_to_confluence/emitter/__init__.py +0 -0
  18. {mkdocs2confluence-0.7.26 → mkdocs2confluence-0.7.27}/src/mkdocs_to_confluence/emitter/xhtml.py +0 -0
  19. {mkdocs2confluence-0.7.26 → mkdocs2confluence-0.7.27}/src/mkdocs_to_confluence/ir/__init__.py +0 -0
  20. {mkdocs2confluence-0.7.26 → mkdocs2confluence-0.7.27}/src/mkdocs_to_confluence/ir/document.py +0 -0
  21. {mkdocs2confluence-0.7.26 → mkdocs2confluence-0.7.27}/src/mkdocs_to_confluence/ir/nodes.py +0 -0
  22. {mkdocs2confluence-0.7.26 → mkdocs2confluence-0.7.27}/src/mkdocs_to_confluence/ir/treeutil.py +0 -0
  23. {mkdocs2confluence-0.7.26 → mkdocs2confluence-0.7.27}/src/mkdocs_to_confluence/loader/__init__.py +0 -0
  24. {mkdocs2confluence-0.7.26 → mkdocs2confluence-0.7.27}/src/mkdocs_to_confluence/loader/config.py +0 -0
  25. {mkdocs2confluence-0.7.26 → mkdocs2confluence-0.7.27}/src/mkdocs_to_confluence/loader/extra_css.py +0 -0
  26. {mkdocs2confluence-0.7.26 → mkdocs2confluence-0.7.27}/src/mkdocs_to_confluence/loader/nav.py +0 -0
  27. {mkdocs2confluence-0.7.26 → mkdocs2confluence-0.7.27}/src/mkdocs_to_confluence/loader/page.py +0 -0
  28. {mkdocs2confluence-0.7.26 → mkdocs2confluence-0.7.27}/src/mkdocs_to_confluence/parser/__init__.py +0 -0
  29. {mkdocs2confluence-0.7.26 → mkdocs2confluence-0.7.27}/src/mkdocs_to_confluence/parser/markdown.py +0 -0
  30. {mkdocs2confluence-0.7.26 → mkdocs2confluence-0.7.27}/src/mkdocs_to_confluence/pdf/__init__.py +0 -0
  31. {mkdocs2confluence-0.7.26 → mkdocs2confluence-0.7.27}/src/mkdocs_to_confluence/pdf/generator.py +0 -0
  32. {mkdocs2confluence-0.7.26 → mkdocs2confluence-0.7.27}/src/mkdocs_to_confluence/pdf/render.py +0 -0
  33. {mkdocs2confluence-0.7.26 → mkdocs2confluence-0.7.27}/src/mkdocs_to_confluence/preprocess/__init__.py +0 -0
  34. {mkdocs2confluence-0.7.26 → mkdocs2confluence-0.7.27}/src/mkdocs_to_confluence/preprocess/abbrevs.py +0 -0
  35. {mkdocs2confluence-0.7.26 → mkdocs2confluence-0.7.27}/src/mkdocs_to_confluence/preprocess/fence.py +0 -0
  36. {mkdocs2confluence-0.7.26 → mkdocs2confluence-0.7.27}/src/mkdocs_to_confluence/preprocess/frontmatter.py +0 -0
  37. {mkdocs2confluence-0.7.26 → mkdocs2confluence-0.7.27}/src/mkdocs_to_confluence/preprocess/icons.py +0 -0
  38. {mkdocs2confluence-0.7.26 → mkdocs2confluence-0.7.27}/src/mkdocs_to_confluence/preprocess/includes.py +0 -0
  39. {mkdocs2confluence-0.7.26 → mkdocs2confluence-0.7.27}/src/mkdocs_to_confluence/preprocess/linkdefs.py +0 -0
  40. {mkdocs2confluence-0.7.26 → mkdocs2confluence-0.7.27}/src/mkdocs_to_confluence/preview/__init__.py +0 -0
  41. {mkdocs2confluence-0.7.26 → mkdocs2confluence-0.7.27}/src/mkdocs_to_confluence/preview/render.py +0 -0
  42. {mkdocs2confluence-0.7.26 → mkdocs2confluence-0.7.27}/src/mkdocs_to_confluence/preview/server.py +0 -0
  43. {mkdocs2confluence-0.7.26 → mkdocs2confluence-0.7.27}/src/mkdocs_to_confluence/publisher/__init__.py +0 -0
  44. {mkdocs2confluence-0.7.26 → mkdocs2confluence-0.7.27}/src/mkdocs_to_confluence/transforms/__init__.py +0 -0
  45. {mkdocs2confluence-0.7.26 → mkdocs2confluence-0.7.27}/src/mkdocs_to_confluence/transforms/abbrevs.py +0 -0
  46. {mkdocs2confluence-0.7.26 → mkdocs2confluence-0.7.27}/src/mkdocs_to_confluence/transforms/assets.py +0 -0
  47. {mkdocs2confluence-0.7.26 → mkdocs2confluence-0.7.27}/src/mkdocs_to_confluence/transforms/editlink.py +0 -0
  48. {mkdocs2confluence-0.7.26 → mkdocs2confluence-0.7.27}/src/mkdocs_to_confluence/transforms/images.py +0 -0
  49. {mkdocs2confluence-0.7.26 → mkdocs2confluence-0.7.27}/src/mkdocs_to_confluence/transforms/internallinks.py +0 -0
  50. {mkdocs2confluence-0.7.26 → mkdocs2confluence-0.7.27}/src/mkdocs_to_confluence/transforms/mermaid.py +0 -0
  51. {mkdocs2confluence-0.7.26 → mkdocs2confluence-0.7.27}/tests/test_abbrevs.py +0 -0
  52. {mkdocs2confluence-0.7.26 → mkdocs2confluence-0.7.27}/tests/test_cli.py +0 -0
  53. {mkdocs2confluence-0.7.26 → mkdocs2confluence-0.7.27}/tests/test_editlink.py +0 -0
  54. {mkdocs2confluence-0.7.26 → mkdocs2confluence-0.7.27}/tests/test_emitter.py +0 -0
  55. {mkdocs2confluence-0.7.26 → mkdocs2confluence-0.7.27}/tests/test_extra_css.py +0 -0
  56. {mkdocs2confluence-0.7.26 → mkdocs2confluence-0.7.27}/tests/test_frontmatter.py +0 -0
  57. {mkdocs2confluence-0.7.26 → mkdocs2confluence-0.7.27}/tests/test_icons.py +0 -0
  58. {mkdocs2confluence-0.7.26 → mkdocs2confluence-0.7.27}/tests/test_images.py +0 -0
  59. {mkdocs2confluence-0.7.26 → mkdocs2confluence-0.7.27}/tests/test_internallinks.py +0 -0
  60. {mkdocs2confluence-0.7.26 → mkdocs2confluence-0.7.27}/tests/test_ir.py +0 -0
  61. {mkdocs2confluence-0.7.26 → mkdocs2confluence-0.7.27}/tests/test_linkdefs.py +0 -0
  62. {mkdocs2confluence-0.7.26 → mkdocs2confluence-0.7.27}/tests/test_loader.py +0 -0
  63. {mkdocs2confluence-0.7.26 → mkdocs2confluence-0.7.27}/tests/test_mermaid.py +0 -0
  64. {mkdocs2confluence-0.7.26 → mkdocs2confluence-0.7.27}/tests/test_page_loader.py +0 -0
  65. {mkdocs2confluence-0.7.26 → mkdocs2confluence-0.7.27}/tests/test_parser.py +0 -0
  66. {mkdocs2confluence-0.7.26 → mkdocs2confluence-0.7.27}/tests/test_pdf.py +0 -0
  67. {mkdocs2confluence-0.7.26 → mkdocs2confluence-0.7.27}/tests/test_preprocess.py +0 -0
  68. {mkdocs2confluence-0.7.26 → mkdocs2confluence-0.7.27}/tests/test_preview.py +0 -0
  69. {mkdocs2confluence-0.7.26 → mkdocs2confluence-0.7.27}/tests/test_publish_client.py +0 -0
  70. {mkdocs2confluence-0.7.26 → mkdocs2confluence-0.7.27}/tests/test_publish_config.py +0 -0
  71. {mkdocs2confluence-0.7.26 → mkdocs2confluence-0.7.27}/tests/test_server.py +0 -0
  72. {mkdocs2confluence-0.7.26 → mkdocs2confluence-0.7.27}/tests/test_treeutil.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: mkdocs2confluence
3
- Version: 0.7.26
3
+ Version: 0.7.27
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
@@ -1,6 +1,6 @@
1
1
  [project]
2
2
  name = "mkdocs2confluence"
3
- version = "0.7.26"
3
+ version = "0.7.27"
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" }
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: mkdocs2confluence
3
- Version: 0.7.26
3
+ Version: 0.7.27
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
@@ -371,7 +371,6 @@ class ConfluenceClient:
371
371
  Sends ``{id, name, color}`` when a matching space state is found, otherwise
372
372
  falls back to ``{name, color}`` with a sensible default colour.
373
373
  """
374
- import sys as _sys
375
374
  def _normalize(s: str) -> str:
376
375
  return s.lower().replace("-", " ").strip()
377
376
 
@@ -383,7 +382,6 @@ class ConfluenceClient:
383
382
  self._space_states[cache_key] = self._fetch_available_states(page_id)
384
383
  print(
385
384
  f" [status] space states fetched: {[s.get('name') for s in self._space_states[cache_key]]}",
386
- file=_sys.stderr,
387
385
  )
388
386
 
389
387
  matched: dict[str, Any] | None = None
@@ -398,7 +396,7 @@ class ConfluenceClient:
398
396
  "name": matched["name"],
399
397
  "color": matched["color"],
400
398
  }
401
- print(f" [status] matched space state: {body}", file=_sys.stderr)
399
+ print(f" [status] matched space state: {body}")
402
400
  else:
403
401
  # Fall back: name + color (required together when no id)
404
402
  _default_colors = {
@@ -410,13 +408,13 @@ class ConfluenceClient:
410
408
  "outdated": "#ff7452",
411
409
  }
412
410
  body = {"name": name, "color": _default_colors.get(_normalize(name), "#2684ff")}
413
- print(f" [status] no space state match — using fallback: {body}", file=_sys.stderr)
411
+ print(f" [status] no space state match — using fallback: {body}")
414
412
 
415
413
  url = self._v1(f"/content/{page_id}/state")
416
414
  resp = self._http.put(url, json=body)
417
- print(f" [status] PUT {url} → HTTP {resp.status_code}", file=_sys.stderr)
415
+ print(f" [status] PUT {url} → HTTP {resp.status_code}")
418
416
  if not resp.is_success:
419
- print(f" [status] response body: {resp.text[:300]}", file=_sys.stderr)
417
+ print(f" [status] response body: {resp.text[:300]}")
420
418
  self._raise_for_status(resp, f"set_page_status({page_id!r}, {status_key!r})")
421
419
 
422
420
  def _fetch_available_states(self, page_id: str) -> list[dict[str, Any]]:
@@ -429,7 +427,8 @@ class ConfluenceClient:
429
427
  resp = self._http.get(self._v1(f"/content/{page_id}/state/available"))
430
428
  if resp.is_success:
431
429
  data: dict[str, Any] = resp.json()
432
- return data.get("spaceContentStates") or []
430
+ return list(data.get("spaceContentStates") or [])
431
+ print(f" [warn] could not fetch available states (HTTP {resp.status_code}) — using fallback")
433
432
  return []
434
433
 
435
434
  def list_attachments(self, page_id: str) -> dict[str, dict[str, Any]]:
@@ -664,12 +664,12 @@ def _post_process_action(
664
664
  # Set Confluence page status (rough-draft / in-progress / etc.) — non-fatal.
665
665
  if action.page_id and action.confluence_status and not action.is_folder:
666
666
  try:
667
- print(f" [status] setting '{action.confluence_status}' on page {action.page_id!r}...", file=sys.stderr)
667
+ print(f" [status] setting '{action.confluence_status}' on page {action.page_id!r}...")
668
668
  client.set_page_status(action.page_id, action.confluence_status, space_key=space_key)
669
- print(" [status] ok", file=sys.stderr)
669
+ print(" [status] ok")
670
670
  except Exception as exc:
671
671
  # Always print status errors — user configured status explicitly
672
- print(f" [warn] could not set page status '{action.confluence_status}': {exc}", file=sys.stderr)
672
+ print(f" [warn] could not set page status '{action.confluence_status}': {exc}")
673
673
 
674
674
  # Upload assets — skip files whose mtime is not newer than Confluence.
675
675
  if action.page_id and action.attachments:
@@ -734,14 +734,12 @@ def execute_publish(
734
734
  try:
735
735
  print(
736
736
  f" [status] setting '{action.confluence_status}' on page {action.page_id!r}...",
737
- file=sys.stderr,
738
737
  )
739
738
  client.set_page_status(action.page_id, action.confluence_status, space_key=space_key)
740
- print(" [status] ok", file=sys.stderr)
739
+ print(" [status] ok")
741
740
  except Exception as exc:
742
741
  print(
743
742
  f" [warn] could not set page status '{action.confluence_status}': {exc}",
744
- file=sys.stderr,
745
743
  )
746
744
  continue
747
745
 
@@ -1945,3 +1945,100 @@ def test_execute_publish_quiet_suppresses_stdout(tmp_path: Path, capsys: pytest.
1945
1945
 
1946
1946
  out, _ = capsys.readouterr()
1947
1947
  assert out == ""
1948
+
1949
+
1950
+ # ── Status: end-to-end flow ───────────────────────────────────────────────────
1951
+
1952
+
1953
+ def test_compile_page_returns_confluence_status(tmp_path: Path) -> None:
1954
+ """compile_page must return the status: value from front matter."""
1955
+ docs = tmp_path / "docs"
1956
+ docs.mkdir()
1957
+ md = docs / "page.md"
1958
+ md.write_text("---\nstatus: in-progress\n---\n\n# My Page\n\nContent.\n", encoding="utf-8")
1959
+
1960
+ node = _page_node("My Page", md)
1961
+ config = _make_config(docs)
1962
+ _, _, _, confluence_status = compile_page(node, config)
1963
+
1964
+ assert confluence_status == "in-progress"
1965
+
1966
+
1967
+ def test_plan_publish_sets_confluence_status_on_create(tmp_path: Path) -> None:
1968
+ """plan_publish must carry confluence_status into a create PageAction."""
1969
+ docs = tmp_path / "docs"
1970
+ docs.mkdir()
1971
+ md = docs / "page.md"
1972
+ md.write_text("---\nstatus: in-progress\n---\n\n# My Page\n\nContent.\n", encoding="utf-8")
1973
+
1974
+ node = _page_node("My Page", md)
1975
+ config = _make_config(docs)
1976
+ conf_config = _make_conf_config()
1977
+
1978
+ client = MagicMock()
1979
+ client.find_page.return_value = None
1980
+
1981
+ plan = plan_publish([node], client, config, conf_config, space_id="42")
1982
+
1983
+ assert plan[0].action == "create"
1984
+ assert plan[0].confluence_status == "in-progress"
1985
+
1986
+
1987
+ def test_plan_publish_sets_confluence_status_on_skip(tmp_path: Path) -> None:
1988
+ """plan_publish must carry confluence_status into a skip (unchanged) PageAction."""
1989
+ from mkdocs_to_confluence.publisher.pipeline import _xhtml_hash
1990
+
1991
+ docs = tmp_path / "docs"
1992
+ docs.mkdir()
1993
+ md = docs / "page.md"
1994
+ md.write_text("---\nstatus: in-progress\n---\n\n# My Page\n\nContent.\n", encoding="utf-8")
1995
+
1996
+ node = _page_node("My Page", md)
1997
+ config = _make_config(docs)
1998
+ conf_config = _make_conf_config()
1999
+
2000
+ xhtml, _, _, _ = compile_page(node, config)
2001
+ stored_hash = _xhtml_hash(xhtml)
2002
+
2003
+ existing_page = {"id": "77", "version": {"number": 2}}
2004
+ client = MagicMock()
2005
+ client.find_page.return_value = existing_page
2006
+ client.get_content_hash.return_value = stored_hash
2007
+
2008
+ plan = plan_publish([node], client, config, conf_config, space_id="42")
2009
+
2010
+ assert plan[0].action == "skip"
2011
+ assert plan[0].confluence_status == "in-progress"
2012
+
2013
+
2014
+ def test_execute_publish_calls_set_page_status_on_create(tmp_path: Path) -> None:
2015
+ """execute_publish must call client.set_page_status for a created page with confluence_status."""
2016
+ from mkdocs_to_confluence.publisher.pipeline import execute_publish
2017
+
2018
+ node = _make_section_node("P", [])
2019
+ action = PageAction(
2020
+ node=node, title="P", action="create",
2021
+ parent_id=None, xhtml="<p/>", confluence_status="in-progress",
2022
+ )
2023
+ client = MagicMock()
2024
+ client.create_page.return_value = {"id": "99"}
2025
+
2026
+ execute_publish([action], client, space_id="42", docs_dir=tmp_path)
2027
+
2028
+ client.set_page_status.assert_called_once_with("99", "in-progress", space_key=None)
2029
+
2030
+
2031
+ def test_execute_publish_calls_set_page_status_on_skip(tmp_path: Path) -> None:
2032
+ """execute_publish must call client.set_page_status even for skipped (unchanged) pages."""
2033
+ from mkdocs_to_confluence.publisher.pipeline import execute_publish
2034
+
2035
+ node = _make_section_node("P", [])
2036
+ action = PageAction(
2037
+ node=node, title="P", action="skip",
2038
+ parent_id=None, page_id="88", confluence_status="in-progress",
2039
+ )
2040
+ client = MagicMock()
2041
+
2042
+ execute_publish([action], client, space_id="42", docs_dir=tmp_path)
2043
+
2044
+ client.set_page_status.assert_called_once_with("88", "in-progress", space_key=None)