Git Product home page Git Product logo

md2cf's People

Contributors

bass-03 avatar bhrutledge avatar bjorns avatar dependabot[bot] avatar gabriel-t-harris avatar iamjackg avatar jannismain avatar jimstein3d avatar jmonsma avatar rtkjbillo avatar ssaraswati avatar sykmschmieder avatar timothybonci avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar

md2cf's Issues

Feature Request: Add labels to published pages

Extend yaml frontmatter parsing to read labels key:

---
title: Page Title
labels: [label1, label2]
---
...

Apply those labels to confluence page after creation. When updating pages, I would probably delete existing labels from the page and apply those listed in yaml frontmatter, to ensure they are in sync with the labels listed in the markdown file.

Upgrading Mistune

The version of Mistune currently used by md2cf is very old.

An example of a problem this causes, is that the version in use creates invalid XHTML if it encounters a double quote inside some image alt text.

I've been working on an upgrade, there various changes to make, feedback welcome, this is my first contribution here :)

#81

Title Checking is Case Sensitive but Confluence Page Uniqueness is Insensitive

Got a good error when running this tool on my repo for "identical titles".

These are the documents (and path, if available) with identical titles:

BUT....

If the title and the path are different based on case, it makes it through the smoke test, and fails in upload.

Screenshot 2023-08-14 104355

b'{"statusCode":400,"data":{"authorized":true,"valid":true,"errors":[],"successful":true},"message":"com.atlassian.confluence.api.service.exceptions.BadRequestException: A page with this title already
exists: A page already exists with the same TITLE in this space"}'   

Feature Request: Page Footers

The --preface-file is great, it's basically a header that gets set at the top of every file.

Can we also have a --postfix-file? Basically the same thing, but appending it as a footer to the page instead.

mermaid diagrams support

mermaid is a standard github way to embed diagrams

it would be amazing if e.g. confluence page were to render diagrams properly

I can see 2 options:

  1. using confluence mermaid plugin, but somehow making the page to pick it up
  2. rendering svg/png serverside and embedding them in a confluence page

do you have a particular view, in case someone would want to sponsor the work?

Images displayed with a width of less than 400px

Hi,

we have a markdown that contains the following piece of code:

![title](my-image.drawio.svg)

It gets correctly uploaded, but it's displayed with a very narrow width (less than 400px). Same happens if we use a png file (size is almost 900px).

Is there a way to instruct Confluence to use a greater width?

We are using version 2.3.0 of md2cf.

Cloud `spaces` vs Server `space`

I did some hilarious scripting to clean up my markdown dump to make it acceptable to the tool, and now instead I'm getting

requests.exceptions.HTTPError: 404 Client Error: Not Found for url: https://<mycompany>.atlassian.net/wiki/space/TEST?expand=homepage

When I go to actually look at Confluence Cloud's URL for the TEST space it's https://<mycompany>.atlassian.net/wiki/spaces/TEST (note the plural spaces).

Have I missed something or found a bug? :)

Feature request: using _index.md instead of empty pages

Hello,

I am using HUGO for generating a documentation site for markdown files. We are making extensive use of _index.md files for the 'index-page' of a directory.

Would it be possible to use these _index.md pages instead of creating an empty file for each directory? This would make it easier to fill in empty spaces and still get the requested directory structure. Thanks!

Example directory structure:

docs
Development
_index.md
docker.md

On confluence I would like to have a Development page with the content of _index.md and a Docker page for docker.md. Currently when I upload this structure it's complaining about double pages because I have the Title for _index.md set to Development

Error when markdown missing top level header

The markdown requires a top level header # Header to create the page title. This should not be required to be considered valid markdown.

This line assumes h1 exists.
page_title = soup.h1.text or file_name

Here is the resulting error.

Traceback (most recent call last):
  File "/home/charles/.local/bin/md2cf", line 11, in <module>
    sys.exit(main())
  File "/home/charles/.local/lib/python3.6/site-packages/md2cf/__main__.py", line 91, in main
    page_data = page_data_from_file_name(file_name)
  File "/home/charles/.local/lib/python3.6/site-packages/md2cf/__main__.py", line 49, in page_data_from_file_name
    page_title = soup.h1.text or file_name
AttributeError: 'NoneType' object has no attribute 'text'

UnicodeDecodeError: 'gbk' codec can't decode byte

I have a document encoded by 'utf-8' but the program try to decode by 'gbk'

