06 July 2020

Antora is a great documentation building solution for Asciidoctor. Amongst its greatest innovations are the standardization of the different resources to build really good docs, and the orchestration of multiple independent repositories into a single published site.

However, it’s fully implements in JavaScript. This is not a bad thing at all, but may present an initial barrier for those used to enjoying Asciidoctor in Ruby or Java. This guide will show how to set up a simple Maven build an Antora site. It’s in fact, an alternative way to run the official install-and-run-quickstart. Spoiler alert, it’s just using a couple of popular Maven plugins ;)

Note
Full example can be found here https://github.com/abelsromero/blog-antora-with-maven.

Antora with Maven…​why?

Antora itself is implemented in JavaScript and uses Node.js to run. But we don’t really need to know about it, installation is easy and normally we interact with Antora’s CLI.

However, if like me…​you come from a Java background and don’t like installing tools globally, this short guide shows how to build an Antora site with existing Maven tools.

This guide does not cover all use cases, at the end there’s a section of further work. But it offers a quickstart. It also offers a self-contained way to setup a reproducible environment and build that could be used for CI environments. What I like to call "just clone & package" processes.

The goal is not retract from using plain Antora but making things easier to start. I hope after getting your feet wet, you’ll give it a try using the stardard toolset.

The process is split in 2 main parts:

  1. Bootstrapping the project from zero.

  2. Configuring maven to install Node and the antora packages. packages is Node terminology for modules or libraries in Java world.

  3. Getting Maven to run the antora cli to build the site.

Bootstrapping the project

This guide uses the install-and-run-quickstart. If we review it we’ll see that the example Antora project is composed of 3 repos

In order to build the site we only need to create a maven project and add the playbook file.

The quickest way is using a maven maven-archetype-quickstart as described in maven-in-five-minutes guide.

$ mvn archetype:generate -DgroupId=org.antora.maven -DartifactId=my-antora-site -DarchetypeArtifactId=maven-archetype-quickstart -DarchetypeVersion=1.4 -DinteractiveMode=false
site:
  title: Antora Demo Site
  # the 404 page and sitemap files only get generated when the url property is set
  url: https://antora.gitlab.io/demo/docs-site
  start_page: component-b::index.adoc
content:
  sources:
    - url: https://gitlab.com/antora/demo/demo-component-a.git
      branches: master
      # setting edit_url to false disables the Edit this Page link for any page that originates from this repository
      # the same thing can be achieved by adding empty credentials (i.e., @) in front of the domain in the URL
      edit_url: false
    - url: https://gitlab.com/antora/demo/demo-component-b.git
      branches: [v2.0, v1.0]
      start_path: docs
ui:
  bundle:
    url: https://gitlab.com/antora/antora-ui-default/-/jobs/artifacts/master/raw/build/ui-bundle.zip?job=bundle-stable
    snapshot: true

And we are done for the first step!

Installing Node, npm and Antora

Now we need the local Node installation and the dependencies. That’s extremely easy using frontend-maven-plugin. This allows running several Javascript tools from Maven, handling also the installation of such tools as well.

All is done inside your project folder, so no need to install anything in your machine. And when you are done, you can delete it.

To setup the installation just add the plugin with two executions. One, to install Node and npm versions you want to use (Antora works better with latest LTS). THe other, to install Antora and its dependencies.

It’s important to keep the order in the pom.xml since both executions run in the same maven phase.

<plugin>
  <groupId>com.github.eirslett</groupId>
  <artifactId>frontend-maven-plugin</artifactId>
  <version>1.10.0</version>
  <executions>
    <execution>
      <id>install node and npm</id>
      <goals>
        <goal>install-node-and-npm</goal>
      </goals>
      <phase>initialize</phase>
      <configuration>
        <nodeVersion>${node.version}</nodeVersion>  (1)
        <npmVersion>${npm.version}</npmVersion>     (2)
      </configuration>
    </execution>
    <execution>
      <id>npm install antora</id>
      <goals>
        <goal>npm</goal>
      </goals>
      <phase>initialize</phase>
      <configuration>
        <arguments>install @antora/cli@${antora.version} @antora/site-generator-default@${antora.version}</arguments> (3)
      </configuration>
    </execution>
  </executions>
</plugin>
  1. Fixed Node version as maven property.

  2. Fixed npm version as maven property. A conventional Node installation includes npm, but here it’s required to it separately.

  3. Npm command to install Antora and the default site generator (aka. style). To install the latest version leave it as install @antora/cli @antora/site-generator-default

