Skip to content
Phillip Webb edited this page Mar 29, 2024 · 7 revisions

Overview

Spring Boot 3.3 and later uses Antora to generate documentation. Antora is a site generator that is built on top of Asciidoctor. It provides us the ability to create a single documentation site for multiple different versions of Spring Boot.

Building Local Docs

To build the documentation locally so you can preview it, run the either the full ./gradlew build or the following command:

$ ./gradlew spring-boot-project:spring-boot-docs:antora

Once complete, the documentation will be available under spring-boot-project/spring-boot-docs/build/site.

You can also run the same antora task to generate partial documentation on any module that has an src/docs/antora/antora.yml file. For example, to build just the REST API documentation you can run:

$ ./gradlew spring-boot-project:spring-boot-actuator-autoconfigure:antora

Structure

Antora has three main concepts that can be used to structure documentation.

  1. Components

  2. Modules

  3. Content Sources

Components

Components are collections of documentation created from one or more content sources. Typically a versioned instance of a component created from a branch or tag in a git repository.

Spring Boot has a single component named boot.

Modules

A module is a collection of content in a component version that’s related by some kind of grouping. It is primarily an organization tool for the writer.

Spring Boot has the following modules:

  • ROOT - The root module and navigation

  • tutorial - Tutorials for how to use Spring Boot

  • reference - The main reference documentation

  • how-to - How-to style question and answer documentation

  • build-tool-plugins - General information about the build tool plugins

  • gradle-plugin - Gradle plugin documentation

  • maven-plugin - Maven plugin documentation

  • cli - CLI documentation

  • specification - Specification docs (e.g. the uber jar format)

  • appendix - Appendix information (with a lot of auto-generated content)

  • api - REST API and Javadoc

A lot of inspiration for the modules comes from the Divio Documentation System.

Content Sources

Content from a git repository is located by specifying a content source. Each content source contains an antora.yml file which provides the information Antora needs to create a component.

Spring Boot uses a src/docs/antora directory under our existing Gradle modules as content sources.

The following modules contain Antora sources:

  • spring-boot-docs

  • spring-boot-actuator-autoconfigure

  • spring-boot-gradle-plugin

  • spring-boot-maven-plugin

Playbook Files

The Antora playbook controls what content is included in the documentation site. There is a playbook file used to generate the real site which lists the branches and tags to include. There is also a local playbook file that is generated on-the-fly by Gradle whenever a documentation module is built.

Once generated, the file can be inspected by looking at build/generated/docs/antora-playbook/antora-playbook.yml.

If you need to change the generated content you’ll need to update the GenerateAntoraPlaybook class under buildSrc.

Navigation Files

Navigation files are special .adoc files used to create the navigation tree on the left of the site. Spring Boot composes navigation files from distinct files located with each module.

Navigation files are name nav-<module>.adoc and placed in the partials directory. The main navigation file that includes the partials is at spring-boot-project/spring-boot-docs/src/docs/antora/nav.adoc.

In addition to the main navigation, modules may also include a src/docs/antora/local-nav.adoc file. This file is not used on the final site, but provides a way to generate navigation for a single module so that its documentation can be viewed without generating the entire site.

Collected Content

Antora typically works by generating a documentation site from content contained in a git repository. Using git content alone isn’t sufficient for Spring Boot since some of our documentation is generated automatically during the build.

For example, we generate an appendix containing the configuration properties we support. We also generate link and version attributes from the spring-boot-dependencies module. These can be used in any .adoc file.

Zip Contents Collector

To support contents that is generated during a build, but not committed to git, we use the zip contents collector extension. This extension allows us to publish a zip file to artifactory containing all the generated content required to build the documentation site.

Individual antora.yml files need to declare the zip contents that they expect to be included. Includes are defined using the module name and a classifier.

The classifier is either:

  • aggregate-content - Generated .adoc files etc.

  • catalog-content - Generated HTML (e.g. Javadoc).

Here’s a typical configuration:

ext:
  zip_contents_collector:
    include:
    - name: root
      classifier: aggregate-content
    - name: api
      classifier: catalog-content
      module: api
      destination: content-catalog

During a local build, zip contents are picked up directly from the /build directory.

Note
It is critical that content zips are actually published to Artifactory. If new zips are added, the spring-boot-docs modules will need to be updated to ensure publishing occurs.

Local Zip Content

The main spring-boot-docs site includes zip content that provides an antora.yml file. This file contains generated attributes and additional configuration that Antora needs to generate a correct site.