Traceback (most recent call last):
  File "C:\Users\vicat\AppData\Local\Programs\Python\Python38\lib\runpy.py", line 194, in _run_module_as_main
    return _run_code(code, main_globals, None,
  File "C:\Users\vicat\AppData\Local\Programs\Python\Python38\lib\runpy.py", line 87, in _run_code
    exec(code, run_globals)
  File "C:\Users\vicat\AppData\Local\Programs\Python\Python38\Scripts\md2cf.exe\__main__.py", line 7, in <module>
  File "C:\Users\vicat\AppData\Local\Programs\Python\Python38\lib\site-packages\md2cf\__main__.py", line 350, in main
    md2cf.document.get_page_data_from_file_path(file_name)
  File "C:\Users\vicat\AppData\Local\Programs\Python\Python38\lib\site-packages\md2cf\document.py", line 140, in get_page_data_from_file_path
    markdown_lines = file_handle.readlines()
UnicodeDecodeError: 'gbk' codec can't decode byte 0xaf in position 2: illegal multibyte sequence

what should i do?

Question/Feature Request: Inject custom Confluence Macros within Markdown

I have a Confluence environment with a handful of custom macros that are really useful - custom formatting, integrations, etc.

I'd like to reference these within the Markdown that I'm passing to md2cf. By default it just treats it as HTML and escapes it (to be expected).

Is there a way currently (or if not, feature request!) to be able to provide a macro's XML within my Markdown files and have md2cf leave it as-is when it generates the payload it sends over to Confluence?

Remove top-level header when used as page title

From my initial testing, it looks like a when the page title is set using "the first top-level header found in the document (i.e. the first # header)", the title appears twice in Confluence: once at the top of the page, and again in the body. In that scenario, could it be removed during the rendering process?

Maybe here?

def header(self, text, level, raw=None):
if self.title is None and level == 1:
self.title = text
return super(ConfluenceRenderer, self).header(text, level, raw=raw)

folder upload breaks when --no-gitignore is omitted

I have a MkDocs project with documentation available, for this I have a .gitignore file for python.

However when I target a folder the action fails when I omit the --no-gitignore flag.

Run md2cf --host 'https://petermefrandsen.atlassian.net/wiki/rest/api' --username *** -*** --space 'md2cf' ./docs
GitHub action job

✔️ Run md2cf --host 'https://petermefrandsen.atlassian.net/wiki/rest/api' --username *** -*** --space 'md2cf' --no-gitignore ./docs
GitHub action job

Run md2cf --host 'https://petermefrandsen.atlassian.net/wiki/rest/api' --username *** -*** --space 'md[2](https://github.com/petermefrandsen/mkdocs-deployment/actions/runs/4043527881/jobs/6952549020#step:5:2)cf' ./docs
  md2cf --host 'https://petermefrandsen.atlassian.net/wiki/rest/api' --username *** -*** --space 'md2cf' ./docs
  shell: /usr/bin/bash -e {0}
  env:
    pythonLocation: /opt/hostedtoolcache/Python/[3](https://github.com/petermefrandsen/mkdocs-deployment/actions/runs/4043527881/jobs/6952549020#step:5:3).11.1/x6[4](https://github.com/petermefrandsen/mkdocs-deployment/actions/runs/4043527881/jobs/6952549020#step:5:4)
    LD_LIBRARY_PATH: /opt/hostedtoolcache/Python/3.11.1/x64/lib
Traceback (most recent call last):
  File "/opt/hostedtoolcache/Python/3.11.1/x64/bin/md2cf", line 8, in <module>
    sys.exit(main())
             ^^^^^^
  File "/opt/hostedtoolcache/Python/3.11.1/x64/lib/python3.11/site-packages/md2cf/__main__.py", line 403, in main
    pages_to_upload = collect_pages_to_upload(args)
                      ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/opt/hostedtoolcache/Python/3.11.1/x64/lib/python3.11/site-packages/md2cf/__main__.py", line [5](https://github.com/petermefrandsen/mkdocs-deployment/actions/runs/4043527881/jobs/6952549020#step:5:5)15, in collect_pages_to_upload
    pages_to_upload += md2cf.document.get_pages_from_directory(
                       ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/opt/hostedtoolcache/Python/3.11.1/x[6](https://github.com/petermefrandsen/mkdocs-deployment/actions/runs/4043527881/jobs/6952549020#step:5:6)4/lib/python3.11/site-packages/md2cf/document.py", line 109, in get_pages_from_directory
    markdown_files = [
                     ^
  File "/opt/hostedtoolcache/Python/3.11.1/x64/lib/python3.11/site-packages/md2cf/document.py", line 110, in <listcomp>
    path for path in markdown_files if not git_repo.is_ignored(path)
                                           ^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/opt/hostedtoolcache/Python/3.11.1/x64/lib/python3.11/site-packages/md2cf/ignored_files.py", line 85, in is_ignored
    return any([m(filepath) for m in matchers])
               ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/opt/hostedtoolcache/Python/3.11.1/x64/lib/python3.11/site-packages/md2cf/ignored_files.py", line 85, in <listcomp>
    return any([m(filepath) for m in matchers])
                ^^^^^^^^^^^
  File "/opt/hostedtoolcache/Python/3.11.1/x64/lib/python3.11/site-packages/gitignore_parser.py", line 32, in <lambda>
    return lambda file_path: any(r.match(file_path) for r in rules)
                             ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/opt/hostedtoolcache/Python/3.11.1/x64/lib/python3.11/site-packages/gitignore_parser.py", line 32, in <genexpr>
    return lambda file_path: any(r.match(file_path) for r in rules)
                                 ^^^^^^^^^^^^^^^^^^
  File "/opt/hostedtoolcache/Python/3.11.1/x64/lib/python3.11/site-packages/gitignore_parser.py", line 143, in match
    if re.search(self.regex, rel_path):
       ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/opt/hostedtoolcache/Python/3.11.1/x64/lib/python3.11/re/__init__.py", line 1[7](https://github.com/petermefrandsen/mkdocs-deployment/actions/runs/4043527881/jobs/6952549020#step:5:8)6, in search
    return _compile(pattern, flags).search(string)
           ^^^^^^^^^^^^^^^^^^^^^^^^
  File "/opt/hostedtoolcache/Python/3.11.1/x64/lib/python3.11/re/__init__.py", line 294, in _compile
    p = _compiler.compile(pattern, flags)
        ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/opt/hostedtoolcache/Python/3.11.1/x64/lib/python3.11/re/_compiler.py", line 743, in compile
    p = _parser.parse(p, flags)
        ^^^^^^^^^^^^^^^^^^^^^^^
  File "/opt/hostedtoolcache/Python/3.11.1/x64/lib/python3.11/re/_parser.py", line 9[8](https://github.com/petermefrandsen/mkdocs-deployment/actions/runs/4043527881/jobs/6952549020#step:5:9)0, in parse
    p = _parse_sub(source, state, flags & SRE_FLAG_VERBOSE, 0)
        ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/opt/hostedtoolcache/Python/3.[11](https://github.com/petermefrandsen/mkdocs-deployment/actions/runs/4043527881/jobs/6952549020#step:5:12).1/x64/lib/python3.11/re/_parser.py", line 455, in _parse_sub
    itemsappend(_parse(source, state, verbose, nested + 1,
                ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/opt/hostedtoolcache/Python/3.11.1/x64/lib/python3.11/re/_parser.py", line 8[41](https://github.com/petermefrandsen/mkdocs-deployment/actions/runs/4043527881/jobs/6952549020#step:5:42), in _parse
    raise source.error('global flags not at the start '
re.error: global flags not at the start of the expression at position 1
Error: Process completed with exit code 1.

Error parsing xhtml: Unexpected character \'>\' (code 62) expected \'=\'\\n

I have a markdown file and I convert it into a mediawiki format using pypandoc and then use md2cf to upload to confluence. However, I'm facing the issue ( pasted in subject ) everytime i try to run the code.

Can you help me in pointing out what could be happening?

Unfortunately, the file that i'm trying to convert is a proprietary document and i can't share it here.

Link parsing fails for document references

Hi,

I just noticed that it's not possible to use md2cf anymore with document references like table of contents, since the relative path detector detects the paths that start with #

[Frequently Asked Questions](#frequently-asked-questions-osep)

Having something like this in the document results in

Page test.md has a relative link to , which is not in the list of pages to be uploaded.  

URL not found

Hello.

I'm trying to publish a test page to the Confluence using the tool. The command looks like this, plus username and password of course.

md2cf -o https://jayconfluence.atlassian.net -s MARKDOWN test.md

As a result I'm getting the 404 page, mentioning that the following URL is not found:
https://jayconfluence.atlassian.net/content?title=Markdown+Syntax&spaceKey=MARKDOWN

Indeed, if I go there - it is not found. The URL to the existing page looks like this:
https://jayconfluence.atlassian.net/wiki/spaces/MARKDOWN/pages/262145/Markdown+Syntax

I get the same trying to create non-existent page. Interestingly, I also get the same response if I provide wrong password, for example.

Did the URL change or am I doing something wrong?

Thanks in advance!

KeyError: PosixPath('.') when uploading files

I get the following error when I try to upload to confluence:

$ md2cf --dry-run  --space xxx --password ${CONFLUENCE_PASSWORD} --username ${CONFLUENCE_USERNAME} --host ${CONFLUENCE_HOST} dist
Traceback (most recent call last):
  File "/home/xxx/.pyenv/versions/3.9.9/bin/md2cf", line 8, in <module>
    sys.exit(main())
  File "/home/xxx/.pyenv/versions/3.9.9/lib/python3.9/site-packages/md2cf/__main__.py", line 235, in main
    pages_to_upload += md2cf.document.get_pages_from_directory(
  File "/home/xxx/.pyenv/versions/3.9.9/lib/python3.9/site-packages/md2cf/document.py", line 85, in get_pages_from_directory
    folder_parent_title = folder_data[folder_parent_path]["title"]
KeyError: PosixPath('.')

adding "try-except" on line 85-116 will have me continue with this but is most likely not the solution.


            try:
                folder_parent_title = folder_data[folder_parent_path]["title"]

                if len(markdown_files) == 1 and collapse_single_pages:
                    parent_page_title = folder_parent_title
                else:
                    if collapse_empty:
                        folder_data[current_path]["title"] = current_path.relative_to(
                            folder_parent_path
                        )
                    if beautify_folders:
                        folder_data[current_path]["title"] = (
                            folder_data[current_path]["title"]
                            .replace("-", " ")
                            .replace("_", " ")
                            .capitalize()
                        )
                    elif use_pages_file and ".pages" in file_names:
                        with open(current_path.joinpath(".pages")) as pages_fp:
                            pages_file_contents = yaml.safe_load(pages_fp)
                        if "title" in pages_file_contents:
                            folder_data[current_path]["title"] = pages_file_contents[
                                "title"
                            ]
                    parent_page_title = folder_data[current_path]["title"]
                    processed_pages.append(
                        Page(
                            title=parent_page_title,
                            parent_title=folder_parent_title,
                            body="",
                        )
                    )
            except KeyError:
                print(f"invalld path, current_path: {current_path}")
                print(f"invalld path, type(current_path): {type(current_path)}")
                pass

output is:

invalld path, current_path: dist
invalld path, type(current_path): <class 'pathlib.PosixPath'>

Relative links break with a hash (#) to sub-section

Markdown reference to section from another (slask.md) file will not work after import to Confluence.
slask.md contains:

#  Main section

##  [sub-section](./child.md#sub-section)    
##  [sub-section](/child.md#sub-section)
##  [sub-section](child.md#sub-section)

This will result in broken links i confluence.

(/child.md#sub-section) results in a link like this https:///<my-secret>.atlassian.net/child.md#sub-section

Edit: out problem seem to be that urlparse.urlparse(link) marks sub-section as a fragment and then dont create a replacement_link.

.pages file at top (root) level

I'd like to be able to specify a pages file at the top level. This is useful in the following scenario:

I have two folders A and B, both containing markdown files, both containing links to each other.

I wish to process A and B into folders ie there should be a parent page for A, and a parent page for B.

Because there are relative links between A and B, A and B must be processed in a single run. The --parent-title option doesn't help because the files from A and from B would be mixed underneath a single parent page.

If A and B are specified on the command line, the .pages files are ignored, because A and B are currently processed as separate "root level" directories.

I cannot specify the real root of A and B, because that would pull in additional directories and markdown files that aren't desired.

I have a proposed fix for this issue which I'll reference shortly...

Associate Markdown files and page ID's

Related to #20.

To make it easier to update pages and change titles, I think it'd be helpful to save the relationship of a Markdown file and its page ID (preferably the whole URL) after creating it. I've seen this done with a separate config file, front matter, and comments. My preference would be to append a comment to the end of the file, e.g.:

# Page title

Page content

<!-- https://ORG.atlassian.net/wiki/spaces/~SPACE_ID/pages/PAGE_ID/Page+title -->

This could then be parsed for the host, space, and page ID.

PyYaml 6.0 failing with cython 3 update

On or about July 14, cython 3 was released with breaking changes. pyyaml 6.0 didn't pin to 2.x, so it's now failing to install. This project pins to 6.0, so it's failing to install.

pyyaml 6.0.1 pins to cython <3, so it should work fine, but it hasn't been merged into master yet. I can open a PR to use pyyaml 6.0.1, but I'm not sure how you feel about using "unreleased" versions of dependencies.

Feature Addition: "Autogenerated" Label Validation

Hey there,

In the process of using md2cf, I've implemented an additional feature that could be beneficial to the wider community.

Below is a summary of the new feature:

Autogenerated Label Validation: I've introduced a new flag --check-autogenerated-label which, when activated, checks for a 'marker' label on the Confluence pages.
This marker indicates if the page was autogenerated.
The name of the marker label can be provided via --autogenerated-flag-label.
If a page that's going to be updated doesn't have this label, the script will prompt the user and ask whether they wish to proceed with the update.

This feature aims to prevent accidental overwriting of manually created pages when mass updating pages via the script. I've developed an initial implementation to satisfy my immediate requirements, but I'm willing to develop a cleaner, more robust version if this feature is deemed useful for the project.

I would be delighted to hear your thoughts on this proposed enhancement.

Feature request: only change when the content changes when uploading (folders)

Hi there,

Background

I recently scripted a mirror from the documentation of one of our projects to our confluence server. Every time something gets merged onto the default branch md2cf gets called and the documentation in confluence gets updated.

Problem

However, it seems that every page gets touched, even if the content didn't change.
With the email subscriptions of these pages my colleagues now get unnecessary notifications about seemingly changed content.

Possible solution

If the generated content would be compared with the content that is currently live on the server, and only uploaded if these two differ, the problem would be solved.

I'll try to implement it. Of course a hint where to start would be helpful 😉

Edit: only had time for the prototype below

1.3.0 available on Pypi but not here

Thank's a lot for your returns about #22.
I've tested your changes and it work fine !! :)
Don't know if it's a bug from me but I can't see your changes here in this repo (master).

index.md as parent page

Is there any way to make the index.md page the parent page for a directory when uploading a directory?

I've tried setting the title of the index.md file to be the same as the parent directory but it errors out because another page already exists with that name.

❌ Some documents have the same title, but all Confluence pages in the same space must have different titles.                                                                                                                           
                                                                                                                                                                                                                                        
These are the documents (and path, if available) with identical titles:                                                                                                                                                                 
                                                                                                                                                                                                                                        
  Title          File                                                                                                                                                                                                                   
 ───────────────────────────────────────────────────────────────────────────────────                                                                                                                                                    
  Architecture   None                                                                                                                                                                                                                   
  Architecture   /Users/thed1360/Projects/new_project/docs/Architecture/index.md   

`<kbd>` does not render in Confluence

Hi there,

I'm sorry to bother you again with a feature request but I really like md2cf and want to make the most of it without using some kind of static site generator.

Today I saw that the <kbd></kbd> syntax renders as regular text in Confluence. This is probably intended behavior, as not every Confluence instance can interpret raw HTML (or at least the one I am working with doesn't).

However, this is the recommended way to present keyboard shortcuts in Markdown, supported by both GFM and GLFM.

Example

 <kbd>ctrl</kbd>+<kbd>alt</kbd>+<kbd>p</kbd> / <kbd>⌘</kbd> <kbd>⌥</kbd> <kbd>p</kbd>

is currently rendered like this:

image

when something like this would be nicer:
CleanShot 2022-09-07 at 13 24 40

Workarounds I have tried

I also tried wrapping the shortcuts in backticks inside the <kbd>'ctrl'</kbd> (using real backticks of course), but confluence didn't display them using the fixed-width font (probably due to the missing spaces around the backticks):
image

Adding spaces around the backticks would modify the markdown output again, so this is where I stopped and opened this issue.

Do you have an idea on how to work around this? Or would it be feasible to support this use-case directly?

updating attachments not allowed

Currently the code for uploading attachments calls the Create Attachment method (https://developer.atlassian.com/cloud/confluence/rest/api-group-content---attachments/#api-wiki-rest-api-content-id-child-attachment-post), which does not allow updates, only the initial upload.

If instead we used the Create or Update Attachment method (https://developer.atlassian.com/cloud/confluence/rest/api-group-content---attachments/#api-wiki-rest-api-content-id-child-attachment-put), this should allow the same call to upsert the attachments instead.

authentication credentials are validated even with --dry-run in md2cf v2.1.0

with this command line "md2cf --host 'https://<my secret>.atlassian.net/wiki/rest/api' --username '<my secret>' --password '<my secret>' --space <my secret> --dry-run --no-gitignore 'C:\my-project\docs'" and with
"pip install md2cf==2.0.2" it will work.
But if I then do this "pip install md2cf==2.1.0" and run the command line again I now get this error:

Traceback (most recent call last):
File "", line 198, in run_module_as_main
File "", line 88, in run_code
File "C:\Python311\Scripts\md2cf.exe_main
.py", line 7, in
File "C:\Python311\Lib\site-packages\md2cf_main
.py", line 384, in main
space_info = confluence.get_space(
^^^^^^^^^^^^^^^^^^^^^
File "C:\Python311\Lib\site-packages\md2cf\api.py", line 259, in get_space
return self._get(f"content?spaceKey={space}/")
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
File "C:\Python311\Lib\site-packages\md2cf\api.py", line 70, in _get
return self._request("GET", path, **kwargs)
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
File "C:\Python311\Lib\site-packages\md2cf\api.py", line 66, in _request
r.raise_for_status()
File "C:\Python311\Lib\site-packages\requests\models.py", line 1021, in raise_for_status
raise HTTPError(http_error_msg, response=self)
requests.exceptions.HTTPError: 401 Client Error: Unauthorized for url: https://<my secret>.atlassian.net/wiki/rest/api/space/<my secret>/?expand=homepage

NOTE: this url (https://.atlassian.net/wiki/rest/api/space/<my secret>/?expand=homepage) in the webbrowser print json content.

EDIT: the difference was that --dry-run is not respected in the new version. I still have problems with authentication credentials with the old version with --dry-run removed from command line.

Uploading attachments broken in 1.0.4

I get the following error when I try to create a page using md2cf v1.0.4:

admrfinch:devops:tmp$md2cf -s '~rfinch' DEMO.md
Creating new page: Bug Demo
Uploading attachments for page: Bug Demo
Uploading file: charizard-pokemon.png
405 Client Error:  for url: https://confluence.idicore.com/rest/api/content/358259237/child/attachment?allowDuplicated=true - b''

after downgrading to 1.0.3:

$ md2cf -s '~rfinch' DEMO.md
Updating page: Bug Demo
Uploading attachments for page: Bug Demo
Uploading file: charizard-pokemon.png
$ cat DEMO.md
# Bug Demo

![](charizard-pokemon.png)

Skip Hidden Folders

When uploading whole directories, like whole repos, markdown files found in hidden folders are also published.

This causes the common practice of PR Templates in the .github or the MR Templates under .gitlab to be published along with the other repo items. This is not desired when you have your markdown files scattered across your repo.

Request/feature: clean slate / delete subpages of parent before publish

If you change the header of a markdown file, I currently end up with two pages in confluence; one old dead page, and the new page maintained by the code.

I might have overlooked some of the many flags, but it would be great if I could set a flag that cleans the targeted parent's subpages, renames the existing confluence page (dif compare) or at least deletes those pages that are not represented in the code.

"With great power comes great responsibility" - I know, but I really need to have the code as my documentation source, and not have doubt about the content in confluence. So this would be a neat feature to me😄

If it is already there, by all means, correct me!

Mistune 2 compatibility

Hi!

I noticed the program only works with Mistune 0.8.4. If the pytest suite is run with Mistune >= 2, the following is reported:

$ pytest
==================================================================== test session starts =====================================================================
platform linux -- Python 3.10.7, pytest-7.2.0, pluggy-1.0.0
rootdir: /tmp/md2cf, configfile: pytest.ini
plugins: pyfakefs-5.0.0, mock-1.11.1
collected 20 items / 5 errors                                                                                                                                

=========================================================================== ERRORS ===========================================================================
__________________________________________________ ERROR collecting tests/functional/test_full_rendering.py __________________________________________________
tests/functional/test_full_rendering.py:3: in <module>
    from md2cf import document
/usr/local/lib/python3.10/site-packages/md2cf-1.5.1-py3.10.egg/md2cf/document.py:11: in <module>
    ???
/usr/local/lib/python3.10/site-packages/md2cf-1.5.1-py3.10.egg/md2cf/confluence_renderer.py:52: in <module>
    ???
E   AttributeError: module 'mistune' has no attribute 'Renderer'
________________________________________________________ ERROR collecting tests/unit/test_document.py ________________________________________________________
tests/unit/test_document.py:1: in <module>
    import md2cf.document as doc
/usr/local/lib/python3.10/site-packages/md2cf-1.5.1-py3.10.egg/md2cf/document.py:11: in <module>
    ???
/usr/local/lib/python3.10/site-packages/md2cf-1.5.1-py3.10.egg/md2cf/confluence_renderer.py:52: in <module>
    ???
E   AttributeError: module 'mistune' has no attribute 'Renderer'
__________________________________________________________ ERROR collecting tests/unit/test_main.py __________________________________________________________
tests/unit/test_main.py:1: in <module>
    import md2cf.__main__ as main
/usr/local/lib/python3.10/site-packages/md2cf-1.5.1-py3.10.egg/md2cf/__main__.py:15: in <module>
    ???
/usr/local/lib/python3.10/site-packages/md2cf-1.5.1-py3.10.egg/md2cf/document.py:11: in <module>
    ???
/usr/local/lib/python3.10/site-packages/md2cf-1.5.1-py3.10.egg/md2cf/confluence_renderer.py:52: in <module>
    ???
E   AttributeError: module 'mistune' has no attribute 'Renderer'
_________________________________________________________ ERROR collecting tests/unit/test_regex.py __________________________________________________________
tests/unit/test_regex.py:1: in <module>
    from md2cf.__main__ import CONTENT_HASH_REGEX
/usr/local/lib/python3.10/site-packages/md2cf-1.5.1-py3.10.egg/md2cf/__main__.py:15: in <module>
    ???
/usr/local/lib/python3.10/site-packages/md2cf-1.5.1-py3.10.egg/md2cf/document.py:11: in <module>
    ???
/usr/local/lib/python3.10/site-packages/md2cf-1.5.1-py3.10.egg/md2cf/confluence_renderer.py:52: in <module>
    ???
E   AttributeError: module 'mistune' has no attribute 'Renderer'
________________________________________________________ ERROR collecting tests/unit/test_renderer.py ________________________________________________________
tests/unit/test_renderer.py:1: in <module>
    from md2cf.confluence_renderer import ConfluenceTag, ConfluenceRenderer
/usr/local/lib/python3.10/site-packages/md2cf-1.5.1-py3.10.egg/md2cf/confluence_renderer.py:52: in <module>
    ???
E   AttributeError: module 'mistune' has no attribute 'Renderer'
================================================================== short test summary info ===================================================================
ERROR tests/functional/test_full_rendering.py - AttributeError: module 'mistune' has no attribute 'Renderer'
ERROR tests/unit/test_document.py - AttributeError: module 'mistune' has no attribute 'Renderer'
ERROR tests/unit/test_main.py - AttributeError: module 'mistune' has no attribute 'Renderer'
ERROR tests/unit/test_regex.py - AttributeError: module 'mistune' has no attribute 'Renderer'
ERROR tests/unit/test_renderer.py - AttributeError: module 'mistune' has no attribute 'Renderer'
!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! Interrupted: 5 errors during collection !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
===================================================================== 5 errors in 0.34s ======================================================================

From what I could gather by searching the internet is the Renderer attribute having been replaced, however my "hack" in simply applying the following patch

--- a/md2cf/confluence_renderer.py      2022-11-09 16:26:31.031940156 +0100
+++ b/md2cf/confluence_renderer.py      2022-11-09 16:27:48.448477461 +0100
@@ -49,7 +49,7 @@
         self.children.append(child)

-class ConfluenceRenderer(mistune.Renderer):
+class ConfluenceRenderer(mistune.HTMLRenderer):
     def __init__(self, strip_header=False, remove_text_newlines=False, **kwargs):
         super().__init__(**kwargs)
         self.strip_header = strip_header

yields various other test failures:

$ pytest
==================================================================== test session starts =====================================================================
platform linux -- Python 3.10.7, pytest-7.2.0, pluggy-1.0.0
rootdir: /tmp/md2cf, configfile: pytest.ini
plugins: pyfakefs-5.0.0, mock-1.11.1
collected 61 items                                                                                                                                           

tests/functional/test_full_rendering.py F                                                                                                              [  1%]
tests/unit/test_confluence.py .................                                                                                                        [ 29%]
tests/unit/test_document.py .FFFFFFFF.....                                                                                                             [ 52%]
tests/unit/test_ignored_files.py ...                                                                                                                   [ 57%]
tests/unit/test_main.py .                                                                                                                              [ 59%]
tests/unit/test_regex.py ...                                                                                                                           [ 63%]
tests/unit/test_renderer.py .........F..F.FFF.....                                                                                                     [100%]

========================================================================== FAILURES ==========================================================================
_____________________________________________________________________ test_full_document _____________________________________________________________________

script_loc = local('/tmp/md2cf/tests/functional')

    def test_full_document(script_loc):
        markdown_path = script_loc.join("test.md")
    
        with open(str(script_loc.join("result.xml"))) as result_file:
            result_data = result_file.read()
    
>       page = document.get_page_data_from_file_path(markdown_path)

tests/functional/test_full_rendering.py:22: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 
/usr/local/lib/python3.10/site-packages/md2cf-1.5.1-py3.10.egg/md2cf/document.py:191: in get_page_data_from_file_path
    page = get_page_data_from_lines(
/usr/local/lib/python3.10/site-packages/md2cf-1.5.1-py3.10.egg/md2cf/document.py:214: in get_page_data_from_lines
    page = parse_page(
/usr/local/lib/python3.10/site-packages/md2cf-1.5.1-py3.10.egg/md2cf/document.py:238: in parse_page
    renderer = ConfluenceRenderer(
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

self = <md2cf.confluence_renderer.ConfluenceRenderer object at 0x7f99c790ebf0>, strip_header = False, remove_text_newlines = False
kwargs = {'use_xhtml': True}

    def __init__(self, strip_header=False, remove_text_newlines=False, **kwargs):
>       super().__init__(**kwargs)
E       TypeError: HTMLRenderer.__init__() got an unexpected keyword argument 'use_xhtml'

/usr/local/lib/python3.10/site-packages/md2cf-1.5.1-py3.10.egg/md2cf/confluence_renderer.py:54: TypeError
_______________________________________________________________ test_get_pages_from_directory ________________________________________________________________

fs = <pyfakefs.fake_filesystem.FakeFilesystem object at 0x7f99c7d7b7c0>

    def test_get_pages_from_directory(fs):
        fs.create_file("/rootfolder/rootfolderfile.md")
        fs.create_dir("/rootfolder/emptydir")
        fs.create_file("/rootfolder/parent/child/childfile.md")
    
>       result = doc.get_pages_from_directory(Path("/rootfolder"))

/tmp/md2cf/tests/unit/test_document.py:18: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 
/usr/local/lib/python3.10/site-packages/md2cf-1.5.1-py3.10.egg/md2cf/document.py:165: in get_pages_from_directory
    processed_page = get_page_data_from_file_path(
/usr/local/lib/python3.10/site-packages/md2cf-1.5.1-py3.10.egg/md2cf/document.py:191: in get_page_data_from_file_path
    page = get_page_data_from_lines(
/usr/local/lib/python3.10/site-packages/md2cf-1.5.1-py3.10.egg/md2cf/document.py:214: in get_page_data_from_lines
    page = parse_page(
/usr/local/lib/python3.10/site-packages/md2cf-1.5.1-py3.10.egg/md2cf/document.py:238: in parse_page
    renderer = ConfluenceRenderer(
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

self = <md2cf.confluence_renderer.ConfluenceRenderer object at 0x7f99c6d041f0>, strip_header = False, remove_text_newlines = False
kwargs = {'use_xhtml': True}

    def __init__(self, strip_header=False, remove_text_newlines=False, **kwargs):
>       super().__init__(**kwargs)
E       TypeError: HTMLRenderer.__init__() got an unexpected keyword argument 'use_xhtml'

/usr/local/lib/python3.10/site-packages/md2cf-1.5.1-py3.10.egg/md2cf/confluence_renderer.py:54: TypeError
-------------------------------------------------------------------- Captured stdout call --------------------------------------------------------------------
No git root found, gitignore checking disabled.
____________________________________________________ test_get_pages_from_directory_collapse_single_pages _____________________________________________________

fs = <pyfakefs.fake_filesystem.FakeFilesystem object at 0x7f99c6df3340>

    def test_get_pages_from_directory_collapse_single_pages(fs):
        fs.create_file("/rootfolder/rootfolderfile.md")
        fs.create_file("/rootfolder/parent/child/childfile.md")
    
>       result = doc.get_pages_from_directory(
            Path("/rootfolder"), collapse_single_pages=True
        )

/tmp/md2cf/tests/unit/test_document.py:38: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 
/usr/local/lib/python3.10/site-packages/md2cf-1.5.1-py3.10.egg/md2cf/document.py:165: in get_pages_from_directory
    processed_page = get_page_data_from_file_path(
/usr/local/lib/python3.10/site-packages/md2cf-1.5.1-py3.10.egg/md2cf/document.py:191: in get_page_data_from_file_path
    page = get_page_data_from_lines(
/usr/local/lib/python3.10/site-packages/md2cf-1.5.1-py3.10.egg/md2cf/document.py:214: in get_page_data_from_lines
    page = parse_page(
/usr/local/lib/python3.10/site-packages/md2cf-1.5.1-py3.10.egg/md2cf/document.py:238: in parse_page
    renderer = ConfluenceRenderer(
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

self = <md2cf.confluence_renderer.ConfluenceRenderer object at 0x7f99c6df1420>, strip_header = False, remove_text_newlines = False
kwargs = {'use_xhtml': True}

    def __init__(self, strip_header=False, remove_text_newlines=False, **kwargs):
>       super().__init__(**kwargs)
E       TypeError: HTMLRenderer.__init__() got an unexpected keyword argument 'use_xhtml'

/usr/local/lib/python3.10/site-packages/md2cf-1.5.1-py3.10.egg/md2cf/confluence_renderer.py:54: TypeError
-------------------------------------------------------------------- Captured stdout call --------------------------------------------------------------------
No git root found, gitignore checking disabled.
__________________________________________ test_get_pages_from_directory_collapse_single_pages_no_non_empty_parent ___________________________________________

fs = <pyfakefs.fake_filesystem.FakeFilesystem object at 0x7f99c6d06260>

    def test_get_pages_from_directory_collapse_single_pages_no_non_empty_parent(fs):
        fs.create_file("/rootfolder/parent/child/childfile.md")
    
>       result = doc.get_pages_from_directory(
            Path("/rootfolder"), collapse_single_pages=True
        )

/tmp/md2cf/tests/unit/test_document.py:58: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 
/usr/local/lib/python3.10/site-packages/md2cf-1.5.1-py3.10.egg/md2cf/document.py:165: in get_pages_from_directory
    processed_page = get_page_data_from_file_path(
/usr/local/lib/python3.10/site-packages/md2cf-1.5.1-py3.10.egg/md2cf/document.py:191: in get_page_data_from_file_path
    page = get_page_data_from_lines(
/usr/local/lib/python3.10/site-packages/md2cf-1.5.1-py3.10.egg/md2cf/document.py:214: in get_page_data_from_lines
    page = parse_page(
/usr/local/lib/python3.10/site-packages/md2cf-1.5.1-py3.10.egg/md2cf/document.py:238: in parse_page
    renderer = ConfluenceRenderer(
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

self = <md2cf.confluence_renderer.ConfluenceRenderer object at 0x7f99c7dd89d0>, strip_header = False, remove_text_newlines = False
kwargs = {'use_xhtml': True}

    def __init__(self, strip_header=False, remove_text_newlines=False, **kwargs):
>       super().__init__(**kwargs)
E       TypeError: HTMLRenderer.__init__() got an unexpected keyword argument 'use_xhtml'

/usr/local/lib/python3.10/site-packages/md2cf-1.5.1-py3.10.egg/md2cf/confluence_renderer.py:54: TypeError
-------------------------------------------------------------------- Captured stdout call --------------------------------------------------------------------
No git root found, gitignore checking disabled.
__________________________________________________________ test_get_pages_from_directory_skip_empty __________________________________________________________

fs = <pyfakefs.fake_filesystem.FakeFilesystem object at 0x7f99c6dd2410>

    def test_get_pages_from_directory_skip_empty(fs):
        fs.create_file("/rootfolder/rootfolderfile.md")
        fs.create_file("/rootfolder/parent/child/childfile.md")
    
>       result = doc.get_pages_from_directory(Path("/rootfolder"), skip_empty=True)

/tmp/md2cf/tests/unit/test_document.py:77: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 
/usr/local/lib/python3.10/site-packages/md2cf-1.5.1-py3.10.egg/md2cf/document.py:165: in get_pages_from_directory
    processed_page = get_page_data_from_file_path(
/usr/local/lib/python3.10/site-packages/md2cf-1.5.1-py3.10.egg/md2cf/document.py:191: in get_page_data_from_file_path
    page = get_page_data_from_lines(
/usr/local/lib/python3.10/site-packages/md2cf-1.5.1-py3.10.egg/md2cf/document.py:214: in get_page_data_from_lines
    page = parse_page(
/usr/local/lib/python3.10/site-packages/md2cf-1.5.1-py3.10.egg/md2cf/document.py:238: in parse_page
    renderer = ConfluenceRenderer(
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

self = <md2cf.confluence_renderer.ConfluenceRenderer object at 0x7f99c6dd0280>, strip_header = False, remove_text_newlines = False
kwargs = {'use_xhtml': True}

    def __init__(self, strip_header=False, remove_text_newlines=False, **kwargs):
>       super().__init__(**kwargs)
E       TypeError: HTMLRenderer.__init__() got an unexpected keyword argument 'use_xhtml'

/usr/local/lib/python3.10/site-packages/md2cf-1.5.1-py3.10.egg/md2cf/confluence_renderer.py:54: TypeError
-------------------------------------------------------------------- Captured stdout call --------------------------------------------------------------------
No git root found, gitignore checking disabled.
________________________________________________ test_get_pages_from_directory_skip_empty_no_non_empty_parent ________________________________________________

fs = <pyfakefs.fake_filesystem.FakeFilesystem object at 0x7f99c7dda260>

    def test_get_pages_from_directory_skip_empty_no_non_empty_parent(fs):
        fs.create_file("/rootfolder/parent/child/childfile.md")
    
>       result = doc.get_pages_from_directory(Path("/rootfolder"), skip_empty=True)

/tmp/md2cf/tests/unit/test_document.py:99: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 
/usr/local/lib/python3.10/site-packages/md2cf-1.5.1-py3.10.egg/md2cf/document.py:165: in get_pages_from_directory
    processed_page = get_page_data_from_file_path(
/usr/local/lib/python3.10/site-packages/md2cf-1.5.1-py3.10.egg/md2cf/document.py:191: in get_page_data_from_file_path
    page = get_page_data_from_lines(
/usr/local/lib/python3.10/site-packages/md2cf-1.5.1-py3.10.egg/md2cf/document.py:214: in get_page_data_from_lines
    page = parse_page(
/usr/local/lib/python3.10/site-packages/md2cf-1.5.1-py3.10.egg/md2cf/document.py:238: in parse_page
    renderer = ConfluenceRenderer(
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

self = <md2cf.confluence_renderer.ConfluenceRenderer object at 0x7f99c7ddacb0>, strip_header = False, remove_text_newlines = False
kwargs = {'use_xhtml': True}

    def __init__(self, strip_header=False, remove_text_newlines=False, **kwargs):
>       super().__init__(**kwargs)
E       TypeError: HTMLRenderer.__init__() got an unexpected keyword argument 'use_xhtml'

/usr/local/lib/python3.10/site-packages/md2cf-1.5.1-py3.10.egg/md2cf/confluence_renderer.py:54: TypeError
-------------------------------------------------------------------- Captured stdout call --------------------------------------------------------------------
No git root found, gitignore checking disabled.
________________________________________________________ test_get_pages_from_directory_collapse_empty ________________________________________________________

fs = <pyfakefs.fake_filesystem.FakeFilesystem object at 0x7f99c6d05450>

    def test_get_pages_from_directory_collapse_empty(fs):
        fs.create_file("/rootfolder/rootfolderfile.md")
        fs.create_file("/rootfolder/parent/child/childfile.md")
    
>       result = doc.get_pages_from_directory(Path("/rootfolder"), collapse_empty=True)

/tmp/md2cf/tests/unit/test_document.py:118: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 
/usr/local/lib/python3.10/site-packages/md2cf-1.5.1-py3.10.egg/md2cf/document.py:165: in get_pages_from_directory
    processed_page = get_page_data_from_file_path(
/usr/local/lib/python3.10/site-packages/md2cf-1.5.1-py3.10.egg/md2cf/document.py:191: in get_page_data_from_file_path
    page = get_page_data_from_lines(
/usr/local/lib/python3.10/site-packages/md2cf-1.5.1-py3.10.egg/md2cf/document.py:214: in get_page_data_from_lines
    page = parse_page(
/usr/local/lib/python3.10/site-packages/md2cf-1.5.1-py3.10.egg/md2cf/document.py:238: in parse_page
    renderer = ConfluenceRenderer(
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

self = <md2cf.confluence_renderer.ConfluenceRenderer object at 0x7f99c790c9a0>, strip_header = False, remove_text_newlines = False
kwargs = {'use_xhtml': True}

    def __init__(self, strip_header=False, remove_text_newlines=False, **kwargs):
>       super().__init__(**kwargs)
E       TypeError: HTMLRenderer.__init__() got an unexpected keyword argument 'use_xhtml'

/usr/local/lib/python3.10/site-packages/md2cf-1.5.1-py3.10.egg/md2cf/confluence_renderer.py:54: TypeError
-------------------------------------------------------------------- Captured stdout call --------------------------------------------------------------------
No git root found, gitignore checking disabled.
______________________________________________ test_get_pages_from_directory_collapse_empty_no_non_empty_parent ______________________________________________

fs = <pyfakefs.fake_filesystem.FakeFilesystem object at 0x7f99c79305e0>

    def test_get_pages_from_directory_collapse_empty_no_non_empty_parent(fs):
        fs.create_file("/rootfolder/parent/child/childfile.md")
    
>       result = doc.get_pages_from_directory(Path("/rootfolder"), collapse_empty=True)

/tmp/md2cf/tests/unit/test_document.py:140: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 
/usr/local/lib/python3.10/site-packages/md2cf-1.5.1-py3.10.egg/md2cf/document.py:165: in get_pages_from_directory
    processed_page = get_page_data_from_file_path(
/usr/local/lib/python3.10/site-packages/md2cf-1.5.1-py3.10.egg/md2cf/document.py:191: in get_page_data_from_file_path
    page = get_page_data_from_lines(
/usr/local/lib/python3.10/site-packages/md2cf-1.5.1-py3.10.egg/md2cf/document.py:214: in get_page_data_from_lines
    page = parse_page(
/usr/local/lib/python3.10/site-packages/md2cf-1.5.1-py3.10.egg/md2cf/document.py:238: in parse_page
    renderer = ConfluenceRenderer(
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

self = <md2cf.confluence_renderer.ConfluenceRenderer object at 0x7f99c7932230>, strip_header = False, remove_text_newlines = False
kwargs = {'use_xhtml': True}

    def __init__(self, strip_header=False, remove_text_newlines=False, **kwargs):
>       super().__init__(**kwargs)
E       TypeError: HTMLRenderer.__init__() got an unexpected keyword argument 'use_xhtml'

/usr/local/lib/python3.10/site-packages/md2cf-1.5.1-py3.10.egg/md2cf/confluence_renderer.py:54: TypeError
-------------------------------------------------------------------- Captured stdout call --------------------------------------------------------------------
No git root found, gitignore checking disabled.
_______________________________________________________ test_get_pages_from_directory_beautify_folders _______________________________________________________

fs = <pyfakefs.fake_filesystem.FakeFilesystem object at 0x7f99c790df30>

    def test_get_pages_from_directory_beautify_folders(fs):
        fs.create_file("/rootfolder/ugly-folder/another_yucky_folder/childfile.md")
    
>       result = doc.get_pages_from_directory(Path("/rootfolder"), beautify_folders=True)

/tmp/md2cf/tests/unit/test_document.py:158: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 
/usr/local/lib/python3.10/site-packages/md2cf-1.5.1-py3.10.egg/md2cf/document.py:165: in get_pages_from_directory
    processed_page = get_page_data_from_file_path(
/usr/local/lib/python3.10/site-packages/md2cf-1.5.1-py3.10.egg/md2cf/document.py:191: in get_page_data_from_file_path
    page = get_page_data_from_lines(
/usr/local/lib/python3.10/site-packages/md2cf-1.5.1-py3.10.egg/md2cf/document.py:214: in get_page_data_from_lines
    page = parse_page(
/usr/local/lib/python3.10/site-packages/md2cf-1.5.1-py3.10.egg/md2cf/document.py:238: in parse_page
    renderer = ConfluenceRenderer(
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

self = <md2cf.confluence_renderer.ConfluenceRenderer object at 0x7f99c6df0400>, strip_header = False, remove_text_newlines = False
kwargs = {'use_xhtml': True}

    def __init__(self, strip_header=False, remove_text_newlines=False, **kwargs):
>       super().__init__(**kwargs)
E       TypeError: HTMLRenderer.__init__() got an unexpected keyword argument 'use_xhtml'

/usr/local/lib/python3.10/site-packages/md2cf-1.5.1-py3.10.egg/md2cf/confluence_renderer.py:54: TypeError
-------------------------------------------------------------------- Captured stdout call --------------------------------------------------------------------
No git root found, gitignore checking disabled.
____________________________________________________________________ test_renderer_reinit ____________________________________________________________________

    def test_renderer_reinit():
        renderer = ConfluenceRenderer()
>       renderer.header("this is a title", 1)

tests/unit/test_renderer.py:102: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

self = <md2cf.confluence_renderer.ConfluenceRenderer object at 0x7f99c790cca0>, text = 'this is a title', level = 1, raw = None

    def header(self, text, level, raw=None):
        if self.title is None and level == 1:
            self.title = text
            # Don't duplicate page title as a header
            if self.strip_header:
                return ""
    
>       return super(ConfluenceRenderer, self).header(text, level, raw=raw)
E       AttributeError: 'super' object has no attribute 'header'

/usr/local/lib/python3.10/site-packages/md2cf-1.5.1-py3.10.egg/md2cf/confluence_renderer.py:71: AttributeError
______________________________________________________________ test_renderer_header_sets_title _______________________________________________________________

    def test_renderer_header_sets_title():
        test_header = "this is a header"
        renderer = ConfluenceRenderer()
    
>       renderer.header(test_header, 1)

tests/unit/test_renderer.py:141: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

self = <md2cf.confluence_renderer.ConfluenceRenderer object at 0x7f99c6df03a0>, text = 'this is a header', level = 1, raw = None

    def header(self, text, level, raw=None):
        if self.title is None and level == 1:
            self.title = text
            # Don't duplicate page title as a header
            if self.strip_header:
                return ""
    
>       return super(ConfluenceRenderer, self).header(text, level, raw=raw)
E       AttributeError: 'super' object has no attribute 'header'

/usr/local/lib/python3.10/site-packages/md2cf-1.5.1-py3.10.egg/md2cf/confluence_renderer.py:71: AttributeError
____________________________________________________ test_renderer_header_lower_level_does_not_set_title _____________________________________________________

    def test_renderer_header_lower_level_does_not_set_title():
        test_header = "this is a header"
        renderer = ConfluenceRenderer()
    
>       renderer.header(test_header, 2)

tests/unit/test_renderer.py:159: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

self = <md2cf.confluence_renderer.ConfluenceRenderer object at 0x7f99c6901ea0>, text = 'this is a header', level = 2, raw = None

    def header(self, text, level, raw=None):
        if self.title is None and level == 1:
            self.title = text
            # Don't duplicate page title as a header
            if self.strip_header:
                return ""
    
>       return super(ConfluenceRenderer, self).header(text, level, raw=raw)
E       AttributeError: 'super' object has no attribute 'header'

/usr/local/lib/python3.10/site-packages/md2cf-1.5.1-py3.10.egg/md2cf/confluence_renderer.py:71: AttributeError
________________________________________________________ test_renderer_header_later_level_sets_title _________________________________________________________

    def test_renderer_header_later_level_sets_title():
        test_lower_header = "this is a lower header"
        test_header = "this is a header"
        renderer = ConfluenceRenderer()
    
>       renderer.header(test_lower_header, 2)

tests/unit/test_renderer.py:169: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

self = <md2cf.confluence_renderer.ConfluenceRenderer object at 0x7f99c6dd3dc0>, text = 'this is a lower header', level = 2, raw = None

    def header(self, text, level, raw=None):
        if self.title is None and level == 1:
            self.title = text
            # Don't duplicate page title as a header
            if self.strip_header:
                return ""
    
>       return super(ConfluenceRenderer, self).header(text, level, raw=raw)
E       AttributeError: 'super' object has no attribute 'header'

/usr/local/lib/python3.10/site-packages/md2cf-1.5.1-py3.10.egg/md2cf/confluence_renderer.py:71: AttributeError
_________________________________________________________ test_renderer_header_only_sets_first_title _________________________________________________________

    def test_renderer_header_only_sets_first_title():
        test_header = "this is a header"
        test_second_header = "this is another header"
        renderer = ConfluenceRenderer()
    
>       renderer.header(test_header, 1)

tests/unit/test_renderer.py:180: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

self = <md2cf.confluence_renderer.ConfluenceRenderer object at 0x7f99c7dc8100>, text = 'this is a header', level = 1, raw = None

    def header(self, text, level, raw=None):
        if self.title is None and level == 1:
            self.title = text
            # Don't duplicate page title as a header
            if self.strip_header:
                return ""
    
>       return super(ConfluenceRenderer, self).header(text, level, raw=raw)
E       AttributeError: 'super' object has no attribute 'header'

/usr/local/lib/python3.10/site-packages/md2cf-1.5.1-py3.10.egg/md2cf/confluence_renderer.py:71: AttributeError
================================================================== short test summary info ===================================================================
FAILED tests/functional/test_full_rendering.py::test_full_document - TypeError: HTMLRenderer.__init__() got an unexpected keyword argument 'use_xhtml'
FAILED tests/unit/test_document.py::test_get_pages_from_directory - TypeError: HTMLRenderer.__init__() got an unexpected keyword argument 'use_xhtml'
FAILED tests/unit/test_document.py::test_get_pages_from_directory_collapse_single_pages - TypeError: HTMLRenderer.__init__() got an unexpected keyword argument 'use_xhtml'
FAILED tests/unit/test_document.py::test_get_pages_from_directory_collapse_single_pages_no_non_empty_parent - TypeError: HTMLRenderer.__init__() got an unexpected keyword argument 'use_xhtml'
FAILED tests/unit/test_document.py::test_get_pages_from_directory_skip_empty - TypeError: HTMLRenderer.__init__() got an unexpected keyword argument 'use_xhtml'
FAILED tests/unit/test_document.py::test_get_pages_from_directory_skip_empty_no_non_empty_parent - TypeError: HTMLRenderer.__init__() got an unexpected keyword argument 'use_xhtml'
FAILED tests/unit/test_document.py::test_get_pages_from_directory_collapse_empty - TypeError: HTMLRenderer.__init__() got an unexpected keyword argument 'use_xhtml'
FAILED tests/unit/test_document.py::test_get_pages_from_directory_collapse_empty_no_non_empty_parent - TypeError: HTMLRenderer.__init__() got an unexpected keyword argument 'use_xhtml'
FAILED tests/unit/test_document.py::test_get_pages_from_directory_beautify_folders - TypeError: HTMLRenderer.__init__() got an unexpected keyword argument 'use_xhtml'
FAILED tests/unit/test_renderer.py::test_renderer_reinit - AttributeError: 'super' object has no attribute 'header'
FAILED tests/unit/test_renderer.py::test_renderer_header_sets_title - AttributeError: 'super' object has no attribute 'header'
FAILED tests/unit/test_renderer.py::test_renderer_header_lower_level_does_not_set_title - AttributeError: 'super' object has no attribute 'header'
FAILED tests/unit/test_renderer.py::test_renderer_header_later_level_sets_title - AttributeError: 'super' object has no attribute 'header'
FAILED tests/unit/test_renderer.py::test_renderer_header_only_sets_first_title - AttributeError: 'super' object has no attribute 'header'
=============================================================== 14 failed, 47 passed in 0.51s ================================================================

I tested the above with Mistune 2.0.4 on Python 3.8 and Python 3.10.

Unfortunately this exceeds my Python skillset - but maybe support for Mistune 2 could be considered for a future release?

Best,
Georg

certificate verify failed: self signed certificate in certificate chain

Possible solve Ref

md2cf --host 'https://confluence.********.com/rest/api' --username ************ --password '************' --space TEST document.md
ERROR: HTTPSConnectionPool(host='confluence.kordia.net', port=443): Max retries exceeded with url: /rest/api/content?title=md2cf+test&spaceKey=TEST (Caused by SSLError(SSLCertVerificationError(1, '[SSL: CERTIFICATE_VERIFY_FAILED] certificate verify failed: self signed certificate in certificate chain (_ssl.c:1129)')))

Convert URL-encoded attachment paths?

Hey there, I checked for previous issues about this, but didn't find anything that seemed relevant.
I'm working with a dump of markdown from a popular wiki-like tool that starts with an N, and for attached images, it exports like this:

Export-folder:
 John a10958b569844bf9bc44097757e559f8.md
 |_ John a10958b569844bf9bc44097757e559f8
     |_profile-picture.jpeg

But in the export file, it's referencing URL-encoded paths, so the spaces are %20, emojis are written out, etc.

![profile-picture.jpeg](John%20a10958b569844bf9bc44097757e559f8/profile-picture.jpeg)

The result is that the tool throws

❌ ERROR: attachment (path) for page (page name) does not exist 

Am I doing something wrong here, or do I just need to pump these through some script to convert the file names and paths to URL-encoded ones? At this point I'm not sure if this is a bug or a feature request. :)

Failed to upload image

I have an image notation in my markdown file

![The credential set form](images/auth-token-form.png)

I see this error when the file is uploaded as part of a folder with the new relative links flag enabled:

ERROR: Session.request() got an unexpected keyword argument 'format'    

I did some very brief digging and it looks like the parameters in api.py may not have been updated to match requests. I see on line 230:

                data={"comment": message} if message else None,
                format=(None, "json"),

and I'm thinking it should be something like:

                json={"comment": message} if message else None,

That's just a guess though.

parent_title and rate limits

Hi!
I really like md2cf! Awesome! I created a small simple Github Action for syncing Github md-files to Confluence in my company, and it works really well. The use of it started spreading to other teams, and some issues surfaced :-)

  1. it says you can use either parent_id or parent_title, but I can't get parent_title to work. it says Unexpected input(s) 'parent_title', valid inputs are ['entryPoint', 'args', 'github_f', 'space', 'confluence_token', 'confluence_host', 'parent_id'] and I don't think it's due to something I did... but maybe I'm mistaken. I looked in the api.py and I can't find any reference to parent_title.

  2. A team that has many md-files to sync ran into rate limiting issues when trying to sync them. It is due to the Confluence rate limiting settings in our company off course (and we are trying to increase that limit), but I did some googling and came across this: https://confluence.atlassian.com/doc/adjusting-your-code-for-rate-limiting-992679008.html
    Any thought on adding something like this in your code for the "micro-implementation" of the ConfluenceServer REST API?

I can provide source code if needed. Otherwise just happy for some thoughts on above issues :-)
Thanks
/Pixie

403 client error "allowedInReadOnlyMode:true" prevents update to page

I have a space with multiple pages. I pushed an update to one of the pages then tried another page in the same space and get this error:

Updating page: pageupdate.md
403 Client Error:  for url: https://confluence.<example>.com/rest/api/content/<pageid> - b'{"statusCode":403,"data":{"authorized":false,"valid":true,"allowedInReadOnlyMode":true,"errors":[],"successful":false},"message":"Could not update Content of type : class com.atlassian.confluence.pages.Page with id <pageid>","reason":"Forbidden"}'

Any tips on this? I've verified that I have read/write permissions on the page.

Handle relative-links within documents

Hello,

I just looked at your project and it does seem to be super-promising for the use-case I have. One of the features that I am missing it the ability to handle relative links within the Markdown files. In the past I tried another tools and provided a pull-request for that functionality, but there have been a few issues with the implementation. Please have a look here for reference:

justmiles/go-markdown2confluence#40

Doing this in Python does sound way more straight forward and sustainable and I was wondering if you had already something in store or maybe an idea on how this could be implemented.

Feature-wise it would be something like

  • Identify relative paths within the document
  • Replace the paths with the Confluence pages
  • Create the Confluence page if it does not already exist

I'd be happy to help out!

Best,
Matthias

ERROR: list index out of range

During deployment of our large repository we got a list out of index error.

Command:

                md2cf --host $(confluence-host) --token $(confluence-api-key) \
                  ./documentation/docs/ \
                  --space $(confluence-space) \
                  --parent-title "Platform" \
                  --insecure \
                  --only-changed \
                  --strip-top-header

Output:

2023-02-14T19:53:07.7272110Z  Total progress ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ 100% 0:00:00           
2023-02-14T19:53:09.0155527Z                                                                                                                                                                 
2023-02-14T19:53:09.0158112Z  📄️ Platform          ━━━━━━━━━━━━━  ❌ Error while uploading 
2023-02-14T19:53:09.0159179Z  📂️ documentation                ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ ⠦   
2023-02-14T19:53:09.0160068Z  ├── 📂️ 05-dashboards                   ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ ⠦   
2023-02-14T19:53:09.0160907Z  │   └── 📂️ 01-grafana                  ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ ⠦   
2023-02-14T19:53:09.0162100Z  │       └── 📄️ Grafana                 ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ ⠦   
2023-02-14T19:53:09.0162952Z  ├── 📂️ 08-logs                         ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ ⠦   
2023-02-14T19:53:09.0163763Z  │   ├── 📂️ 03-loki                     ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ ⠦   
2023-02-14T19:53:09.0165744Z  │   │   └── 📄️ Loki                    ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ ⠦   
2023-02-14T19:53:09.0167407Z  │   │       ├── 📎️ logs-loki-layout.png━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ ⠦   
2023-02-14T19:53:09.0168252Z  │   │       └── 📎️                     ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ ⠦   
2023-02-14T19:53:09.0169094Z  │   │           logs-loki-configuratio…━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ ⠦   
2023-02-14T19:53:09.0169944Z  │   ├── 📂️ 02-fluentd                  ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ ⠦   
2023-02-14T19:53:09.0170767Z  │   │   └── 📄️ FluentD                 ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ ⠦   
2023-02-14T19:53:09.0171567Z  │   │       └── 📎️                     ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ ⠦   
2023-02-14T19:53:09.0172730Z  │   │           logs-fluentd-layout.png━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ ⠦   
2023-02-14T19:53:09.0178884Z  │   └── 📂️ 01-fluentbit                ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ ⠦   
2023-02-14T19:53:09.0180068Z  │       └── 📄️ FluentBit               ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ ⠦   
2023-02-14T19:53:09.0180898Z  │           ├── 📎️                     ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ ⠦   
2023-02-14T19:53:09.0181732Z  │           │   logs-fluentbit-configu…━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ ⠦   
2023-02-14T19:53:09.0182695Z  │           ├── 📎️                     ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ ⠦   
2023-02-14T19:53:09.0183536Z  │           │   logs-fluentbit-layout.…━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ ⠦   
2023-02-14T19:53:09.0184338Z  │           └── 📎️                     ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ ⠦   
2023-02-14T19:53:09.0185158Z  │               logs-fluentbit-design.…━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ ⠦   
2023-02-14T19:53:09.0186054Z  ├── 📂️ 10-network                      ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ ⠦   
2023-02-14T19:53:09.0186872Z  │   ├── 📄️ Network                     ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ ⠦   
2023-02-14T19:53:09.0187714Z  │   ├── 📂️ 03-nginx-ingress-controller ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ ⠦   
2023-02-14T19:53:09.0188581Z  │   │   └── 📄️ Nginx Ingress Controller━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ ⠦   
2023-02-14T19:53:09.0189387Z  │   │       └── 📎️                     ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ ⠦   
2023-02-14T19:53:09.0190325Z  │   │           network-network-mappin…━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ ⠦   
2023-02-14T19:53:09.0191176Z  │   └── 📂️ 02-network-mapping          ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ ⠦   
2023-02-14T19:53:09.0192021Z  │       └── 📄️ Network Mapping         ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ ⠦   
2023-02-14T19:53:09.0193046Z  ├── 📂️ 09-metrics                      ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ ⠦   
2023-02-14T19:53:09.0194124Z  │   ├── 📂️ 01-alertmanager             ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ ⠦   
2023-02-14T19:53:09.0194964Z  │   │   └── 📄️ AlertManager            ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ ⠦   
2023-02-14T19:53:09.0195767Z  │   │       └── 📎️                     ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ ⠦   
2023-02-14T19:53:09.0196641Z  │   │           metrics-alertmanager.p…━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ ⠦   
2023-02-14T19:53:09.0197422Z  │   ├── 📂️ 02-prometheus               ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ ⠦   
2023-02-14T19:53:09.0198183Z  │   │   └── 📄️ Prometheus              ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ ⠦   
2023-02-14T19:53:09.0198925Z  │   │       └── 📎️                     ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ ⠦   
2023-02-14T19:53:09.0199659Z  │   │           metrics-prometheus-lay…━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ ⠦   
2023-02-14T19:53:09.0200454Z  │   ├── 📂️ 04-victoria-metrics-alert   ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ ⠦   
2023-02-14T19:53:09.0201264Z  │   │   └── 📄️ Victoria Metrics Alert  ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ ⠦   
2023-02-14T19:53:09.0202401Z  │   │       └── 📎️                     ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ ⠦   
2023-02-14T19:53:09.0203212Z  │   │           metrics-victoria-metri…━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ ⠦   
2023-02-14T19:53:09.0205464Z  │   └── 📂️ 03-victoria-metrics         ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ ⠦   
2023-02-14T19:53:09.0206361Z  │       └── 📄️ Victoria Metrics        ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ ⠦   
2023-02-14T19:53:09.0207171Z  │           ├── 📎️                     ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ ⠦   
2023-02-14T19:53:09.0207979Z  │           │   metrics-victoria-metri…━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ ⠦   
2023-02-14T19:53:09.0208919Z  │           └── 📎️                     ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ ⠦   
2023-02-14T19:53:09.0209737Z  │               metrics-victoria-metri…━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ ⠦   
2023-02-14T19:53:09.0210574Z  ├── 📂️ 06-gitops                       ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ ⠦   
2023-02-14T19:53:09.0211599Z  │   └── 📂️ 01-argocd                   ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ ⠦   
2023-02-14T19:53:09.0212412Z  │       └── 📄️ ArgoCD                  ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ ⠦   
2023-02-14T19:53:09.0213233Z  │           └── 📎️ argocd-1.png        ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ ⠦   
2023-02-14T19:53:09.0214126Z  ├── 📂️ 01-proposition   ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ ⠦   
2023-02-14T19:53:09.0215024Z  │   └── 📄️ PROPOSITION  ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ ⠦   
2023-02-14T19:53:09.0215992Z  │       └── 📎️                         ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ ⠦   
2023-02-14T19:53:09.0216848Z  │           ./service-propositi…━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ ⠦   
2023-02-14T19:53:09.0217673Z  ├── 📂️ 03-infrastructure               ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ ⠦   
2023-02-14T19:53:09.0218503Z  │   └── 📄️ Infrastructure              ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ ⠦   
2023-02-14T19:53:09.0219543Z  ├── 📂️ 02-generic-design               ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ ⠦   
2023-02-14T19:53:09.0220371Z  │   └── 📄️ Generic Design              ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ ⠦   
2023-02-14T19:53:09.0221210Z  │       ├── 📎️ ./generic-design-1.png  ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ ⠦   
2023-02-14T19:53:09.0222060Z  │       ├── 📎️ ./generic-design-2.png  ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ ⠦   
2023-02-14T19:53:09.0223607Z  │       ├── 📎️ ./generic-design-3.png  ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ ⠦   
2023-02-14T19:53:09.0224473Z  │       ├── 📎️ ./generic-design-4.png  ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ ⠦   
2023-02-14T19:53:09.0225558Z  │       ├── 📎️ ./generic-design-5.png  ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ ⠦   
2023-02-14T19:53:09.0229346Z  │       └── 📎️ ./generic-design-6.png  ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ ⠦   
2023-02-14T19:53:09.0230889Z  ├── 📂️ 04-procedures                   ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ ⠦   
2023-02-14T19:53:09.0232049Z  │   ├── 📂️ 03-operational-guide        ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ ⠦   
2023-02-14T19:53:09.0232934Z  │   │   ├── 📄️ Onboarding ArgoCD       ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ ⠦   
2023-02-14T19:53:09.0233824Z  │   │   ├── 📄️ Onboarding new Client   ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ ⠦   
2023-02-14T19:53:09.0234675Z  │   │   │   Cluster                    ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ ⠦   
2023-02-14T19:53:09.0235476Z  │   │   │   └── 📎️                     ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ ⠦   
2023-02-14T19:53:09.0236293Z  │   │   │       ./operational-guide-on…━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ ⠦   
2023-02-14T19:53:09.0237134Z  │   │   ├── 📄️ Onboarding External     ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ ⠦   
2023-02-14T19:53:09.0237963Z  │   │   │   Secret Operator (ESO)      ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ ⠦   
2023-02-14T19:53:09.0238763Z  │   │   │   └── 📎️                     ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ ⠦   
2023-02-14T19:53:09.0239585Z  │   │   │       ./operational-guide-on…━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ ⠦   
2023-02-14T19:53:09.0240579Z  │   │   ├── 📄️ Onboarding logs         ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ ⠦   
2023-02-14T19:53:09.0241384Z  │   │   │   ├── 📎️                     ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ ⠦   
2023-02-14T19:53:09.0242193Z  │   │   │   │   operational-guide-onbo…━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ ⠦   
2023-02-14T19:53:09.0243004Z  │   │   │   └── 📎️                     ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ ⠦   
2023-02-14T19:53:09.0262792Z  │   │   │       ./operational-guide-on…━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ ⠦   
2023-02-14T19:53:09.0264232Z  │   │   ├── 📄️ Onboarding dashboards   ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ ⠦   
2023-02-14T19:53:09.0265135Z  │   │   │   ├── 📎️                     ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ ⠦   
2023-02-14T19:53:09.0265981Z  │   │   │   │   operational-guide-onbo…━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ ⠦   
2023-02-14T19:53:09.0266805Z  │   │   │   ├── 📎️                     ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ ⠦   
2023-02-14T19:53:09.0267898Z  │   │   │   │   operational-guide-onbo…━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ ⠦   
2023-02-14T19:53:09.0270239Z  │   │   │   ├── 📎️                     ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ ⠦   
2023-02-14T19:53:09.0271075Z  │   │   │   │   operational-guide-onbo…━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ ⠦   
2023-02-14T19:53:09.0271892Z  │   │   │   └── 📎️                     ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ ⠦   
2023-02-14T19:53:09.0272705Z  │   │   │       operational-guide-onbo…━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ ⠦   
2023-02-14T19:53:09.0273563Z  │   │   ├── 📄️ Onboarding New Alert    ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ ⠦   
2023-02-14T19:53:09.0274379Z  │   │   │   Channel                    ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ ⠦   
2023-02-14T19:53:09.0276513Z  │   │   │   └── 📎️                     ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ ⠦   
2023-02-14T19:53:09.0277615Z  │   │   │       operational-guide-onbo…━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ ⠦   
2023-02-14T19:53:09.0278540Z  │   │   ├── 📄️ Onboarding App Metrics  ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ ⠦   
2023-02-14T19:53:09.0279364Z  │   │   │   ├── 📎️                     ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ ⠦   
2023-02-14T19:53:09.0280180Z  │   │   │   │   operational-guide-onbo…━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ ⠦   
2023-02-14T19:53:09.0280993Z  │   │   │   ├── 📎️                     ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ ⠦   
2023-02-14T19:53:09.0281809Z  │   │   │   │   operational-guide-onbo…━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ ⠦   
2023-02-14T19:53:09.0282606Z  │   │   │   └── 📎️                     ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ ⠦   
2023-02-14T19:53:09.0283743Z  │   │   │       operational-guide-onbo…━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ ⠦   
2023-02-14T19:53:09.0284633Z  │   │   └── 📄️ Onboarding an Alert     ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ ⠦   
2023-02-14T19:53:09.0285445Z  │   │       └── 📎️                     ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ ⠦   
2023-02-14T19:53:09.0286288Z  │   │           ./operational-guide-on…━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ ⠦   
2023-02-14T19:53:09.0287361Z  │   ├── 📂️ 02-customer-guide           ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ ⠦   
2023-02-14T19:53:09.0288193Z  │   │   ├── 📄️ Onboarding a Cluster in ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ ⠦   
2023-02-14T19:53:09.0289008Z  │   │   │   ArgoCD                     ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ ⠦   
2023-02-14T19:53:09.0289821Z  │   │   ├── 📄️ Onboarding New          ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ ⠦   
2023-02-14T19:53:09.0290628Z  │   │   │   Application                ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ ⠦   
2023-02-14T19:53:09.0291421Z  │   │   │   ├── 📎️                     ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ ⠦   
2023-02-14T19:53:09.0292226Z  │   │   │   │   customer-guide-introdu…━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ ⠦   
2023-02-14T19:53:09.0293023Z  │   │   │   ├── 📎️                     ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ ⠦   
2023-02-14T19:53:09.0293987Z  │   │   │   │   customer-guide-onboard…━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ ⠦   
2023-02-14T19:53:09.0294927Z  │   │   │   ├── 📎️                     ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ ⠦   
2023-02-14T19:53:09.0295965Z  │   │   │   │   customer-guide-onboard…                                        
2023-02-14T19:53:09.0296822Z  │   │   │   ├── 📎️                                                             
2023-02-14T19:53:09.0297603Z  │   │   │   │   customer-guide-onboard…                                        
2023-02-14T19:53:09.0298366Z  │   │   │   ├── 📎️                                                             
2023-02-14T19:53:09.0299138Z  │   │   │   │   customer-guide-onboard…                                        
2023-02-14T19:53:09.0299914Z  │   │   │   └── 📎️                                                             
2023-02-14T19:53:09.0302040Z  │   │   │       customer-guide-onboard…                                        
2023-02-14T19:53:09.0303004Z  │   │   └── 📄️ Onboarding New Grafana                                          
2023-02-14T19:53:09.0304046Z  │   │       Datasource                                                         
2023-02-14T19:53:09.0304827Z  │   ├── 📂️ 01-getting-started                                                  
2023-02-14T19:53:09.0306613Z  │   │   ├── 📄️ Checklist template                                              
2023-02-14T19:53:09.0307581Z  │   │   │   pre-requirements                                                   
2023-02-14T19:53:09.0308370Z  │   │   ├── 📄️ Structure                                                       
2023-02-14T19:53:09.0309156Z  │   │   ├── 📄️ Generic design                                                  
2023-02-14T19:53:09.0309934Z  │   │   └── 📄️ Around                                                      
2023-02-14T19:53:09.0310709Z  │   └── 📂️ 04-developer-guide                                                  
2023-02-14T19:53:09.0311507Z  │       ├── 📄️ GitOps Structure                                                
2023-02-14T19:53:09.0312300Z  │       │   ├── 📎️                                                             
2023-02-14T19:53:09.0313071Z  │       │   │   developer-guide-introd…                                        
2023-02-14T19:53:09.0313845Z  │       │   ├── 📎️                                                             
2023-02-14T19:53:09.0314609Z  │       │   │   developer-guide-gitops…                                        
2023-02-14T19:53:09.0315361Z  │       │   └── 📎️                                                             
2023-02-14T19:53:09.0316131Z  │       │       developer-guide-gitops…                                        
2023-02-14T19:53:09.0316919Z  │       └── 📄️ Git strategy                                                    
2023-02-14T19:53:09.0317676Z  │           ├── 📎️                                                             
2023-02-14T19:53:09.0318445Z  │           │   developer-guide-git-fl…                                        
2023-02-14T19:53:09.0319627Z  │           ├── 📎️                                                             
2023-02-14T19:53:09.0320348Z  │           │   developer-guide-git-fl…                                        
2023-02-14T19:53:09.0321067Z  │           ├── 📎️                                                             
2023-02-14T19:53:09.0321954Z  │           │   developer-guide-git-fl…                                        
2023-02-14T19:53:09.0322722Z  │           └── 📎️                                                             
2023-02-14T19:53:09.0323494Z  │               developer-guide-git-fl…                                        
2023-02-14T19:53:09.0324290Z  └── 📂️ 07-integrations                                                         
2023-02-14T19:53:09.0325066Z      ├── 📂️ 04-hungryhippo                                                      
2023-02-14T19:53:09.0325855Z      │   └── 📄️ HungryHippo                                                     
2023-02-14T19:53:09.0326632Z      │       └── 📎️                                                             
2023-02-14T19:53:09.0327544Z      │           integrations-hungryhip…                                        
2023-02-14T19:53:09.0346026Z      ├── 📂️ 01-aad-pod-identity                                                 
2023-02-14T19:53:09.0346934Z      │   └── 📄️ AAD Pod Identity                                                
2023-02-14T19:53:09.0347842Z      │       └── 📎️                                                             
2023-02-14T19:53:09.0348642Z      │           integrations-aad-pod-i…                                        
2023-02-14T19:53:09.0349490Z      ├── 📂️ 02-external-secret-operator                                         
2023-02-14T19:53:09.0351842Z      │   └── 📄️ External Secret Operator                                        
2023-02-14T19:53:09.0352683Z      │       └── 📎️                                                             
2023-02-14T19:53:09.0353464Z      │           integrations-external-…                                        
2023-02-14T19:53:09.0354656Z      └── 📂️ 03-grafana-integration                                              
2023-02-14T19:53:09.0355490Z          └── 📄️ Grafana Integration                                             
2023-02-14T19:53:09.0356201Z              (Grafana Sync)                                                     
2023-02-14T19:53:09.0356954Z              └── 📎️                                                             
2023-02-14T19:53:09.0357732Z                  integrations-grafana-s…                                        
2023-02-14T19:53:09.0358279Z                                                                                 
2023-02-14T19:53:09.0359067Z  Total progress                                            1% -:--:--           
2023-02-14T19:53:09.0359767Z                                                                                 ERROR: list index out of range 
2023-02-14T19:53:09.0578898Z ##[error]Bash exited with code '1'.

Unicode

I don't know if it's a md2cf or my problem. I have several md's with umlauts (äöü etc.). These are not converted correctly.
Can I fix this somewhere?
I am unfortunately not a gifted Python programmer

AttributeError: module 'mistune' has no attribute 'Renderer'

Traceback (most recent call last):
  File "/Users/eeshan.jaiswal/.pyenv/versions/3.9.0/envs/gitlab_script/bin/md2cf", line 5, in <module>
    from md2cf.__main__ import main
  File "/Users/eeshan.jaiswal/.pyenv/versions/3.9.0/envs/gitlab_script/lib/python3.9/site-packages/md2cf/__main__.py", line 13, in <module>
    import md2cf.document
  File "/Users/eeshan.jaiswal/.pyenv/versions/3.9.0/envs/gitlab_script/lib/python3.9/site-packages/md2cf/document.py", line 9, in <module>
    from md2cf.confluence_renderer import ConfluenceRenderer
  File "/Users/eeshan.jaiswal/.pyenv/versions/3.9.0/envs/gitlab_script/lib/python3.9/site-packages/md2cf/confluence_renderer.py", line 52, in <module>
    class ConfluenceRenderer(mistune.Renderer):
AttributeError: module 'mistune' has no attribute 'Renderer'

Using Python 3.9.0

Argument `--force-unique` does not exist

When running this for a stack of our MD files we got an error stating:

Some documents have the same title. Update them or use --force-unique:

However, the --force-unique argument does not exist

Support "minorEdit" option

The confluence API supports the "minorEdit" option to not notify watchers when a page has been updated.

I would like to reduce the number of e-mails generated by automatically updating several pages at once, so I've implemented this for the update_page handler in #50 .

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.