Alternatively, for the dependencies installation we can create a npm packages.json descriptor and set <arguments>install</arguments>. But this makes it harder to keep all configuration in a single file.

Important

After a first installation, the file package-lock.json will be created with the exact version of all dependencies (direct and transitive). For the case of Antora is safe to ignore it and not pushing it into your git repo.

Finally, to have a controlled build we can define all versions as properties.

<properties>
  <node.version>v12.18.2</node.version>
  <npm.version>6.14.5</npm.version>
  <antora.version>2.3.3</antora.version>
</properties>

Generating the site

The only thing remaining is building the site. In a normal installation here would just run Antora’s cli.

$ antora antora-playbook.yml

Here, we can use the exec-maven-plugin to run the same command.

<plugin>
  <groupId>org.codehaus.mojo</groupId>
  <artifactId>exec-maven-plugin</artifactId>
  <version>3.0.0</version>
  <executions>
    <execution>
      <goals>
        <goal>exec</goal>
      </goals>
      <phase>compile</phase>
      <configuration>
        <!-- If we don't want to depend on default node installation path we can use -->
        <executable>node/node</executable> (1)
        <arguments>
          <argument>node_modules/.bin/antora</argument> (2)
          <argument>antora-playbook.yml</argument> (3)
        </arguments>
      </configuration>
    </execution>
  </executions>
</plugin>
  1. node binary is installed by default in node folder. To change that, we could use a maven property and set <installDirectory> during the installation execution.

  2. Packages with executable commands like antora are available under .bin directory of the packages installation folder.

  3. Antora playbook descriptor file located at the root of the project.

With that, we complete the configuration! To build the site we only run

$ mvn compile

Some extras

Some small improvements on the guide…​

Running installation on demand

Node artifacts (binaries and packages) are already cached locally and frontend-maven-plugin takes care of it, but we can improve the build time avoiding the installation steps in a normal build. We can wrap the frontend-maven-plugin configuration in a profile, for example called install-deps, and run int under demand with

$ mvn -Pinstall-deps initialize
Note
  • Node distribution zips are cached in $M2_REPO/com/github/eirslett/node/.

  • npm packages, are cache in $HOME/.npm. Which is how npm command normally works.

Cleaning temporal directories

After a full build we will end up with three extra folders in our project node, node_modules and build. If we want to delete all installation and site folders, we just need to set additional filesets to maven-clean-plugin configuration.

<plugin>
  <groupId>org.apache.maven.plugins</groupId>
  <artifactId>maven-clean-plugin</artifactId>
  <version>3.1.0</version>
  <configuration>
    <filesets>
      <fileset>
        <directory>node</directory> (1)
        <followSymlinks>false</followSymlinks>
      </fileset>
      <fileset>
        <directory>node_modules</directory> (2)
        <followSymlinks>false</followSymlinks>
      </fileset>
      <fileset>
        <directory>build</directory> (3)
        <followSymlinks>false</followSymlinks>
      </fileset>
    </filesets>
  </configuration>
</plugin>
  1. Node and npm installation folder

  2. Node packages installation folder

  3. Antora output directory

If we wanted to only delete some when we need to, the same profile approach as before could be applied.

Customizing build directory

Storing the generated site in the root of our project is not very "maven-like". Luckily Antora allows to change that through output options.

Simplest way is to add --to-dir argument to exec plugin, but check the docs to see what other options are there.

<configuration>
  <executable>node/node</executable>
  <arguments>
    <argument>node_modules/.bin/antora</argument>
    <argument>antora-playbook.yml</argument>
    <argument>--to-dir=target/site</argument> (1)
  </arguments>
</configuration>
  1. Set antora output inside’s maven’s target.

Custom maven lyfecycle

Just a side note. A common theme when using maven vs task oriented building tools is that we cannot simply run what we need. Instead, we add plugins to specific phases while all other defaults still run (validations, resources, processing, java compiling, etc.).

In this guide we minimized this by setting the plugins to early phases (initialize, compile), but is not perfect. If we want 100% control we can define a custom maven lifecycle with only what we need. This will require creating an XML descriptor in another module.

Note

I couldn’t find any official reference, so here is a complete example I use as reference from time to time asciidoctor-lifecycle-maven-plugin.

I hope it helps!!

Abel

Currently listening to "Deadhead" by Devin Townsend