markdown-to-confluence 0.1.13__py3-none-any.whl → 0.2.1__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.
- {markdown_to_confluence-0.1.13.dist-info → markdown_to_confluence-0.2.1.dist-info}/LICENSE +21 -21
- {markdown_to_confluence-0.1.13.dist-info → markdown_to_confluence-0.2.1.dist-info}/METADATA +226 -168
- markdown_to_confluence-0.2.1.dist-info/RECORD +17 -0
- {markdown_to_confluence-0.1.13.dist-info → markdown_to_confluence-0.2.1.dist-info}/WHEEL +1 -1
- {markdown_to_confluence-0.1.13.dist-info → markdown_to_confluence-0.2.1.dist-info}/zip-safe +1 -1
- md2conf/__init__.py +13 -13
- md2conf/__main__.py +211 -139
- md2conf/api.py +517 -459
- md2conf/application.py +231 -154
- md2conf/converter.py +965 -626
- md2conf/entities.dtd +537 -537
- md2conf/mermaid.py +54 -0
- md2conf/processor.py +113 -91
- md2conf/properties.py +53 -52
- markdown_to_confluence-0.1.13.dist-info/RECORD +0 -16
- {markdown_to_confluence-0.1.13.dist-info → markdown_to_confluence-0.2.1.dist-info}/entry_points.txt +0 -0
- {markdown_to_confluence-0.1.13.dist-info → markdown_to_confluence-0.2.1.dist-info}/top_level.txt +0 -0
md2conf/__main__.py
CHANGED
|
@@ -1,139 +1,211 @@
|
|
|
1
|
-
import argparse
|
|
2
|
-
import logging
|
|
3
|
-
import os.path
|
|
4
|
-
import sys
|
|
5
|
-
|
|
6
|
-
from
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
from .
|
|
12
|
-
from .
|
|
13
|
-
from .
|
|
14
|
-
from .
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
1
|
+
import argparse
|
|
2
|
+
import logging
|
|
3
|
+
import os.path
|
|
4
|
+
import sys
|
|
5
|
+
import typing
|
|
6
|
+
from pathlib import Path
|
|
7
|
+
from typing import Any, Literal, Optional, Sequence, Union
|
|
8
|
+
|
|
9
|
+
import requests
|
|
10
|
+
|
|
11
|
+
from . import __version__
|
|
12
|
+
from .api import ConfluenceAPI
|
|
13
|
+
from .application import Application
|
|
14
|
+
from .converter import ConfluenceDocumentOptions
|
|
15
|
+
from .processor import Processor
|
|
16
|
+
from .properties import ConfluenceProperties
|
|
17
|
+
|
|
18
|
+
|
|
19
|
+
class Arguments(argparse.Namespace):
|
|
20
|
+
mdpath: Path
|
|
21
|
+
domain: str
|
|
22
|
+
path: str
|
|
23
|
+
username: str
|
|
24
|
+
apikey: str
|
|
25
|
+
space: str
|
|
26
|
+
loglevel: str
|
|
27
|
+
ignore_invalid_url: bool
|
|
28
|
+
heading_anchors: bool
|
|
29
|
+
root_page: Optional[str]
|
|
30
|
+
generated_by: Optional[str]
|
|
31
|
+
render_mermaid: bool
|
|
32
|
+
diagram_output_format: Literal["png", "svg"]
|
|
33
|
+
webui_links: bool
|
|
34
|
+
|
|
35
|
+
|
|
36
|
+
class KwargsAppendAction(argparse.Action):
|
|
37
|
+
"""Append key-value pairs to a dictionary"""
|
|
38
|
+
|
|
39
|
+
def __call__(
|
|
40
|
+
self,
|
|
41
|
+
parser: argparse.ArgumentParser,
|
|
42
|
+
namespace: argparse.Namespace,
|
|
43
|
+
values: Union[None, str, Sequence[Any]],
|
|
44
|
+
option_string: Optional[str] = None,
|
|
45
|
+
) -> None:
|
|
46
|
+
try:
|
|
47
|
+
d = dict(map(lambda x: x.split("="), typing.cast(Sequence[str], values)))
|
|
48
|
+
except ValueError:
|
|
49
|
+
raise argparse.ArgumentError(
|
|
50
|
+
self,
|
|
51
|
+
f'Could not parse argument "{values}". It should follow the format: k1=v1 k2=v2 ...',
|
|
52
|
+
)
|
|
53
|
+
setattr(namespace, self.dest, d)
|
|
54
|
+
|
|
55
|
+
|
|
56
|
+
def main() -> None:
|
|
57
|
+
parser = argparse.ArgumentParser()
|
|
58
|
+
parser.prog = os.path.basename(os.path.dirname(__file__))
|
|
59
|
+
parser.add_argument("--version", action="version", version=__version__)
|
|
60
|
+
parser.add_argument(
|
|
61
|
+
"mdpath", help="Path to Markdown file or directory to convert and publish."
|
|
62
|
+
)
|
|
63
|
+
parser.add_argument("-d", "--domain", help="Confluence organization domain.")
|
|
64
|
+
parser.add_argument(
|
|
65
|
+
"-p", "--path", help="Base path for Confluence (default: '/wiki/')."
|
|
66
|
+
)
|
|
67
|
+
parser.add_argument("-u", "--username", help="Confluence user name.")
|
|
68
|
+
parser.add_argument(
|
|
69
|
+
"-a",
|
|
70
|
+
"--apikey",
|
|
71
|
+
help="Confluence API key. Refer to documentation how to obtain one.",
|
|
72
|
+
)
|
|
73
|
+
parser.add_argument(
|
|
74
|
+
"-s",
|
|
75
|
+
"--space",
|
|
76
|
+
help="Confluence space key for pages to be published. If omitted, will default to user space.",
|
|
77
|
+
)
|
|
78
|
+
parser.add_argument(
|
|
79
|
+
"-l",
|
|
80
|
+
"--loglevel",
|
|
81
|
+
choices=[
|
|
82
|
+
logging.getLevelName(level).lower()
|
|
83
|
+
for level in (
|
|
84
|
+
logging.DEBUG,
|
|
85
|
+
logging.INFO,
|
|
86
|
+
logging.WARN,
|
|
87
|
+
logging.ERROR,
|
|
88
|
+
logging.CRITICAL,
|
|
89
|
+
)
|
|
90
|
+
],
|
|
91
|
+
default=logging.getLevelName(logging.INFO),
|
|
92
|
+
help="Use this option to set the log verbosity.",
|
|
93
|
+
)
|
|
94
|
+
parser.add_argument(
|
|
95
|
+
"-r",
|
|
96
|
+
dest="root_page",
|
|
97
|
+
help="Root Confluence page to create new pages. If omitted, will raise exception when creating new pages.",
|
|
98
|
+
)
|
|
99
|
+
parser.add_argument(
|
|
100
|
+
"--generated-by",
|
|
101
|
+
default="This page has been generated with a tool.",
|
|
102
|
+
help="Add prompt to pages (default: 'This page has been generated with a tool.').",
|
|
103
|
+
)
|
|
104
|
+
parser.add_argument(
|
|
105
|
+
"--no-generated-by",
|
|
106
|
+
dest="generated_by",
|
|
107
|
+
action="store_const",
|
|
108
|
+
const=None,
|
|
109
|
+
help="Do not add 'generated by a tool' prompt to pages.",
|
|
110
|
+
)
|
|
111
|
+
parser.add_argument(
|
|
112
|
+
"--render-mermaid",
|
|
113
|
+
dest="render_mermaid",
|
|
114
|
+
action="store_true",
|
|
115
|
+
default=True,
|
|
116
|
+
help="Render Mermaid diagrams as image files and add as attachments.",
|
|
117
|
+
)
|
|
118
|
+
parser.add_argument(
|
|
119
|
+
"--no-render-mermaid",
|
|
120
|
+
dest="render_mermaid",
|
|
121
|
+
action="store_false",
|
|
122
|
+
help="Inline Mermaid diagram in Confluence page.",
|
|
123
|
+
)
|
|
124
|
+
parser.add_argument(
|
|
125
|
+
"--render-mermaid-format",
|
|
126
|
+
dest="diagram_output_format",
|
|
127
|
+
choices=["png", "svg"],
|
|
128
|
+
default="png",
|
|
129
|
+
help="Format for rendering Mermaid diagrams (default: 'png').",
|
|
130
|
+
)
|
|
131
|
+
parser.add_argument(
|
|
132
|
+
"--heading-anchors",
|
|
133
|
+
action="store_true",
|
|
134
|
+
default=False,
|
|
135
|
+
help="Place an anchor at each section heading with GitHub-style same-page identifiers.",
|
|
136
|
+
)
|
|
137
|
+
parser.add_argument(
|
|
138
|
+
"--ignore-invalid-url",
|
|
139
|
+
action="store_true",
|
|
140
|
+
default=False,
|
|
141
|
+
help="Emit a warning but otherwise ignore relative URLs that point to ill-specified locations.",
|
|
142
|
+
)
|
|
143
|
+
parser.add_argument(
|
|
144
|
+
"--local",
|
|
145
|
+
action="store_true",
|
|
146
|
+
default=False,
|
|
147
|
+
help="Write XHTML-based Confluence Storage Format files locally without invoking Confluence API.",
|
|
148
|
+
)
|
|
149
|
+
parser.add_argument(
|
|
150
|
+
"--headers",
|
|
151
|
+
nargs="*",
|
|
152
|
+
required=False,
|
|
153
|
+
action=KwargsAppendAction,
|
|
154
|
+
metavar="KEY=VALUE",
|
|
155
|
+
help="Apply custom headers to all Confluence API requests.",
|
|
156
|
+
)
|
|
157
|
+
parser.add_argument(
|
|
158
|
+
"--webui-links",
|
|
159
|
+
action="store_true",
|
|
160
|
+
default=False,
|
|
161
|
+
help="Enable Confluence Web UI links.",
|
|
162
|
+
)
|
|
163
|
+
|
|
164
|
+
args = Arguments()
|
|
165
|
+
parser.parse_args(namespace=args)
|
|
166
|
+
|
|
167
|
+
# NOTE: If we switch to modern type aware CLI tool like typer
|
|
168
|
+
# the following line won't be necessary
|
|
169
|
+
args.mdpath = Path(args.mdpath)
|
|
170
|
+
|
|
171
|
+
logging.basicConfig(
|
|
172
|
+
level=getattr(logging, args.loglevel.upper(), logging.INFO),
|
|
173
|
+
format="%(asctime)s - %(levelname)s - %(funcName)s [%(lineno)d] - %(message)s",
|
|
174
|
+
)
|
|
175
|
+
|
|
176
|
+
options = ConfluenceDocumentOptions(
|
|
177
|
+
heading_anchors=args.heading_anchors,
|
|
178
|
+
ignore_invalid_url=args.ignore_invalid_url,
|
|
179
|
+
generated_by=args.generated_by,
|
|
180
|
+
root_page_id=args.root_page,
|
|
181
|
+
render_mermaid=args.render_mermaid,
|
|
182
|
+
diagram_output_format=args.diagram_output_format,
|
|
183
|
+
webui_links=args.webui_links,
|
|
184
|
+
)
|
|
185
|
+
properties = ConfluenceProperties(
|
|
186
|
+
args.domain, args.path, args.username, args.apikey, args.space, args.headers
|
|
187
|
+
)
|
|
188
|
+
if args.local:
|
|
189
|
+
Processor(options, properties).process(args.mdpath)
|
|
190
|
+
else:
|
|
191
|
+
try:
|
|
192
|
+
with ConfluenceAPI(properties) as api:
|
|
193
|
+
Application(
|
|
194
|
+
api,
|
|
195
|
+
options,
|
|
196
|
+
).synchronize(args.mdpath)
|
|
197
|
+
except requests.exceptions.HTTPError as err:
|
|
198
|
+
logging.error(err)
|
|
199
|
+
|
|
200
|
+
# print details for a response with JSON body
|
|
201
|
+
if err.response is not None:
|
|
202
|
+
try:
|
|
203
|
+
logging.error(err.response.json())
|
|
204
|
+
except requests.exceptions.JSONDecodeError:
|
|
205
|
+
pass
|
|
206
|
+
|
|
207
|
+
sys.exit(1)
|
|
208
|
+
|
|
209
|
+
|
|
210
|
+
if __name__ == "__main__":
|
|
211
|
+
main()
|