Publish to GitHub Pages

Antora is designed to create sites that run anywhere, whether it be on a static web host or the local filesystem. However, some hosts offer “features” that mess with Antora’s output. GitHub Pages is one of those hosts.

Jekyll and underscore files

By default, GitHub Pages runs all files through another static site generator named Jekyll (even if your repository is not set up to use Jekyll). Since Antora already produces a ready-made site, there’s absolutely no need for this step. But it’s more than just the wasted effort.

Jekyll has the nasty side effect of removing all files that begin with an underscore (_). Why is this a problem? By default, Antora puts UI files in a folder named _. It also places images inside the folder named _images. When Jekyll comes through, it wipes out these folders. As a result, you get no UI and no images.

.nojekyll

Fortunately, there’s a way to disable this “feature” of GitHub Pages. The solution is to add a .nojekyll file to the root of the published site (i.e., the output directory configured in your playbook).

The presence of the .nojekyll file at the root of the gh-pages branch tells GitHub Pages not to run the published files through Jekyll. The result is that your Antora-made site will work as expected.

Let’s look at two ways to create the .nojekyll file when you run Antora.

Touch the file manually

One way to add this file is to touch the .nojekyll file in the output directory after Antora runs, but before committing the files to GitHub Pages. For example:

$ touch build/site/.nojekyll

Fortunately, there’s way to do this without having to run a separate command.

Use the supplemental UI

To avoid the need for the extra command, the other way to do it is to inject the file using Antora’s supplemental UI feature. To do so, add the following supplemental_files block under the ui category in your playbook file:

Example 1. antora-playbook.yml that adds .nojekyll file using supplemental UI
ui:
  bundle:
    url: <url-of-bundle-goes-here>
  supplemental_files:
  - path: ui.yml
    contents: |
      static_files:
      - .nojekyll
  - path: .nojekyll

This configuration defines files from memory. The first file, ui.yml, tells Antora which files to promote to the root of the site (outside the UI folder) using the static_files key. The second file, .nojekyll, gets written to the root of the published site. Since the contents key is absent, Antora will create an empty file (the equivalent of the touch command from above).

Using GitHub Actions

If your playbook repository is hosted on GitHub, you can configure a GitHub Actions workflow to build and publish your site to GitHub Pages. The benefit of using GitHub Actions is two-fold. First, you don’t have to worry about copying the published files to the gh-pages branch for publishing. Second, you don’t have to worry about the .nojekyll file since the action handles it for you. Let’s get started!

Example 2 shows an example of a GitHub Actions workflow that uses the latest stable release of Antora to build and publish your site to GitHub Pages. This workflow assumes that the name of the default branch of your playbook repository is main, that the name of your playbook file is antora-playbook.yml, and that Antora is configured to publish the files to the build/site directory. If your site uses different settings, you’ll need to update the values in the workflow file accordingly.

Example 2. .github/workflows/publish.yml builds the Antora site and deploys it to GitHub Pages using GitHub Actions
name: Publish to GitHub Pages
on:
  push:
    branches: [main]
  # Allows you to run this workflow manually from the Actions tab
  workflow_dispatch:
concurrency:
  group: github-pages
  cancel-in-progress: false
# Sets permissions of the GITHUB_TOKEN to allow deployment to GitHub Pages
permissions:
  contents: read
  pages: write
  id-token: write
jobs:
  build:
    runs-on: ubuntu-latest
    environment:
      name: github-pages
      url: ${{ steps.deployment.outputs.page_url }}
    steps:
    - name: Checkout repository
      uses: actions/checkout@v4
    - name: Configure Pages
      uses: actions/configure-pages@v3
    - name: Install Node.js
      uses: actions/setup-node@v4
      with:
        node-version: '18'
    - name: Install Antora
      run: npm i antora
    - name: Generate Site
      run: npx antora antora-playbook.yml
    - name: Upload Artifacts
      uses: actions/upload-pages-artifact@v2
      with:
        path: build/site
    - name: Deploy to GitHub Pages
      id: deployment
      uses: actions/deploy-pages@v2

As Example 2 shows, you can install and invoke Antora directly from the workflow. This workflow installs a specific release of Antora (both the CLI and site generator packages) and then uses the Antora CLI in the workflow. The workflow then uses the peaceiris/actions-gh-pages action to publish the site, along with the required .nojekyll file, to GitHub Pages.

To install and use a different version of Antora, append a version to the package name, such as antora@3.0.3.

Let’s now take this a step further by adding the Antora Lunr Extension to incorporate a search widget in the built site. First, you need to update your playbook repository and UI to meet the minimum requirements of the Antora Lunr Extension as described in the README for that project. Once that’s done, return to the GitHub Actions workflow and configure it to install the extension at the same time it installs Antora. The result is shown in Example 3.

Example 3. .github/workflows/publish.yml includes the Antora Lunr Extension
name: Publish to GitHub Pages with Lunr Search Extension
on:
  push:
    branches: [main]
  # Allows you to run this workflow manually from the Actions tab
  workflow_dispatch:
concurrency:
  group: github-pages
  cancel-in-progress: false
# Sets permissions of the GITHUB_TOKEN to allow deployment to GitHub Pages
permissions:
  contents: read
  pages: write
  id-token: write
jobs:
  build:
    runs-on: ubuntu-latest
    environment:
      name: github-pages
      url: ${{ steps.deployment.outputs.page_url }}
    steps:
    - name: Checkout repository
      uses: actions/checkout@v4
    - name: Configure Pages
      uses: actions/configure-pages@v3
    - name: Install Node.js
      uses: actions/setup-node@v4
      with:
        node-version: '18'
    - name: Install Antora with the Antora Lunr Extension
      run: npm i antora @antora/lunr-extension
    - name: Generate Site
      run: npx antora antora-playbook.yml
    - name: Upload Artifacts
      uses: actions/upload-pages-artifact@v2
      with:
        path: build/site
    - name: Deploy to GitHub Pages
      id: deployment
      uses: actions/deploy-pages@v2

To install and use a different version of the Antora Lunr Extension, append a version to the package name, such as @antora/lunr-extension@1.0.0-alpha.5.

Using a custom domain

You can serve your site from a different URL than the default <username>.github.io using a Github Pages custom domain.

If you’re publishing your site Using GitHub Actions as the source, then you can configure your custom domain from the Settings  Pages configuration screen in the GitHub web interface. No other action is required in the CI workflow.

On the other hand, if you’re publishing your site from a branch (e.g., gh-pages), then you need to write the custom domain to a CNAME file at the root of the site. In order to do so, add the following step to your GitHub Actions workflow file (or the equivalent for whatever CI pipeline you’re using) after running Antora.

- name: Create CNAME file
  run: echo my-domain-name.com > build/site/CNAME

Alternately, you can use a supplemental UI to add the CNAME file, similar to how the .nojekyll file was added earlier.

Example 4. antora-playbook.yml that adds CNAME file using supplemental UI
ui:
  bundle:
    url: <url-of-bundle-goes-here>
  supplemental_files:
  - path: ui.yml
    contents: |
      static_files:
      - CNAME
  - path: CNAME
    contents: |
      my-domain-name.com

This configuration defines files from memory. The first file, ui.yml, tells Antora which files to promote to the root of the site (outside the UI folder) using the static_files key. The second file, CNAME, gets written to the root of the published site and contains the custom domain name, as required by GitHub Pages when publishing from a branch. The benefit of this approach is that it does not require an extra step in the CI workflow.

Refer to Managing a custom domain for your GitHub Pages site in the GitHub documentation for more information about using a custom domain and how to activate it.