Antora 備忘録

## 導入

皆さんの好きなマークアップ言語は何ですか？
私はAsciiDocが好きなのですが、普段はMarkdown対応したwikiサービスを使っています。特に不便は感じていませんが、やはり AsciiDoc の表現力を知っていると、時々物足りなさを感じる場面もあります。特に Markdown、[CommonMark](https://spec.commonmark.org/0.29/)において、数式を想定した仕様がまとめられていない点は気になってしまいます。このような理由から、AsciiDoc に対応した wiki サービスの台頭を望む声も少なくないはずです。(当社調べ)

今回はAsciiDoc好きのための静的サイトジェネレータである[Antora](https://antora.org/)について、ドキュメントを一読したので要約をまとめます。最初に注意点として、私はAntoraの開発に関与していない全くの素人なので、雰囲気を知る程度に読んでいただければ幸いです。

### 記事の目的

この記事は私自身の知識整理のために作成するとともに、AsciiDoc 好きな方々へ Antora を布教することを目的としています。Antora の導入は多少ハードルがある、と個人的に感じました。少なくとも docs を流し読みしただけで期待した動作を得ることは難しいと思います。「使いたいけど docs をガッツリ読んで architecture を把握する時間がない」という方を念頭に記事を作成しています。

したがってこの記事では、AsciiDoc の記法であったり、AsciiDoc と Markdown の違いであったり、npm 環境についてであったりは記述しません。また文章量の問題から、Antora におけるバージョン/navigation file の扱い、UI についても記載しません。

## Antoraの特徴

公式サイトにも記載されるように Antora は、AsciiDoc 好きのための静的サイトジェネレータです。"for tech writers"とあるように、ライブラリの docs ページによくある、バージョン毎に記事を管理する、といった高級な事もできるとのこと(私は確認してない)です。もちろん、個人ページの運用にも利用できます。私が特に好ましく思っているのは次の点です。

- No flavor lock-in

例えばMarkdownにおける数式記述の構文がよい例かと思います。Markdownではその仕様が定まっていないため、サービスによって記法が若干異なったりします。互換性を意識した設計思想は心強いのではないでしょうか。

## Antora の簡単な例

### 環境設定

```shell
mkdir test_page
cd test_page
git init
npm init
npm install @antora/cli@2.2 @antora/site-generator-default@2.2 # 2020/02/04時点の最新です。公式サイトから最新版を確認ください。
```

次に `touch antora-playbook.yml` して内容を次のようにします。

```yaml:antora-playbook.yml
site:
  title: てすとページ
  url: http://localhost/
  start_page: home::index.adoc
content:
  sources:
    - url: .
      start_path: docs
ui:
  bundle:
    url: https://gitlab.com/antora/antora-ui-default/-/jobs/artifacts/master/raw/build/ui-bundle.zip?job=bundle-stable
output:
  dir: ./public
```

上記において `site>title` と `site>url` 、 `output>dir` は変更しても問題ありません。しかし `site>content>sources>url` や `site>content>sources>start_path` などは、architecture を把握してからいじることを奨めます。

`touch .gitignore` で以下を記述しておきましょう。

```shell:.gitignore
public/
node_modules/
```

### 記事追加

`mkdir -p docs/modules/ROOT/pages; touch docs/modules/ROOT/pages/index.adoc` して動作確認のための記事を書きましょう。

```asciidoc:docs/modules/ROOT/pages/index.adoc
= Top Page

== 見出し

ほげほげ。
[source,shell]
----
echo "hoge"
----
```

後述の Architecture で触れますが、 `antora.yml` という重要な役割を有するファイルを作成します。
`touch docs/antora.yml` の中身を次のように編集します。

```yaml:docs/antora.yml
name: home
version: master
```

ここまでで次のようなディレクトリ構成となっています。

```shell
.
|-- antora-playbook.yml
|-- docs
|   |-- antora.yml
|   `-- modules
|       `-- ROOT
|           `-- pages
|               `-- index.adoc
|-- package-lock.json
`-- package.json
```

### サイト生成

以上で最低限必要なファイルの準備が終わりました。次のコマンドを実行して `public/` 以下に先程の `index.adoc` に対応した html ファイルを生成します。

```shell
git add .
git commit -m "updated" # 更新をcommitしないとantoraにファイルの存在が認識されないようです。詳しい仕様は把握していないです。
./node_modules/.bin/antora antora-playbook.yml
```

これで`public/home/index.html` が生成されていれば成功です。ブラウザで確認してみてください。

## Antora の Architecture

先程の簡易例には、いくつかの YAML ファイルや意味深なディレクトリ名がありました。ここではこれらの役割を説明することで、サイト構成の理解を促します。主に次の用語の役割が明確になることを意識して説明を進めます。

- component descriptor
- documentation component
- module
- family
- page identifier
- playbook

### Module: 共通のテーマを有する記事や資料のまとまり

wiki のようなものを構成する際、似た内容の記事は一箇所に集めて管理する、というのは一般的と思います。そして記事を作成する場合、現代ではテキストのみとは限らず、画像・音声・動画を埋め込むこともあるかと思います。テーマを共有する複数の記事、そして記事作成に必要な各種資料の集まりのことを **Module** と呼びます。先程の簡易例において、 `ROOT` ディレクトリはこの Module の一種です。

Module 内において、音声・画像・動画・テキストデータを同一ディレクトリに保存するのは好ましくありません。音声データだけのディレクトリ、テキストデータだけのディレクトリ、といった具合に分けて管理するのが自然でしょう。Module における同一種類データを内包するディレクトリのことを、 **Family** と呼びます。簡易例における `pages` ディレクトリはこの Family の一種で、adoc ファイルをまとめる場所として扱われます。簡易例ではテキストファイルしか使わなかったため pages しか現れませんでしたが他にも、画像を格納する `images` 、 zip ファイルや pdf ファイルのような添付ファイルを保存する `attachments` などもFamily subdirectory として扱われます。

ここまでで Module という用語の導入を行い、これが共通のテーマを有する記事のまとまりであることを説明しました。であればディレクトリ名を見ただけで、そのディレクトリ以下にどのようなテーマの記事が格納されているのかわかるほうがありがたいです。ROOT 以外のディレクトリ名を与えられた、ROOT ディレクトリと同階層にあるディレクトリを **named module** と呼びます。つまり、Module は 2 種類に大別することができ、ROOT module と、それ以外の named module があります。ROOT module と named module の使い分けについては documentation component と絡むので後述します。

以下の例において、 `ThemeA` が named module に相当します。

```
.
|-- ROOT
|   |-- attachments
|   |-- images
|   |   `-- fig.svg
|   `-- pages
|       `-- index.adoc
`-- ThemeA
    |-- images
    `-- pages
        |-- article1.adoc
        `-- index.adoc
```

### Documentation Component: メタ情報を共有する module のまとまり

Documentation Component とは、端的に言えば module のまとまりを指します。まとめられた module においては記事の version、まとまりの名前(name)などのメタ情報を共有しています。以降では単に **Component** と呼ぶこともあります。具体的にどのような単位で module を分けるか、component を分けるべきかは公式 docs にも記載ありませんが、[例として、1 つの商品のドキュメントを 1 つの documentation component として表す](https://docs.antora.org/antora/2.2/component-structure/)と記述されています。

```shell
compA
|-- antora.yml
`-- modules
    |-- ROOT
    |   |-- attachments
    |   |-- images
    |   |   `-- fig.svg
    |   `-- pages
    |       `-- index.adoc
    `-- ThemeA
        |-- images
        `-- pages
            |-- article1.adoc
            `-- index.adoc
```

上記のディレクトリ構造において、`antora.yml` と `modules` を含むディレクトリが Documentation Component となります。後述しますが、 **compAというディレクトリ名は重要ではありません。 antora.ymlの name key に設定された値が、この Documentation Component の名前になります。** Documentation Component を構成する必須条件は以下の4つです。

- component descriptor file (antora.yml) を含む
- modules directory を含む
- modules directory に少なくとも 1 つの module を含む
- module に少なくとも 1 つの Family を含む

#### Component Descriptor file: Documentation Component の存在を Antora へ伝えるファイル

- `antora.yml` と名付けられた、
- YAML で記述され、
- 少なくとも `name, version` key とその値が設定されており、
- modules directory と同階層に配置されるファイル

の事を **Component Descriptor file** と呼びます。この記事では簡単のために、Component Descriptor file のことを antora.yml と呼んだりします。`antora.yml` の役割は 2 つ、(i) Antora へ Component の存在を伝えること、(ii) Component の名前やバージョン、navigation file の存在といった情報を保持することです。

```shell:antora.yml
name: home
version: v1.0
nav:
  - modules/ROOT/nav.adoc
```

上記は antora.yml の一例です。この antora.yml 以下のディレクトリで構成される Component について、(i) その名前が `home` であること、(ii) バージョンが `v1.0` であること、(iii) navigation file が `modules/ROOT/nav.adoc` に存在すること、がわかります。nav.adoc やバージョンの詳細について本記事では解説しません。

### Page Identifier: ユニークに記事を特定するための ID

記事を構成するどの資料も、何らかの Family に属し、その Family はいずれかの Module 内に配置され、Module は Documentation Component で束ねられていることをここまでで説明しました。したがってすべての adoc ファイルはこれらによって一意に決定されます。これを **Page Identifier (PageID)** と呼びます。この PageID は、ある adoc ファイルにおいて別の adoc ファイルへのリンクを作成する時などに利用されます。PageID は次の書式に従います。

```shell
version@component:module:path/to/adocfile
```

ここで `path/to/adocfile` の部分には該当 module 内の pages ディレクトリからのパスを記載します。

```shell
compA
|-- antora.yml
`-- modules
    `-- ROOT
        `-- pages
            |-- index.adoc
            `-- topic1
                `-- article1.adoc
```

上記の例において、antora.yml に記載された comp name を `home` 、version を `v1.0` とした場合、`index.adoc` の PageID は `v1.0@home:ROOT:index.adoc` となり、 `article1.adoc` の PageID は `v1.0@home:ROOT:topic1/article1.adoc` となります。ある adoc ファイル内で別の adoc ファイルへのリンクを張る際は

```shell
xref:version@component:module:path/to/adocfile[リンクテキスト]
```

という形で記述します。しかし毎回このような ID を記述するのは煩雑であるため、 **同一 module の記事へアクセスする際の簡略記法** が用意されており、以下の形で記述できます。

```shell
xref:path/to/adocfile[リンクテキスト]
```

### Playbook: サイトの構成を記述する設定ファイル

前述までの内容は、Antora では記事をどのように格納し、ユニークに特定するかを記述していました。ここでは最後に、サイト全体の構成情報を扱う設定ファイルについて記述します。

Antora はサイトジェネレータですから、最終的には HTML への出力を想定します。したがって、(i) base URL は何なのか、(ii) サイトのタイトルは何か、(iii) UI は何を用いるか、(iv) そもそも documentation component はどこにあるのか、などの情報を Antora へ伝える必要があります。

上記のようなサイト全体の情報や、サイト構成に必要な情報をまとめた設定ファイルを **Playbook** と呼びます。前述の簡易例における `antora-playbook.yml` がこれにあたります。様々な情報を扱うことができキリがないため、ここでは簡易例における `content>sources>{url,start_path}` に触れます。

#### `content>sources>url`: Document Component (Git) Repository の場所の明記

`content>sources>{url, start_path}` key で Documentation Component を含むリポジトリの場所を明記します。公式 docs に記載されておらず、ソースコードも読んでいない私の意見ですが、ここでいう Repository は Git Repository を指すと思われます。というのも、単に Documentation Component の要件を満たすディレクトリを指定しても、エラーが生じて処理が進まなかったためです。上記簡易例のようにローカル環境のパスでリポジトリを指定することも、公式 docs のように URL でリポジトリを指定することも可能です。

`start_path` key ではそのリポジトリにおける、Documentation Component のディレクトリパスを指定します。このような仕様になっているのは、例えばあるライブラリのソースコードを含むリポジトリの一部として、Component が含まれるような管理状況を想定しているためと思われます。前述の簡易例の場合、Component の要件を満たすのは `docs` ディレクトリであるため、**antora-playbook.yml から docs への相対パス** が記述されています。

## まとめ

Architecture 理解に必要な要素を詰め込んだ結果、わかりにくい記事になってしまいました。布教の目的もあるので、今後ブラッシュアップしていく予定です。この記事を読んで AsciiDoc や Antora に触れる人が 1 人でも増えればうれしい限りです。

本記事で触れることができなかったUI関連について知りたい方は、[Antora Default UI](https://gitlab.com/antora/antora-ui-default/blob/master/docs/modules/ROOT/pages/index.adoc)あたりの資料を読むことをお薦めします。

## 追記: 2023/03/16

上記に記載の内容は，追記時点でも有用だと確認しました．またホームページ全体の構成については，まとめに若干触れたとおり，[Antora Default UI](https://gitlab.com/antora/antora-ui-default)をforkし修正することで変えることができます．[公式でもページのPlaybook](https://gitlab.com/antora/demo/docs-site)ではurl部分に，CIによるartifactのURLを指定していますが，作成過程ではローカルパスを渡しても機能しますので，そういった利便性は確保されています．