Publishing Markdown to Confluence using GitHub Actions
Tags: confluence, githubactions, markdown
Published: 2024-02-21 23:30:11

*TL;DR We're going to setup automated Markdown export from a GitHub repository to Confluence to benefit from knowledge locality in a centralized storage.*

> `Markdown` is a lightweight markup language for creating formatted text using a plain-text editor.

https://en.wikipedia.org/wiki/Markdown

With the growing popularity of GitHub and other code hosting services, `Markdown` has become widely used as a language for documentation. It is very convenient to keep nicely formatted documentation close to the code it describes.

While `Markdown` in code repositories may lack advanced features such as search index or rich web components, it has the advantages of simplicity, lower chance of becoming obsolete and clear versioning.

With the addition of support for [mermaid diagrams](https://github.blog/2022-02-14-include-diagrams-markdown-files-mermaid/) in GitHub, `Markdown` has become a very powerful tool for versioned and accurate documentation.

In contrast, Atlassian Confluence acts as a centralized repository of information, often used by large organizations to share knowledge across people and departments. There are advantages to such an approach; access control and discoverability may be better. For example, access to code repositories is usually given to developers who work with it, not to people who support users of the product.

However, Confluence often becomes an information graveyard. As the codebase evolves, pages become obsolete or even misleading.

Could you have the best of both worlds? The answer is "Yes"!

We can organize the source of truth for code-related documents into `Markdown` files that live in code repositories, and then automatically sync changes to Confluence using GitHub Actions.

There is a [`markdown-confluence`](https://markdown-confluence.com/usage/github-actions.html) tool and a GitHub action around it that makes the export process much easier than it could have been.

Let's take an example repo and configure export of `OpenAPI` documentation and handwritten `README.md` into Confluence.

First thing to do is to get an API token. If you have enough permissions, you can create one at your [profile page](https://id.atlassian.com/manage-profile/security/api-tokens), otherwise you'll probably need to contact a person that manages your Confluence.

![Confluence API token](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/lgd2xb4yvcxt1fi8v63u.png)

Then you need to provide credentials to GitHub Actions with repository secrets, use email for `ATLASSIAN_USERNAME` and API token for `ATLASSIAN_API_TOKEN`.

![GitHub Actions secrets](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/bjyja4d93113h9kblvgb.png)

I have a [toy service](https://github.com/vearutop/cache-story) that I used for [one of my previous posts](https://dev.to/vearutop/implementing-robust-in-memory-cache-with-go-196e). It has a GitHub Action [workflow](https://github.com/vearutop/cache-story/blob/v1.1.5/.github/workflows/docs.yml#L34-L44) to keep the OpenAPI documentation up to date on a GitHub wiki page.

```yaml
...
      - name: Checkout wiki
        uses: actions/checkout@v2
        with:
          repository: ${{github.repository}}.wiki
          path: wiki
      - name: Build openapi.json
        run: |
          make build
          ./bin/* -openapi > openapi.json
          cat ./openapi.json
      - name: Generate markdown docs
        uses: docker://swaggest/swac
        with:
          args: /bin/sh -c "swac markdown ./openapi.json --add-schema-url openapi.json --out ./wiki/API-Docs.md;mv -f ./openapi.json ./wiki/openapi.json"
      - name: Push to wiki
        run: |
          cd wiki
          git config --local user.email "action@github.com"
          git config --local user.name "GitHub Action"
          git add .
          git diff-index --quiet HEAD || git commit -m "Add changes" && git push
```

Let's upgrade this workflow to push a document to Confluence.

> **NOTE**: Official doc has a misleading suggestion of `markdown-confluence/publish@v1`, workflow fails with
> _Error: Unable to resolve action markdown-confluence/publish, repository not found_
> `markdown-confluence/publish-action@v5` works well.

```yaml
      - name: Publish Markdown to Confluence
        uses: markdown-confluence/publish-action@v5
        with:
          confluenceBaseUrl: https://vearutop.atlassian.net
          confluenceParentId: 65538
          folderToPublish: wiki
          atlassianUserName: ${{ secrets.ATLASSIAN_USERNAME }}
          atlassianApiToken: ${{ secrets.ATLASSIAN_API_TOKEN }}
```

Unfortunately, the result wasn't that great, `Markdown` flavors are different between GitHub and Confluence.

![Broken Markdown rendering](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/7x0nsmg8pezx2y6u2c3s.png)

Confluence doesn't allow manual anchoring with
```markdown
## <a id="operations"></a> Operations
```

Instead, it expects local anchors to be in form of urlencoded value with some filtering (spaces are replaced by hyphens, backticks are removed).

Also, headers within list items are not supported. Making a spoiler with `<details>` does not work either. These are the few things I found while experimenting with Confluence export.

Once I've updated OpenAPI Markdown generator to accommodate for these quirks, the result was much better. The list was rendered correctly, local links to schema definitions started working.

![Better Markdown rendering](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/ol5uhn5y4tptmzkzvrry.png)

Now I'd like to publish a page with some images and diagrams. `README.md` of the repo is a good candidate. It has both images and mermaid diagrams.

Result is surprisingly good, images are displayed, 

![Image rendering](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/se99fk5eco830jqso2ua.png)

mermaid diagrams too (though rendered as PNG).

![Mermaid rendering](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/pag8jlpm69e86nyd0i65.png)

Spoiler didn't work.

![Spoiler rendering](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/rgguk7j8e527ysu83499.png)


GitHub Action to publish Markdown to Confluence accepts path to directory with `.md` files. If you need to publish under a different parent id, you'll probably need to build a directory structure and invoke publishing with different configurations as separate steps.

Here is a final [example workflow](https://github.com/vearutop/cache-story/blob/v1.1.6/.github/workflows/docs.yml).