markdown-to-confluence 0.4.1__py3-none-any.whl → 0.4.3__py3-none-any.whl

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.
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: markdown-to-confluence
3
- Version: 0.4.1
3
+ Version: 0.4.3
4
4
  Summary: Publish Markdown files to Confluence wiki
5
5
  Author-email: Levente Hunyadi <hunyadi@gmail.com>
6
6
  Maintainer-email: Levente Hunyadi <hunyadi@gmail.com>
@@ -58,15 +58,18 @@ This Python package
58
58
  * Text with **bold**, *italic*, `monospace`, <ins>underline</ins> and ~~strikethrough~~
59
59
  * Link to [sections on the same page](#getting-started) or [external locations](http://example.com/)
60
60
  * Subscript and superscript (with HTML tags `<sub>` and `<sup>`)
61
+ * Math formulas with LaTeX notation
61
62
  * Emoji
62
63
  * Ordered and unordered lists
63
64
  * Block quotes
64
65
  * Code blocks (e.g. Python, JSON, XML)
65
66
  * Images (uploaded as Confluence page attachments or hosted externally)
66
67
  * Tables
68
+ * Footnotes
67
69
  * [Table of contents](https://docs.gitlab.com/ee/user/markdown.html#table-of-contents)
68
70
  * [Admonitions](https://python-markdown.github.io/extensions/admonition/) and alert boxes in [GitHub](https://docs.github.com/en/get-started/writing-on-github/getting-started-with-writing-and-formatting-on-github/basic-writing-and-formatting-syntax#alerts) and [GitLab](https://docs.gitlab.com/ee/development/documentation/styleguide/#alert-boxes)
69
71
  * [Collapsed sections](https://docs.github.com/en/get-started/writing-on-github/working-with-advanced-formatting/organizing-information-with-collapsed-sections)
72
+ * draw\.io diagrams
70
73
  * [Mermaid diagrams](https://mermaid.live/) in code blocks (converted to images)
71
74
 
72
75
  Whenever possible, the implementation uses [Confluence REST API v2](https://developer.atlassian.com/cloud/confluence/rest/v2/) to fetch space properties, and get, create or update page content.
@@ -79,12 +82,26 @@ Whenever possible, the implementation uses [Confluence REST API v2](https://deve
79
82
  pip install markdown-to-confluence
80
83
  ```
81
84
 
82
- **Optional.** Converting code blocks of Mermaid diagrams into Confluence image attachments requires [mermaid-cli](https://github.com/mermaid-js/mermaid-cli):
85
+ ### Command-line utilities
86
+
87
+ **Optional.** Converting `*.drawio` diagrams to PNG or SVG images before uploading to Confluence as attachments requires installing [draw.io](https://www.drawio.com/). (Refer to `--render-drawio`.)
88
+
89
+ **Optional.** Converting code blocks of Mermaid diagrams to PNG or SVG images before uploading to Confluence as attachments requires [mermaid-cli](https://github.com/mermaid-js/mermaid-cli). (Refer to `--render-mermaid`.)
83
90
 
84
91
  ```sh
85
92
  npm install -g @mermaid-js/mermaid-cli
86
93
  ```
87
94
 
95
+ ### Marketplace apps
96
+
97
+ As authors of *md2conf*, we don't endorse or support any particular Confluence marketplace apps.
98
+
99
+ **Optional.** Editable draw\.io diagrams require [draw.io Diagrams marketplace app](https://marketplace.atlassian.com/apps/1210933/draw-io-diagrams-uml-bpmn-aws-erd-flowcharts). (Refer to `--no-render-drawio`.)
100
+
101
+ **Optional.** Displaying Mermaid diagrams in Confluence without pre-rendering in the synchronization phase requires a marketplace app. (Refer to `--no-render-mermaid`.)
102
+
103
+ **Optional.** Displaying formulas and equations in Confluence requires [marketplace app](https://marketplace.atlassian.com/apps/1226109/latex-math-for-confluence-math-formula-equations), refer to [LaTeX Math for Confluence - Math Formula & Equations](https://help.narva.net/latex-math-for-confluence/).
104
+
88
105
  ## Getting started
89
106
 
90
107
  In order to get started, you will need
@@ -152,7 +169,7 @@ If you lack appropriate permissions, you will get an *Unauthorized* response fro
152
169
  Each Markdown file is associated with a Confluence wiki page with a Markdown comment:
153
170
 
154
171
  ```markdown
155
- <!-- confluence-page-id: 85668266616 -->
172
+ <!-- confluence-page-id: 20250001023 -->
156
173
  ```
157
174
 
158
175
  The above tells the tool to synchronize the Markdown file with the given Confluence page ID. This implies that the Confluence wiki page must exist such that it has an ID. The comment can be placed anywhere in the source file.
@@ -193,24 +210,28 @@ First, *md2conf* builds an index of pages in the directory hierarchy. The index
193
210
 
194
211
  If a Markdown file doesn't yet pair up with a Confluence page, *md2conf* creates a new page and assigns a parent. Parent-child relationships are reflected in the navigation panel in Confluence. You can set a root page ID with the command-line option `-r`, which constitutes the topmost parent. (This could correspond to the landing page of your Confluence space. The Confluence page ID is always revealed when you edit a page.) Whenever a directory contains the file `index.md` or `README.md`, this page becomes the future parent page, and all Markdown files in this directory (and possibly nested directories) become its child pages (unless they already have a page ID). However, if an `index.md` or `README.md` file is subsequently found in one of the nested directories, it becomes the parent page of that directory, and any of its subdirectories.
195
212
 
213
+ The top-level directory to be synchronized must always have an `index.md` or `README.md`, which maps to the root of the corresponding sub-tree in Confluence (specified with `-r`).
214
+
196
215
  The concepts above are illustrated in the following sections.
197
216
 
198
217
  #### File-system directory hierarchy
199
218
 
200
- The title of each Markdown file (either the text of the topmost unique heading (`#`), or the title specified in front-matter) is shown next to the file name.
219
+ The title of each Markdown file (either the text of the topmost unique heading (`#`), or the title specified in front-matter) is shown next to the file name. `docs` is the top-level directory to be synchronized.
201
220
 
202
221
  ```
203
- .
222
+ docs
223
+ ├── index.md: Root page
204
224
  ├── computer-science
205
225
  │ ├── index.md: Introduction to computer science
206
226
  │ ├── algebra.md: Linear algebra
207
227
  │ └── algorithms.md: Theory of algorithms
208
- └── machine-learning
209
- ├── README.md: AI and ML
210
- ├── awareness.md: Consciousness and intelligence
211
- └── statistics
212
- ├── index.md: Introduction to statistics
213
- └── median.md: Mean vs. median
228
+ ├── machine-learning
229
+ ├── README.md: AI and ML
230
+ ├── awareness.md: Consciousness and intelligence
231
+ └── statistics
232
+ ├── index.md: Introduction to statistics
233
+ └── median.md: Mean vs. median
234
+ └── ethics.md: Ethical considerations
214
235
  ```
215
236
 
216
237
  #### Page hierarchy in Confluence
@@ -218,14 +239,15 @@ The title of each Markdown file (either the text of the topmost unique heading (
218
239
  Observe how `index.md` and `README.md` files have assumed parent (or ancestor) role for any Markdown files in the same directory (or below).
219
240
 
220
241
  ```
221
- root
242
+ Root page
222
243
  ├── Introduction to computer science
223
244
  │ ├── Linear algebra
224
245
  │ └── Theory of algorithms
225
- └── AI and ML
226
- ├── Consciousness and intelligence
227
- └── Introduction to statistics
228
- └── Mean vs. median
246
+ ├── AI and ML
247
+ ├── Consciousness and intelligence
248
+ └── Introduction to statistics
249
+ └── Mean vs. median
250
+ └── Ethical considerations
229
251
  ```
230
252
 
231
253
  ### Emoji
@@ -299,6 +321,25 @@ Unfortunately, Confluence struggles with SVG images, e.g. they may only show in
299
321
 
300
322
  External images referenced with an absolute URL retain the original URL.
301
323
 
324
+ ### LaTeX math formulas
325
+
326
+ Inline formulas can be enclosed with `$` signs, or delimited with `\(` and `\)`, i.e.
327
+
328
+ * the code `$\sum_{i=1}^{n} i = \frac{n(n+1)}{2}$` is shown as $\sum_{i=1}^{n} i = \frac{n(n+1)}{2}$,
329
+ * and `\(\lim _{x\rightarrow \infty }\frac{1}{x}=0\)` is shown as $\lim _{x\rightarrow \infty }\frac{1}{x}=0$.
330
+
331
+ Block formulas can be enclosed with `$$`, or wrapped in code blocks specifying the language `math`:
332
+
333
+ ```md
334
+ $$\int _{a}^{b}f(x)dx=F(b)-F(a)$$
335
+ ```
336
+
337
+ is shown as
338
+
339
+ $$\int _{a}^{b}f(x)dx=F(b)-F(a)$$
340
+
341
+ Displaying math formulas in Confluence requires the extension [LaTeX Math for Confluence - Math Formula & Equations](https://help.narva.net/latex-math-for-confluence/).
342
+
302
343
  ### Ignoring files
303
344
 
304
345
  Skip files in a directory with rules defined in `.mdignore`. Each rule should occupy a single line. Rules follow the syntax (and constraints) of [fnmatch](https://docs.python.org/3/library/fnmatch.html#fnmatch.fnmatch). Specifically, `?` matches any single character, and `*` matches zero or more characters. For example, use `up-*.md` to exclude Markdown files that start with `up-`. Lines that start with `#` are treated as comments.
@@ -307,6 +348,20 @@ Files that don't have the extension `*.md` are skipped automatically. Hidden dir
307
348
 
308
349
  Relative paths to items in a nested directory are not supported. You must put `.mdignore` in the same directory where the items to be skipped reside.
309
350
 
351
+ If you add the `synchronized` attribute to JSON or YAML front-matter with the value `false`, the document content (including attachments) and metadata (e.g. tags) will not be synchronized with Confluence:
352
+
353
+ ```yaml
354
+ ---
355
+ title: "Collaborating with other teams"
356
+ page_id: "19830101"
357
+ synchronized: false
358
+ ---
359
+
360
+ This Markdown document is neither parsed, nor synchronized with Confluence.
361
+ ```
362
+
363
+ This is useful if you have a page in a hierarchy that participates in parent-child relationships but whose content is edited directly in Confluence. Specifically, these documents can be referenced with relative links from other Markdown documents in the file system tree.
364
+
310
365
  ### Page title
311
366
 
312
367
  *md2conf* makes a best-effort attempt at setting the Confluence wiki page title when it publishes a Markdown document the first time. The following are probed in this order:
@@ -348,12 +403,18 @@ properties:
348
403
 
349
404
  The attribute `properties` is parsed as a dictionary with keys of type string and values of type JSON. *md2conf* passes JSON values to Confluence REST API unchanged.
350
405
 
351
- ### Converting diagrams
406
+ ### draw\.io diagrams
407
+
408
+ With the command-line option `--no-render-drawio` (default), editable diagram data is extracted from images with embedded draw\.io diagrams (`*.drawio.png` and `*.drawio.svg`), and uploaded to Confluence as attachments. `*.drawio` and `*.drawio.xml` files are uploaded as-is. You need a [marketplace app](https://marketplace.atlassian.com/apps/1210933/draw-io-diagrams-uml-bpmn-aws-erd-flowcharts) to view and edit these diagrams on a Confluence page.
409
+
410
+ With the command-line option `--render-drawio`, images with embedded draw\.io diagrams (`*.drawio.png` and `*.drawio.svg`) are uploaded unchanged, and shown on the Confluence page as images. These diagrams are not editable in Confluence. When both an SVG and a PNG image is available, PNG is preferred. `*.drawio` and `*.drawio.xml` files are converted into PNG or SVG images by invoking draw\.io as a command-line utility, and the generated images are uploaded to Confluence as attachments, and shown as images.
411
+
412
+ ### Mermaid diagrams
352
413
 
353
414
  You can include [Mermaid diagrams](https://mermaid.js.org/) in your Markdown documents to create visual representations of systems, processes, and relationships. When a Markdown document contains a code block with the language specifier `mermaid`, *md2conf* offers two options to publish the diagram:
354
415
 
355
- 1. Pre-render into an image. The code block is interpreted by and converted into a PNG or SVG image with the Mermaid diagram utility [mermaid-cli](https://github.com/mermaid-js/mermaid-cli). The generated image is then uploaded to Confluence as an attachment to the page. This is the approach we use and support.
356
- 2. Render on demand. The code block is transformed into a [diagram macro](https://atlasauthority.atlassian.net/wiki/spaces/MARKDOWNCLOUD/pages/2946826241/Diagram+Macro), which is processed by Confluence. You need a [Confluence plugin](https://marketplace.atlassian.com/apps/1211438/markdown-html-plantuml-latex-diagrams-open-api-mermaid) to turn macro definitions into images when a Confluence page is visited. This is a contributed feature. As authors of *md2conf*, we don't endorse or support any particular Confluence plugin.
416
+ 1. Pre-render into an image (command-line option `--render-mermaid`). The code block is interpreted by and converted into a PNG or SVG image with the Mermaid diagram utility [mermaid-cli](https://github.com/mermaid-js/mermaid-cli). The generated image is then uploaded to Confluence as an attachment to the page. This is the approach we use and support.
417
+ 2. Display on demand (command-line option `--no-render-mermaid`). The code block is transformed into a [diagram macro](https://atlasauthority.atlassian.net/wiki/spaces/MARKDOWNCLOUD/pages/2946826241/Diagram+Macro), which is processed by Confluence. You need a [marketplace app](https://marketplace.atlassian.com/apps/1211438/markdown-html-plantuml-latex-diagrams-open-api-mermaid) to turn macro definitions into images when a Confluence page is visited.
357
418
 
358
419
  If you are running into issues with the pre-rendering approach (e.g. misaligned labels in the generated image), verify if `mermaid-cli` can process the Mermaid source:
359
420
 
@@ -379,8 +440,9 @@ Use the `--help` switch to get a full list of supported command-line options:
379
440
 
380
441
  ```console
381
442
  $ python3 -m md2conf --help
382
- usage: md2conf [-h] [--version] [-d DOMAIN] [-p PATH] [--api-url API_URL] [-u USERNAME] [-a API_KEY] [-s SPACE] [-l {debug,info,warning,error,critical}] [-r ROOT_PAGE] [--keep-hierarchy] [--generated-by GENERATED_BY]
383
- [--no-generated-by] [--render-mermaid] [--no-render-mermaid] [--render-mermaid-format {png,svg}] [--heading-anchors] [--ignore-invalid-url] [--local] [--headers [KEY=VALUE ...]] [--webui-links]
443
+ usage: md2conf [-h] [--version] [-d DOMAIN] [-p PATH] [--api-url API_URL] [-u USERNAME] [-a API_KEY] [-s SPACE] [-l {debug,info,warning,error,critical}] [-r ROOT_PAGE] [--keep-hierarchy] [--flatten-hierarchy]
444
+ [--generated-by GENERATED_BY] [--no-generated-by] [--render-drawio] [--no-render-drawio] [--render-mermaid] [--no-render-mermaid] [--render-mermaid-format {png,svg}] [--heading-anchors]
445
+ [--no-heading-anchors] [--ignore-invalid-url] [--local] [--headers [KEY=VALUE ...]] [--webui-links]
384
446
  mdpath
385
447
 
386
448
  positional arguments:
@@ -389,28 +451,30 @@ positional arguments:
389
451
  options:
390
452
  -h, --help show this help message and exit
391
453
  --version show program's version number and exit
392
- -d DOMAIN, --domain DOMAIN
393
- Confluence organization domain.
394
- -p PATH, --path PATH Base path for Confluence (default: '/wiki/').
454
+ -d, --domain DOMAIN Confluence organization domain.
455
+ -p, --path PATH Base path for Confluence (default: '/wiki/').
395
456
  --api-url API_URL Confluence API URL. Required for scoped tokens. Refer to documentation how to obtain one.
396
- -u USERNAME, --username USERNAME
457
+ -u, --username USERNAME
397
458
  Confluence user name.
398
- -a API_KEY, --apikey API_KEY, --api-key API_KEY
459
+ -a, --apikey, --api-key API_KEY
399
460
  Confluence API key. Refer to documentation how to obtain one.
400
- -s SPACE, --space SPACE
401
- Confluence space key for pages to be published. If omitted, will default to user space.
402
- -l {debug,info,warning,error,critical}, --loglevel {debug,info,warning,error,critical}
461
+ -s, --space SPACE Confluence space key for pages to be published. If omitted, will default to user space.
462
+ -l, --loglevel {debug,info,warning,error,critical}
403
463
  Use this option to set the log verbosity.
404
464
  -r ROOT_PAGE Root Confluence page to create new pages. If omitted, will raise exception when creating new pages.
405
465
  --keep-hierarchy Maintain source directory structure when exporting to Confluence.
466
+ --flatten-hierarchy Flatten directories with no index.md or README.md when exporting to Confluence.
406
467
  --generated-by GENERATED_BY
407
468
  Add prompt to pages (default: 'This page has been generated with a tool.').
408
469
  --no-generated-by Do not add 'generated by a tool' prompt to pages.
409
- --render-mermaid Render Mermaid diagrams as image files and add as attachments.
410
- --no-render-mermaid Inline Mermaid diagram in Confluence page.
470
+ --render-drawio Render draw.io diagrams as image files and add as attachments. (Converter required.)
471
+ --no-render-drawio Inline draw.io diagram in Confluence page. (Marketplace app required.)
472
+ --render-mermaid Render Mermaid diagrams as image files and add as attachments. (Converter required.)
473
+ --no-render-mermaid Inline Mermaid diagram in Confluence page. (Marketplace app required.)
411
474
  --render-mermaid-format {png,svg}
412
- Format for rendering Mermaid diagrams (default: 'png').
475
+ Format for rendering Mermaid and draw.io diagrams (default: 'png').
413
476
  --heading-anchors Place an anchor at each section heading with GitHub-style same-page identifiers.
477
+ --no-heading-anchors Don't place an anchor at each section heading.
414
478
  --ignore-invalid-url Emit a warning but otherwise ignore relative URLs that point to ill-specified locations.
415
479
  --local Write XHTML-based Confluence Storage Format files locally without invoking Confluence API.
416
480
  --headers [KEY=VALUE ...]
@@ -0,0 +1,29 @@
1
+ markdown_to_confluence-0.4.3.dist-info/licenses/LICENSE,sha256=Pv43so2bPfmKhmsrmXFyAvS7M30-1i1tzjz6-dfhyOo,1077
2
+ md2conf/__init__.py,sha256=ZEoZwOt29zT2OnQNpbYW9lO3zJEJ6soXwwjYX9PwNNo,402
3
+ md2conf/__main__.py,sha256=RImfFrO2m9C5iebmBrHKlLjosy_A8AY4O7PK9CmiWSw,11120
4
+ md2conf/api.py,sha256=DbG1udDb9ti4OjqgSW3DSuHwxKNFPVDTkhjnaB1GNMI,37193
5
+ md2conf/application.py,sha256=MsumqUFw1WPo6-57r06Poq4wg2DPd3hQ4jA5qC4Oios,8212
6
+ md2conf/collection.py,sha256=EobgMRJgkYloWlY03NZJ52MRC_SGLpTVCHkltDbQyt0,837
7
+ md2conf/converter.py,sha256=mr5UvEhOnM7ZYRIGsgrW85PpxmlpXFjsKYsa8uGFxp0,50475
8
+ md2conf/domain.py,sha256=tA9V0vb5Vo9Nt0eQvwAFARaM9TX88LBVQ73nVvdcaqA,1851
9
+ md2conf/drawio.py,sha256=P_t7Wp7Tg9XkZM2ZchWCWWEdBaU1KgZ_YX9ZlkZo4Dk,8293
10
+ md2conf/emoji.py,sha256=UzDrxqFo59wHmbbJmMNdn0rYFDXbZE4qirOM-_egzXc,2603
11
+ md2conf/entities.dtd,sha256=M6NzqL5N7dPs_eUA_6sDsiSLzDaAacrx9LdttiufvYU,30215
12
+ md2conf/extra.py,sha256=VuMxuOnnC2Qwy6y52ukIxsaYhrZArRqMmRHRE4QZl8g,687
13
+ md2conf/local.py,sha256=Cicfp9SJDJuX0aUWZPWCfWKPfQQWxEbifUsmqwxFjDU,3733
14
+ md2conf/markdown.py,sha256=9BQbYD4GfpBYmx-3N1M36u2nVWY0VJ9UWKye2Jtnmnk,2901
15
+ md2conf/matcher.py,sha256=m5rZjYZSjhKfdeKS8JdPq7cG861Mc6rVZBkrIOZTHGE,6916
16
+ md2conf/mermaid.py,sha256=f0x7ISj-41ZMh4zTAFPhIWwr94SDcsVZUc1NWqmH_G4,2508
17
+ md2conf/metadata.py,sha256=LzZM-oPNnzCULmLhF516tPlV5zZBknccwMHt8Nan-xg,1007
18
+ md2conf/processor.py,sha256=z2d2KMPEYWaxflOtH2UTwrjzpPU8TtLSEUvor85ez1Q,9732
19
+ md2conf/properties.py,sha256=RC1jY_TKVbOv2bJxXn27Fj4fNWzyoNUQt6ltgUyVQAQ,3987
20
+ md2conf/puppeteer-config.json,sha256=-dMTAN_7kNTGbDlfXzApl0KJpAWna9YKZdwMKbpOb60,159
21
+ md2conf/py.typed,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
22
+ md2conf/scanner.py,sha256=Cyvjab8tBvKgubttQvNagS8nailuTvFBqUGoiX5MNp8,5351
23
+ md2conf/xml.py,sha256=HoKJfF1yRZ3Gk8jTS-kRpOqVs0nQJZyr56l0Fo3y9fs,2193
24
+ markdown_to_confluence-0.4.3.dist-info/METADATA,sha256=sU45yX796M_cZ0ssGMaDtuxb0xwtKaPTktNAy2rczMg,29119
25
+ markdown_to_confluence-0.4.3.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
26
+ markdown_to_confluence-0.4.3.dist-info/entry_points.txt,sha256=F1zxa1wtEObtbHS-qp46330WVFLHdMnV2wQ-ZorRmX0,50
27
+ markdown_to_confluence-0.4.3.dist-info/top_level.txt,sha256=_FJfl_kHrHNidyjUOuS01ngu_jDsfc-ZjSocNRJnTzU,8
28
+ markdown_to_confluence-0.4.3.dist-info/zip-safe,sha256=AbpHGcgLb-kRsJGnwFEktk7uzpZOCcBY74-YBdrKVGs,1
29
+ markdown_to_confluence-0.4.3.dist-info/RECORD,,
md2conf/__init__.py CHANGED
@@ -5,7 +5,7 @@ Parses Markdown files, converts Markdown content into the Confluence Storage For
5
5
  Confluence API endpoints to upload images and content.
6
6
  """
7
7
 
8
- __version__ = "0.4.1"
8
+ __version__ = "0.4.3"
9
9
  __author__ = "Levente Hunyadi"
10
10
  __copyright__ = "Copyright 2022-2025, Levente Hunyadi"
11
11
  __license__ = "MIT"
md2conf/__main__.py CHANGED
@@ -15,16 +15,11 @@ import os.path
15
15
  import sys
16
16
  import typing
17
17
  from pathlib import Path
18
- from typing import Any, Literal, Optional, Sequence, Union
19
-
20
- import requests
18
+ from typing import Any, Iterable, Literal, Optional, Sequence, Union
21
19
 
22
20
  from . import __version__
23
- from .api import ConfluenceAPI
24
- from .application import Application
25
- from .converter import ConfluenceDocumentOptions, ConfluencePageID
21
+ from .domain import ConfluenceDocumentOptions, ConfluencePageID
26
22
  from .extra import override
27
- from .local import LocalConverter
28
23
  from .metadata import ConfluenceSiteMetadata
29
24
  from .properties import ArgumentError, ConfluenceConnectionProperties, ConfluenceSiteProperties
30
25
 
@@ -43,6 +38,7 @@ class Arguments(argparse.Namespace):
43
38
  root_page: Optional[str]
44
39
  keep_hierarchy: bool
45
40
  generated_by: Optional[str]
41
+ render_drawio: bool
46
42
  render_mermaid: bool
47
43
  diagram_output_format: Literal["png", "svg"]
48
44
  local: bool
@@ -51,7 +47,7 @@ class Arguments(argparse.Namespace):
51
47
 
52
48
 
53
49
  class KwargsAppendAction(argparse.Action):
54
- """Append key-value pairs to a dictionary"""
50
+ """Append key-value pairs to a dictionary."""
55
51
 
56
52
  @override
57
53
  def __call__(
@@ -71,8 +67,52 @@ class KwargsAppendAction(argparse.Action):
71
67
  setattr(namespace, self.dest, d)
72
68
 
73
69
 
70
+ def unsupported(prefer: str) -> type[argparse.Action]:
71
+ class UnsupportedAction(argparse.Action):
72
+ """Display an error for unsupported command-line options."""
73
+
74
+ @override
75
+ def __call__(
76
+ self,
77
+ parser: argparse.ArgumentParser,
78
+ namespace: argparse.Namespace,
79
+ values: Union[None, str, Sequence[Any]],
80
+ option_string: Optional[str] = None,
81
+ ) -> None:
82
+ raise argparse.ArgumentError(
83
+ self,
84
+ f"this command-line option is no longer supported, use `--{prefer}`",
85
+ )
86
+
87
+ @override
88
+ def __repr__(self) -> str:
89
+ return f"{unsupported.__name__}({repr(prefer)})"
90
+
91
+ return UnsupportedAction
92
+
93
+
94
+ class PositionalOnlyHelpFormatter(argparse.HelpFormatter):
95
+ def _format_usage(
96
+ self,
97
+ usage: Optional[str],
98
+ actions: Iterable[argparse.Action],
99
+ groups: Iterable[argparse._MutuallyExclusiveGroup],
100
+ prefix: Optional[str],
101
+ ) -> str:
102
+ # filter only positional arguments
103
+ positional_actions = [a for a in actions if not a.option_strings]
104
+
105
+ # format usage string with only positional arguments
106
+ usage_str = super()._format_usage(usage, positional_actions, groups, prefix).rstrip()
107
+
108
+ # insert [OPTIONS] as a placeholder for all options (detailed below)
109
+ usage_str += " [OPTIONS]\n"
110
+
111
+ return usage_str
112
+
113
+
74
114
  def main() -> None:
75
- parser = argparse.ArgumentParser()
115
+ parser = argparse.ArgumentParser(formatter_class=PositionalOnlyHelpFormatter)
76
116
  parser.prog = os.path.basename(os.path.dirname(__file__))
77
117
  parser.add_argument("--version", action="version", version=__version__)
78
118
  parser.add_argument("mdpath", help="Path to Markdown file or directory to convert and publish.")
@@ -123,6 +163,12 @@ def main() -> None:
123
163
  default=False,
124
164
  help="Maintain source directory structure when exporting to Confluence.",
125
165
  )
166
+ parser.add_argument(
167
+ "--flatten-hierarchy",
168
+ dest="keep_hierarchy",
169
+ action="store_false",
170
+ help="Flatten directories with no index.md or README.md when exporting to Confluence.",
171
+ )
126
172
  parser.add_argument(
127
173
  "--generated-by",
128
174
  default="This page has been generated with a tool.",
@@ -135,24 +181,43 @@ def main() -> None:
135
181
  const=None,
136
182
  help="Do not add 'generated by a tool' prompt to pages.",
137
183
  )
184
+ parser.add_argument(
185
+ "--render-drawio",
186
+ dest="render_drawio",
187
+ action="store_true",
188
+ default=True,
189
+ help="Render draw.io diagrams as image files and add as attachments. (Installed utility required.)",
190
+ )
191
+ parser.add_argument(
192
+ "--no-render-drawio",
193
+ dest="render_drawio",
194
+ action="store_false",
195
+ help="Inline draw.io diagram in Confluence page. (Marketplace app required.)",
196
+ )
138
197
  parser.add_argument(
139
198
  "--render-mermaid",
140
199
  dest="render_mermaid",
141
200
  action="store_true",
142
201
  default=True,
143
- help="Render Mermaid diagrams as image files and add as attachments.",
202
+ help="Render Mermaid diagrams as image files and add as attachments. (Installed utility required.)",
144
203
  )
145
204
  parser.add_argument(
146
205
  "--no-render-mermaid",
147
206
  dest="render_mermaid",
148
207
  action="store_false",
149
- help="Inline Mermaid diagram in Confluence page.",
208
+ help="Inline Mermaid diagram in Confluence page. (Marketplace app required.)",
150
209
  )
151
210
  parser.add_argument(
152
- "--render-mermaid-format",
211
+ "--diagram-output-format",
153
212
  dest="diagram_output_format",
154
213
  choices=["png", "svg"],
155
214
  default="png",
215
+ help="Format for rendering Mermaid and draw.io diagrams (default: 'png').",
216
+ )
217
+ parser.add_argument(
218
+ "--render-mermaid-format",
219
+ action=unsupported("diagram-output-format"),
220
+ metavar="FORMAT",
156
221
  help="Format for rendering Mermaid diagrams (default: 'png').",
157
222
  )
158
223
  parser.add_argument(
@@ -161,6 +226,12 @@ def main() -> None:
161
226
  default=False,
162
227
  help="Place an anchor at each section heading with GitHub-style same-page identifiers.",
163
228
  )
229
+ parser.add_argument(
230
+ "--no-heading-anchors",
231
+ action="store_false",
232
+ dest="heading_anchors",
233
+ help="Don't place an anchor at each section heading.",
234
+ )
164
235
  parser.add_argument(
165
236
  "--ignore-invalid-url",
166
237
  action="store_true",
@@ -175,7 +246,7 @@ def main() -> None:
175
246
  )
176
247
  parser.add_argument(
177
248
  "--headers",
178
- nargs="*",
249
+ nargs="+",
179
250
  required=False,
180
251
  action=KwargsAppendAction,
181
252
  metavar="KEY=VALUE",
@@ -204,11 +275,14 @@ def main() -> None:
204
275
  generated_by=args.generated_by,
205
276
  root_page_id=ConfluencePageID(args.root_page) if args.root_page else None,
206
277
  keep_hierarchy=args.keep_hierarchy,
278
+ render_drawio=args.render_drawio,
207
279
  render_mermaid=args.render_mermaid,
208
280
  diagram_output_format=args.diagram_output_format,
209
281
  webui_links=args.webui_links,
210
282
  )
211
283
  if args.local:
284
+ from .local import LocalConverter
285
+
212
286
  try:
213
287
  site_properties = ConfluenceSiteProperties(
214
288
  domain=args.domain,
@@ -224,6 +298,11 @@ def main() -> None:
224
298
  )
225
299
  LocalConverter(options, site_metadata).process(args.mdpath)
226
300
  else:
301
+ from requests import HTTPError, JSONDecodeError
302
+
303
+ from .api import ConfluenceAPI
304
+ from .application import Application
305
+
227
306
  try:
228
307
  properties = ConfluenceConnectionProperties(
229
308
  api_url=args.api_url,
@@ -242,14 +321,14 @@ def main() -> None:
242
321
  api,
243
322
  options,
244
323
  ).process(args.mdpath)
245
- except requests.exceptions.HTTPError as err:
324
+ except HTTPError as err:
246
325
  logging.error(err)
247
326
 
248
327
  # print details for a response with JSON body
249
328
  if err.response is not None:
250
329
  try:
251
330
  logging.error(err.response.json())
252
- except requests.exceptions.JSONDecodeError:
331
+ except JSONDecodeError:
253
332
  pass
254
333
 
255
334
  sys.exit(1)