samizdat

Use Markdown for Confluence

TL;DR Mark — a tool for syncing your Markdown with Confluence

Devil’s Playground

Oh man, I was so frustrated when I was trying to edit docs in Confluence, and it broke all my text, trying to adjust any tags led to breaking the text even more. I felt the same when Slack introduced their Wysiwyg editor that solved problems that never existed, but at least they added an option to disable it.

Wysiwyg is the Devil’s Playground, but there is a solution.

Store with Git, write in Markdown, automatically sync with Confluence. Interesting? Let me tell you about seven benefits that you could have if you could use Git for storing your docs instead of editing them directly in Confluence:

  1. Git
    Confluence has a history of changes, but it’s very limited: you can’t do git blame, you can’t do git log -S to search who added a specific text.

  2. Pull Requests
    Now, someone with a fresh set of eyes can identify problems and provide suggestions for docs. You write code — someone reviews it, you write a book — someone reviews it. Now, you write docs — someone reviews it.

  3. Instruments
    We have some great tools like VSCode, Neovim, Sublime, or Idea with all required set of extensions that make our coding even more efficient and less frustrating.

    Then why not using it for writing docs? It’s more efficient than Wysiwyg for sure.

  4. No context switching
    No need to jump around to read docs a bit. Just open it up in your editor because you store the docs near to the code.

  5. Hosting Agnostic
    You can just push your docs on GitHub if you want to open source it.

  6. Decentralization
    Everyone has their own complete copy of docs, develops it, and syncs up later.

  7. Continuous Integration
    If you have a proper tool that I’m going to introduce, you can automatically update your docs in Confluence after a successful git push.

The solution

Mark — a tool for syncing your Markdown documentation with Atlassian Confluence

Mark reads your Markdown file, creates a Confluence page if it doesn’t, uploads attachments if any, translates Markdown into HTML, and updates the contents of the page via REST API.

It’s like you don’t even need to create sections/pages in your Confluence anymore, just mention them in your .md files.

Mark uses an extended file format, which, still being valid markdown, contains several HTML-ish metadata headers, which can be used to locate page inside Confluence instance and update it accordingly.

<!-- Space: <space key> -->
<!-- Parent: <parent 1> -->
<!-- Parent: <parent 2> -->
<!-- Title: <title> -->
<!-- Attachment: <local path 1> -->
<!-- Attachment: <local path 2> -->

Some text here.

Example

mark.md

<!-- Space: DEV -->
<!-- Parent: Automation -->
<!-- Parent: Documentation -->
<!-- Title: Sync Markdown to Confluence -->
<!-- Attachment: images/screenshot.png -->

Article text here.
![screenshot](images/architecture.png)
$ mark -u user -p password -b http://confluence/ -f mark.md

Mark will upload file images/screenshot.png as an attachment, create a page in Confluence by the following path:
Dev / Automation / Documentation → Sync Markdown to Confluence, then render Markdown as HTML and put the contents via REST API on the user’s behalf.

Continuous Integration

It’s quite trivial to integrate Mark into a CI/CD system, here is an example with Snake CI in case of self-hosted Bitbucket Server / Data Center.

stages:
  - sync

Sync documentation:
  stage: sync
  only:
    branches:
      - main
  image: kovetskiy/mark
  commands:
    - for file in $(find -type f -name '*.md'); do
        echo "> Sync $file";
        mark -u $MARK_USER -p $MARK_PASS -b $MARK_URL -f $file || exit 1;
        echo;
      done

In this example, I’m using the kovetskiy/mark image for creating a job container where the repository with documentation will be cloned to. The following command finds all *.md files and runs mark against them one by one:

for file in $(find -type f -name '*.md'); do
    echo "> Sync $file";
    mark -u $MARK_USER -p $MARK_PASS -b $MARK_URL -f $file || exit 1;
    echo;
done

The following directive tells the CI to run this particular job only if the changes are pushed into the main branch. It means you can safely push your changes into feature branches without being afraid that they automatically shown in Confluence, then go through the reviewal process and automatically deploy them when PR got merged.

only:
  branches:
    - main

Mark is open source and available on GitHub: github.com/kovetskiy/mark