Because our build is modular, we also need to include this generated content when building other documentation modules. In these cases, we cannot declare the include in the antora.yml file because we only want the include for the local build. We don’t want our main documentation playbook to find these includes and attempt to download them.

To deal with this, special configuration is applied to the generateAntoraPlaybook task.

Here’s a typical example:

tasks.named("generateAntoraPlaybook") {
	alwaysInclude = [name: "actuator-rest-api", classifier: "local-aggregate-content"]
	dependsOn antoraActuatorRestApiLocalAggregateContent
}

In the example above, the local-aggregate-content will be included for the local build but never actually published. The antoraActuatorRestApiLocalAggregateContent task is responsible for creating the zip file. It typically delegates to generateAntoraYml.

The Docs Build

Generating the site during the regular project build is a helpful way of previewing the documentation, but it’s not suitable for publishing the real site. The real documentation site needs to collect content sources from multiple branches and present them in a unified way.

To do this, we have an orphan branch named docs-build. This branch contains a GitHub workflow that builds the real site.

The workflow is run whenever a commit is made to the docs-build branch. It’s also run after after a successful CI build to publish snapshot docs.

Docs are actually published to a spring-boot/antora folder and we have a rewrite rule on the server to make this look like the root folder. Legacy docs are still available at spring-boot/docs.

Xref Extension

By default Antora does not validate that the fragment part of an xref actually exists in the target document. To deal with this, we use the xref extension.

This extension ensure fragments actually exist in the target document. It also allows us to stub out references for content that isn’t yet available.

Stubbing is important due to the modular nature of our build. For example, our Maven plugin documentation may have xrefs to sections in the reference modules. These can only be validated when spring-boot-docs is built. When the spring-boot-maven-plugin is built, we need to stub out these xrefs so they don’t fail.

Stubbing is configure on the generated local playbook file. Here’s a typical Gradle configuration:

tasks.named("generateAntoraPlaybook") {
	xrefStubs = ["appendix:.*", "api:.*", "reference:.*"]
    ...
}

Differences From Asciidoctor

There are few differences from the aciidoctor generation that we used in earlier versions of Spring Boot.

Extensions

The Asciidoctor extensions used in Antora are written in Javascript. This means our previous extensions cannot be used.

A replacement set of extensions have been developed which offer equivalent features. As the underlying code is different, there may be subtle issues when migrating.

Attribute Names

During the migration to Antora we’ve taken the opportunity to follow the best practices for Asciidoctor attribute naming. This means that attributes will be different and may need to change during a forward-merge.

Formatting

Section Headings

Antora .adoc files continue to use our standard formatting rule of three spaces before a section header, however, we now also include a line after the header. This is done to ensure that attributes are always read correctly.

Source Blocks

We no longer need to specify indent=0 in source blocks or indent the code. Most source blocks should now just need [source,<language>]. For example:

[source,xml]
----
<dependencies>
	<dependency>
		<groupId>org.springframework.boot</groupId>
		<artifactId>spring-boot-starter-actuator</artifactId>
	</dependency>
</dependencies>
----

Tabs

We now use standard asciidoctor tabs rather than our own extension. This mean that markup will need to change when forward-merging.

Here some typical tab markup:

[tabs]
======
Maven::
+
[source,shell]
----
$ target/myproject
----
Gradle::
+
[source,shell]
----
$ build/native/nativeCompile/myproject
----
======

Code Imports

We now use @springio/asciidoctor-extensions/include-code-extension to include code fragments.

This uses a slightly different syntax of:

include-code::SomeCode[]

Config Props

This uses a slightly different syntax of:

[configprops,yaml]
----
spring:
  datasource:
    url: jdbc:mysql://localhost/test
    username: dbuser
    password: dbpass
----

Upgrading

Upgrading Dependencies

All version information for Antora and the extensions it uses are declared in antora/package.json. To upgrade to to the latest versions you can do the following:

$ cd antora
$ npx npm-check-updates -u
$ npm install

You should then commit the updated package.json and package-lock.json files.

Upgrades should be applied on the oldest supported branch and merged forwards.

Note
The package.json file in the main branch is also used by the docs-build branch.

Upgrading the UI Bundle URL

The package.json file also stores the UI bundle URL. As with the dependencies, it will also be used by the docs-build branch.

To upgrade, edit the package.json file and change the ui-bundle-url key under config.

Important
The ui-bundle-url should always reference a stable version. Never use latest as it is a moving target development build.
Clone this wiki locally