Documenting Ansible Collections
Tags: ansible, ansiblecollections, automation
Published: 2021-01-06 14:10:55

**WORDS BY** Tadej Borovšak

It is a well-known fact that real programmers do not document their code. If it was hard to write, it should be hard to understand and use. But this attitude might prove itself to be a bit problematic if we would like to get paid for our work. And because we do not want to see you all broke, we will show you how to document your Ansible collections using [Sphinx] (https://www.sphinx-doc.org/).

#**Prerequisites**
If you would like to follow along and test the commands from this post on your computer, you will need to create a new virtual environment and clone the [Sensu Go Ansible collection] (https://galaxy.ansible.com/sensu/sensu_go) like this:

```
$ python3 -m venv venv
$ . venv/bin/activate
(venv) $ mkdir -p ansible_collections/sensu
(venv) $ cd ansible_collections/sensu
(venv) $ git clone https://github.com/sensu/sensu-go-ansible.git sensu_go
(venv) $ cd sensu_go
(venv) $ rm -rf docs/*
```

And yes, all those directories **are** needed. We will (probably) explain the reason behind this requirement in another post. But now it is time to get back to the problem of documenting the Ansible collections.

#**One, two… eight documentation types!?!**
Technical writers distinguish between different kinds of documentation. The exact number of types varies but is usually somewhere between [four] (https://www.quora.com/What-are-the-different-types-of-technical-documentation/answer/Esteban-Rodriguez-66) and [eight] (https://www.rhyous.com/2011/07/21/the-different-types-of-technical-documentation-for-software-and-why-each-is-important/).

For this post, we will clump together most of the types and only consider two kinds of documentation: the API specification that is part of the source code, and the rest of the guides that we ship with our Ansible collection. Why? Because we can extract the API documentation from the source code. We need to write the rest of the documentation manually.

We will only briefly look at the writing process itself and focus instead on more technical aspects of the documentation authoring:

* What documentation tools do we need to install?
* How can we set up the initial structure of the documentation directory?
* What steps do we need to perform when rendering the documentation?
* How can we publish the collection documentation online?

So let us get right into the bootstrapping process.

#**Tools of the trade**
We will be using two tools to prepare the documentation for our collection: [ansible-doc-extractor] (https://pypi.org/project/ansible-doc-extractor/) for extracting the documentation embedded in Ansible modules, and [Sphinx] (https://www.sphinx-doc.org/) for driving the rendering process.

>You have not heard about `ansible-doc-extractor` yet? It is probably the best invention since sliced bread ;) What does it do? It can extract module API documentation and render it into reStructuredText documents. We created it because we are lazy and need something that allows us to create a web API documentation for [Sensu Go Ansible collection modules] (https://galaxy.ansible.com/sensu/sensu_go). And now you all get to benefit from our laziness ;)

Since both packages are available on [Python package index] (https://pypi.org/), we can install them with a single command:

```
(venv) $ pip install Sphinx ansible-doc-extractor
```

If everything went according to plans, we now have `sphinx-quickstart` and `ansible-doc-extractor` commands available and can start creating the documentation directory structure.

#**The documentation structure**
Our Ansible collection documentation will live in the `docs` top-level directory. This directory is where we will place all of the Sphinx configuration, where our source documentation will live, and where the Sphinx will output the rendered documents.

Once we have created the `docs` directory, we can initialize the Sphinx configuration by running:

```
(venv) $ ( cd docs && sphinx-quickstart )
```

Make sure you answer `y` when asked about whether to separate the `source` and `build` directories. Having those directories split will help us keep the documentation source files tidy and neat. Feel free to answer all other questions to your liking.

Now comes the best part: writing and generating the actual documentation ;)

#**Preparing documentation sources**
If we want our documentation to be of any use for the end-user, we should probably create at least three sections:

* A short quickstart chapter is a must in today’s world where time is money.
* An article or two that describe common scenarios that our Ansible collection is designed to cover.
* A generated module API docs that end-users will use when writing playbooks.

We will need to write the first two sections manually - there is no way around it. But as already said before, we can automate that last part.

Let us assume that we have our API documentation files stored in the `docs/source/modules` directory. Then we can include all of them into our documentation by modifying the table of contents directive in the `docs/source/index.rst` document to:

```
.. toctree::
   :maxdepth: 1
   :caption: Contents:
   :glob:

   modules/*
```

Now, all we need to do is extract the module API documentation and place it inside the `docs/source/modules` directory. And thanks to `ansible-doc-extractor`, this is almost trivial. For example, extracting the API documentation from the `sensu.sensu_go.asset` module is as easy as running:

```
(venv) $ mkdir -p docs/source/modules
(venv) $ ANSIBLE_COLLECTIONS_PATHS=$(pwd)/../../.. \
  ansible-doc-extractor docs/source/modules plugins/modules/asset.py
```

Once the previous command finishes, we should have the documentation for our asset module safely stored in the `docs/source/modules/asset.rst` document.

>You might have noticed that `ANSIBLE_COLLECTIONS_PATHS` variable that we set when running the extraction process. Why do we need it? Because Ansible needs to include Python modules that contain document fragments. And it can only do so if we point it towards the right folder. Oh, by the way, this is why we created all those folders when we cloned the Sensu Go collection.

Now for the grand finale: rendering the documentation and publishing it to the web.

#**Publishing the docs**
We will be using [GitHub Pages] (https://pages.github.com/) to host our documentation for the sake of simplicity. You can, of course, use just about anything that can serve a static web page.

But before we can push our content to the GitHub, we need to render it. Thankfully, the `sphinx-quickstart` program created a Makefile in the `docs` folder that contains all the commands we need. All we need to run is:

```
(venv) $ ( cd docs && make html )
```

If we now open the `docs/build/html/index.html` file with the web browser of our choice, we should see the home page of our documentation:

```
(venv) $ xdg-open docs/build/html/index.html
```

The last thing we need to do is upload the final documentation to the GitHub. If you need help with this, feel free to visit the [GitHub Pages] (https://pages.github.com/) site and browse the documentation. For the rest of you, here is the autopilot version of the first-time publish:

```
(venv) $ git checkout --orphan gh-pages
(venv) $ git rm -rf .
(venv) $ touch .nojekyll
(venv) $ git add .nojekyll
(venv) $ git commit -m "Init gh-pages branch"
(venv) $ git checkout master
(venv) $ git worktree add gh-pages gh-pages
(venv) $ rm -rf docs/build
(venv) $ ( cd docs && make html )
(venv) $ rsync -av docs/build/html/ gh-pages/
(venv) $ cd gh-pages
(venv) $ git commit -am "Update docs for release x.y.z"
(venv) $ git push -u origin gh-pages
(venv) $ cd ..
```

Subsequent documentation updates are substantially more straightforward:

```
(venv) $ rm -rf docs/build
(venv) $ ( cd docs && make html )
(venv) $ rsync -av docs/build/html/ gh-pages/
(venv) $ cd gh-pages
(venv) $ git commit -am "Update docs for release x.y.z"
(venv) $ git push
(venv) $ cd ..
```

You are welcome ;)

#**Parting words**
Some of you might have noticed that the generated documentation currently looks like crap. And we cannot blame you ;) Keep in mind that `ansible-doc-extractor` is in its early stages of evolution. Once we verify the concept, we will kindly ask a designer or two to help us get things in better shape. But until then, enjoy the eye-watering beauty of “vim is all I need”-Esque design ;)

If you need a real-world example of an Ansible collection with documentation machinery all set up, head over to the [Sensu Go Ansible collection] (https://galaxy.ansible.com/sensu/sensu_go) repository and look into the `docs` directory. And as always, feel free to contact us on [Twitter] (https://twitter.com/xlab_si) or [Reddit] (https://www.reddit.com/r/ansible/comments/dlgzbp/documenting_ansible_collections/).

Cheers!