Antora

Introduction

Like its simpler Markdown cousin, AsciiDoc is a readable markup language that is easy to create in any text editor. As we explained, it is primarily aimed at creating technical content. The Asciidoctor program can transform that content into various output formats amongst which is HTML, the language of the web.

If you have run asciidoctor as shown here then you can open the files in documentation/manual/html/ directory and read the documentation in a browser from there. No plug-in will be required as you are reading HTML rendered nicely by your browser.

However, Asciidoctor is not a web-site builder. So while the styling will be good, the documentation tree is still just that — a simple tree of files without any navigation bar, menus, search box etc. so not a real website. Navigation is limited to the forward and back buttons in your browser.

Antora aims to fill the void. It is a static website generator that creates sites where the content is written in AsciiDoc and where the corresponding files are stored in a version control system like git. It is used to add elements like menus, navigation bars, search boxes, etc. — elements we expect to see when we visit a website.

Antora has been designed to facilitate very large scale technical documentation projects where the content might be spread across many different repos. In fact it can be used to consistently document a complete operating system with thousands of libraries and hundreds of applications. Not only that, its design means it can gracefully handle different versions of each of those through their lifecycle.

This generality is both a blessing and a curse!

While there is copious Antora documentation, it tends to focus on the great flexibility of the system without nearly enough reference to specific simple complete examples. The suggested use cases are quite heavy weight with lots of documentation in different repositories and possibly even a team of people with specialized technical writing roles and so on. Even the Antora Quick-start isn’t all that useful as it relies on existing content pulled from other repositories instead of building a small site from scratch in a single repository. This all makes Antora appear quite daunting.

Of course it is great that the system was designed to scale up to the very large but it is quite hard to get started with a much simpler scenario like documenting our little header-only library GF2++.

Well we do know there are couple of requirements up front:

  • The documentation must be written in AsciiDoc.
    We’ve already talked about the advantages of AsciiDoc as a markup language so we’ll take that as a given.

  • The documentation must be stored somewhere in a git repository.
    Using git to keep track of changes to documentation in the same way it is used to track changes in a code base is generally a good thing but in fact the git repository can be a completely local and even pretty bogus. Assuming you have git installed, from the root directory of your project you can just do:

    git init .
    git commit --allow-empty -m 'Created repository'

That’s it! You have now got a local git repository that Antora will be happy to work with even though you haven’t checked in anything to it.

Sample Project

Let’s focus on a simple scenario where you are building an application FabulousApp whose code base sits in a single repository and which is documented with an installation manual and a user guide.

In broad strokes the FabulousApp repository might be laid out along the following lines:

FabulousApp
├── .git/...                        (1)
├── docs                            (2)
│   ├── installation-manual/...     (3)
│   └── user-guide/...
├── src                             (4)
│   ├── FabulousApp.cpp
│   └── ...
├── README.adoc                     (5)
├── ....                            (6)
1 FabulousApp is a git repository.
2 As is common practice, the documentation for FabulousApp is stored in a docs/ directory at the root of the repo.
3 The installation-manual and user-guide are in their own directories and will have content files written in AsciiDoc.
4 In this example the source code for FabulousApp is in the same repo as its documentation.
5 As an aside we note that GitHub and friends all natively render AsciiDoc so you can use its more powerful format for all those README files!
6 A real project probably has lots of other files and directories for build systems, test cases, deployment logs, etc.

We now want to use Antora to build a website that hosts that documentation in a decent looking, easily navigable website.

In common with most static website generators, Antora expects that the content for a documentation website is laid out in a particular structure of directories and files. Because Antora is designed to potentially handle very large documentation projects that structure might be a bit deeper than you might think strictly necessary.

You can use Antora symlinks to keep the documentation source files in more natural locations closer to the root of the repository. We do that in the sample setup below and also in our own repository.

Antora Modules

There are a couple of Antora concepts that need to be understood to make sense of the Antora’s required documentation layout, namely that of an Antora module and an Antora component.

The first of those is pretty natural — a module is simply any block of documentation that sits together in a single directory.

So in our FabulousApp example there are two modules — the installation manual and the user guide. The content for each module is stored in its own directory — of course there are ways to link and even include content from other modules as needed.

That all seems very unobjectionable. However, in order for Antora to work, the module directories must be structured in a specific manner. In particular, the AsciiDoc content in the module needs to be down one level in the module’s pages/ subdirectory. For example our docs/user-guide/ needs to be laid out like:

FabulousApp
├── ...
├── docs
│   ├── ...
│   └── user-guide
│       ├── pages/...           (1)
│       ├── images/...          (2)
│       ├── ...                 (3)
│       └── nav.adoc            (4)
├── ...
1 All of the AsciiDoc content needs to be in the pages/ subdirectory.
2 Any image files you use in the documentation goes in this sibling images/ subdirectory.
3 There other specifically Antora named subdirectories for other types of content.
4 The nav.adoc is basically an AsciiDoc list that tells Antora how to navigate the content.

The pages/ subdirectory will usually be the main event for the module and will have the bulk of the written content.

Within the pages/ directory you can organize your AsciiDoc files any way you like by creating subdirectories and so on. For example, the user guide might have subdirectories for each chapter.

The pages/ directory can have a a sibling images/ directory to hold the image files you reference in the user-guide. Similarly there might be other sibling directories (specifically examples/, partials/, and attachments/) for other types of content in the module.

The names pages/, images/ etc. are specified by Antora. They are the Antora family directories.

Adhering to this module structure isn’t a huge constraint. While we have introduced a little more depth by using the pages/ subdirectory and so on, the required Antora naming scheme is fortunately not at all offensive or exceptional. Even if we move away from Antora as our website builder it shouldn’t be hard to map this particular module organization onto whatever is required by the replacement.

That final nav.adoc file will consist of AsciiDoc lists and sub-lists that tell Antora how to navigate the content in this module. While building the website Antora will use the nav.adoc file as a template for the sidebar the reader will use to navigate from. The navigation file is a bit more specific to Antora but it is usually really small and there are ways to make it be of some general use.

The navigation file can be called anything (the nav.adoc name is not specified by Antora). Also it need not sit at the root of the module though it generally makes sense to keep it there where the authors can quickly modify it as needed when they add or remove sections from the module.

Antora Components

An Antora component is just a package of modules. It can be any directory that contains two required elements:

modules/

A subdirectory with one or more Antora documentation modules laid out as shown above.
The modules name is mandatory but as we will see below it can just be a symbolic link to a different more convenient location in your repo.

antora.yml

A file that sets some overall configuration/metadata parameters for the Antora component. The name is also mandatory.

Officially the antora.yml file is referred to as the Antora component version descriptor which is a bit of a mouthful for what is usually a pretty small file!

For FabulousApp the antora.yml file might just look like:

name: fabulous-app                          (1)
title: FabulousApp                          (2)
version: latest                             (3)
start_page: user-guide:overview.adoc        (4)
nav:                                        (5)
  - modules/install-manual/nav.adoc         (6)
  - modules/user-guide/nav.adoc
1 The name of the component should be URL friendly so ideally all lower case with limited punctuation, no spaces etc.
2 The title will be what you may see in the website’s UI and it can be anything.
3 Tag for the component version.
4 Need to tell Antora where the starting page is for the component (note you don’t need to specify "pages/").
5 You need to specify how to navigate the modules by telling Antora where each module stores its navigation file.
The component directory is not just restricted to the two required elements and can contain other files and subdirectories.

An Antora component stores all the documentation modules that are naturally versioned, tagged, released, etc., together.

For example, at any given time the bulk of our FabulousApp clients will be using the current stable release of the software and therefore should see the corresponding stable release version of the install manual and user guide. A smaller group of adventurous users might be using the new beta version of the app and they need to see the beta install manual and user guide.

This is precisely a scenario that Antora is designed to deal with. It is the reason it requires that everything should be stored in a git repo in the first place. This is consistent with the idea that we should treat the documentation just like we treat the code base.

Just like FabulousApp, a repo may have multiple documentation modules. However, it will typically only have a single component so everything in the repo (the source code, the whole documentation tree etc.) is versioned at the same time.

By the way, the component directory name can be anything. In an earlier version of this document we actually just plopped all the component requirements straight into the docs/ directory. That works but things got a bit messy — experience shows that it is preferable to create some other directory for the component and other Antora artifacts.

The component idea is Antora specific so we will create it in an antora/ directory at the top level of our repo. Here is the relevant section of the FabulousApp repo with the addition of the antora/ top level directory

FabulousApp
├── ...
├── antora                      (1)
│   ├── antora.yml              (2)
│   └── modules -> ../docs      (3)
├── docs
│   ├── install-manual/...
│   └── user-guide/...
├── ...
1 This is the new directory for the Antora component which can be called anything you like.
2 The component has a required metadata file that must be called antora.yml.
3 The component has a required subdirectory that must be called modules with all the documentation content for the website in module form. Here it is just a symbolic link to the docs/ directory at the top of the repo.
Antora can follow symlinks. This means that the component’s modules/ subdirectory can just be a symbolic link to a more conveniently located documentation tree. So the docs/ directory doesn’t have to introduce another level or get renamed to modules which seems a little too Antora specific.

This is just one possible layout that will keep Antora happy. Lots of variations are possible. As their own documentation site puts it:

Antora employs both convention and configuration to aggregate content and generate your site.

You can see all the gory details about Antora conventions in the Antora documentation.

The Antora UI Bundle

Before we talk more about the process of building the site we should consider what we want the finished product to look like.

Like every other reasonable static website generator, Antora separates the look and feel (the user interface/UI) of the site from its content.

As outlined above, the content is stored in modules that are packaged in one or more components. It is written using the AsciiDoc markup format and stored in files and directories that follow Antora conventions. Each component is stored somewhere in a git repository either locally or out in the cloud, for instance on GitHub, GitLab, BitBucket, etc.

The UI is defined by a separate set of directories and files that are completely independent of that content. The UI holds all the graphical assets, styling directives and so on, that define the look and feel for the documentation website. To be used by Antora the whole UI directory is actually zipped up into what it calls a UI bundle.

The UI bundle is independent of the content and potentially can be reused across multiple sites. In practice, there will be some small changes needed to customize a generic UI for a specific site (perhaps change a logo or the names in a top-level menu dropdown and so on). Antora allows for this by incorporating the concept of a supplemental UI which is a small directory of extra files that get overlaid on the UI bundle. Files in the supplemental UI over-write any matching ones in the main UI bundle. We will see an example below.

Antora’s documentation for the Antora UI and the Antora UI bundle unfortunately is a bit sparse. There is a default UI in GitLab that you can study in all its glory and that does have some documentation.

The default UI has an equivalent UI bundle that you can use in your own documentation website. The good news is it will give you a very decent looking website with nice typography and a modern clean look and feel like this example.

Even if you use the “default” UI bundle you will still need to supply some supplemental UI files to get rid of artifacts that will not make sense for your project.

The Antora Playbook

We have seen that Antora honors the natural division of documentation into modules (the install manual, the user guide and so on). It packages those modules into a component which is just a directory containing the modules and a little metadata configuration file antora.yml. The component will probably live in the same repo as the associated source code and get updated and versioned in sync with that code base.

Larger projects will have lots of repos for different libraries and applications that work together as a system. In typical usage each of those will have their own Antora component to hold the associated documentation.

We need to tell Antora how to configure and build the actual documentation website from the content stored in one or more Antora components. What’s the title of the site, what’s its URL, what’s its landing page, what’s its look and feel, where is it published etc.

All of this is the role of the oddly named Antora playbook. To quote from the documentation an Antora playbook has several purposes:

  • It specifies the global information for the site such as its title and URL.

  • It specifies where to find all the content used in the site.

  • It specifies the home page for the site.

  • It specifies any AsciiDoc attributes or extensions that should be used when processing the content files. (For example, Antora does not support site search out of the box but you can call on an extension to automatically add that feature).

  • It specifies the UI bundle and any supplemental UI files that controls the visual layout, style, and behavior of the website. The playbook crucially is the glue between the content packaged in Antora components and the separate Antora UI bundle.

  • It specifies where and in what format the website should be published. That can be to a local directory or zip archive. It can even be directly to some custom provider that you code up using JavaScript.

  • It specifies any caching used by Antora and how to handle repository updates etc.

Running antora with the a playbook argument pulls in all the documentation components from the various repos the playbook references.

Antora then creates a virtual catalog of all those pages and assets giving each one an Antora ID that takes into account any version information, the name of the owning component and module, as well as the page itself of course. These ID’s are used to generate page URL’s and can also be used also for internal links and cross references. The repository structure for the documentation is never exposed.

In short a playbook.yml file sets the parameters Antora needs to produce a finished website. The file itself can be called anything and it is the key argument for the antora command:

$ antora playbook.yml

Assuming everything goes well that command builds the documentation site.

Building the Site

So where should the playbook be stored and where will it put all the tools and artifacts it uses? Where will it store the actual output website?

Antora suggests that you set up a completely separate repo for this purpose. In our case then you might create a different repo (say FabulousApp-antora) to house the playbook and any tools Antora needs to build FabulousApp’s documentation website.

Why would you introduce this complication?

Our app is simple enough to be stored in a single repo. In a more complicated scenario we might have a whole system with several repos full of the source code for different libraries and applications that work together in some fashion. Each library and application will have its own documentation in some Antora component which is just a directory somewhere in the coresponding repo with an antora.yml configuration file and a modules/ subdirectory in it. Just like above, each module will in turn have its content in a pages/ subdirectory possibly supported by items in other Antora family directories.

In that scenario it’s not clear which of the repos should have the playbook that builds out the entire documentation website. Each repo owning its own documentation component is fine but it seems sensible to keep the master playbook in some separate location — the playbook-repo.

If you go this route the playbook-repo has none of the website source content. Instead it just has all the parameters and tools that make it possible to build the site and pulls in content as needed from the other repos. Any supplemental UI files will also be housed there. In large documentation projects the playbook repo probably isn’t really in the technical writer’s domain but more part of the developer world where they work on the website UI code and the website deployment scripts.

For a small single repo project like ours this is an overkill and there is no reason that the site cannot be built in the same repo that holds the source and documentation content.

So taking our FabulousApp example we might get to a repo structure like the following:

FabulousApp
├── ...
├── antora
│   ├── antora.yml
│   ├── modules -> ../docs
│   ├── playbook.yml            (1)
│   ├── UI
│   │   └── supplemental-ui/... (2)
│   ├── build/...               (3)
│   └── ...                     (4)
├── docs
│   ├── install-manual/...
│   └── user-guide/...
├── src/...
├── README.adoc
└── ...
1 The configuration file for building the site is this playbook.yml file though the name can be anything.
2 Here we are using Antora’s default UI but need to override a few items by using the supplemental-ui feature.
3 We have added a new directory build which is where the site gets built locally. This is probably not part of the git repository as it created on the fly
4 In practice there will be some other files and directories that Antora will use to cache dependencies from run to run.

A very basic playbook.yml file for FabulousApp might look something like the following:

Basic playbook
site:                       (1)
  title: Our Fabulous App
  start_page: fabulous-app:user-guide:introduction.adoc

content:
  sources:
    - url: ..               (2)
      start_path: antora    (3)

ui:                         (4)
  bundle:
    url: https://gitlab.com/antora/antora-ui-default/-/jobs/artifacts/HEAD/raw/build/ui-bundle.zip?job=bundle-stable
  supplemental_files: ./UI/supplemental-ui

output:                     (5)
  dir: ./build
1 This sets the website’s title and landing page which is a page in a module stored in a component.
2 This tells Antora where to put content from. Each url should be a git repository and the start_path key tells Antora where to look for a component from the root of the repo.
3 In this example we are using Antora’s default UI though in practice we will need to override a few things. The UI/supplemental-ui/ directory will have the necessary overrides.
4 This tells Antora to put the finished website into a local subdirectory called build/.
You probably will want to add some things to this playbook — for one thing it is a good idea to tell Antora to do some caching so everything needn’t get rebuilt for every small content change. You also might need to add some extensions that do things like automatically create a search index for your site etc.

You invoke Antora as follows:

$ cd FabulousApp/antora
$ antora playbook.yml

If everything goes well you will get output along the following lines:

Site generation complete!
Open file:///.../antora/build/index.html in a browser to view your site.