At Decodable we migrated our docs platform onto Antora. I wrote previously about my escapades in getting cross-repository authentication working using Private Access Tokens (PAT). These are fine for just a single user, but they’re tied to that user, which isn’t a good practice for deployment in this case.
In this article I’ll show how to use GitHub Apps and Installation Access Tokens (IAT) instead, and go into some detail on how we’ve deployed Antora. Our GitHub repositories are private which makes it extra-gnarly.
Overview 🔗
The docs platform is built on Antora, which generates a static site. This is hosted on Cloudflare Pages.
-
The docs content is on our private
code_repo
GitHub repo. -
The Antora platform configuration and build scripts is held on the private
docs-platform
GitHub repo. -
The user interface of the documentation site is taken mostly from the vanilla antora-ui-default, with some small tweaks in this repo.
GitHub Actions is used to trigger, build, and deploy the docs site.
Why Two Repositories? 🔗
One of the brilliant things that Antora supports is the ability to pull in the docs content from a git repository. This can be the same as the one that is hosting the Antora configuration—or as in our case, the repository that holds the code for which the documentation is written.
This means that when a new feature is released the code and the docs can be kept in lock-step, both deploy and rollback if necessary.
You can also pull in code from multiple repositories—Antora is super-flexible like this, and supports various styles of code organisation and control.
Build and Deploy 🔗
The docs content lives in code_repo
under /docs
. The actual build and deploy happens on the docs-platform
repository, and as a result the process is more complicated than it would be in a single repository.
-
For docs changes, when a change is pushed to
main
oncode_repo
that includes a file under/docs/
, thedocs-trigger-deploy.yaml
workflow runs. This in turn triggers thecloudflare.yaml
workflow on thedocs-platform
repository. -
For a platform change the
cloudflare.yaml
workflow on thedocs-platform
repository will run directly.
PR Preview builds 🔗
Not everyone writes perfect docs PRs. I has ben knowwn to right mistaks with speling and grammer, and that’s before you get onto the correctness of documentation which in software is particularly important to be precise and accurate.
By being able to create a preview of PRs as they are raised it’s easy to seek review from colleagues. However readable Asciidoc might be as a markup language, it’s not going to be more readable than the parsed and rendered web page with its links and images in all their glory. The same goes for PRs against the Antora platform configuration itself—whether customising the skin, tweaking a config—all these things benefit from being able to see them in action before hitting the deploy button.
Thus, when a PR is raised against main
on the docs-platform
or code_repo
repos (and includes a file under /docs/
in the case of the code_repo
repo) a preview docs site is built and deployed to a unique URL.
The build process does some additional things that the production one doesn’t:
-
The trigger action passes details about the source PR as inputs to the
docs-platform
workflow -
The preview-cloudflare.yaml workflow on the
docs-platform
modifies the Antora playbook to use the source branch of the PR on thecode-repo
repository (instead ofmain
)- name: If not a platform PR, set the branch of the source repo for antora content if: github.event_name != 'pull_request' id: override_antora_playbook_yml run: | sed -i '7s/main/${{ inputs.pr_branch }}/' antora-playbook.yml
-
It overlays a message on the preview site indicating the PR with which that it is associated.
-
Once deployed, it updates the originating PR with the URL of the preview site.
Security and Configuration in GitHub Workflows 🔗
There are two areas of security that need to be correctly configured:
-
Cloudflare
-
GitHub Intra-Repository interactions
Cloudflare 🔗
This one is fairly simple. Store these in the docs-platform
repository settings:
-
Repository secret:
CLOUDFLARE_API_TOKEN
-
Repository variable:
CLOUDFLARE_ACCOUNT_ID
The GitHub Action then uses these when deploying the site to Cloudflare.
GitHub Intra-Repository Interactions 🔗
This is more complicated since the two repositories are private. The following interactions need authorisation beyond what happens by default within a GitHub Workflow:
-
Actions running in
code_repo
repo-
Triggering a workflow in
docs-platform
fromcode_repo
workflow
-
-
Actions running in
docs-platform
repo-
When Antora builds the docs site it needs to clone the
code_repo
repository -
Updating the PR issue on
code_repo
that triggered the preview build
-
How It Works 🔗
The auth for these is handled using a custom GitHub App (Antora Docs Build Bot
) installation on each repository.
The auth can also be done using Personal Access Tokens (PAT) but this would then be tied to a particular user’s account and is therefore not suitable for an organisation. |
When each workflow runs its first step is to use the create-github-app-token action to generate a GitHub App installation access token (IAT). This is valid for the session only, and then provides the authorisation for the intra-repository actions.
The IAT is used in two ways:
-
From the github-script action via the optional
github-token
parameter. This is used for two different interactions:-
To trigger the
docs-platform
build and deploy workflows from thecode_repo
repository. -
When the Preview workflow adds a comment to the PR that triggered it. If this PR came from the
docs-platform
repository (i.e. local to the action) then no additional auth is needed, but to comment on thecode_repo
repository it is.
-
-
When Antora builds the site it clones the
code_repo
repository. Since this is run from a different repository the default authentication that would apply to an action running in the same repository doesn’t exist. Antora performs the authentication using the pre-specifiedGIT_CREDENTIALS
environment variable. This must follow the following syntax:https://x-access-token:[email protected]
Setting up the GitHub App 🔗
This needs to be done by a user with Owner
rights on the GitHub organisation. The App has to be created in the GitHub organisation, and from there is installed to the two repositories. The GitHub docs detail the process - below is a short set of notes covering the essential settings:
-
From your GitHub profile page set the
settings context
to that of your organisation, and then click onDeveloper settings
(at the very bottom of the page) and thenGitHub Apps
-
Click on
New GitHub App
.-
Give the new app a name (e.g.
Antora Docs Build Bot
) -
Set the
Homepage URL
to that ofdocs-platform
repo -
Disable
Webhook
-
-
Under
Repository permissions
set the followingActions
Read/Write
Contents
Read
Issues
Read/Write
Metadata
Read
Pull Requests
Read/Write
-
Click
Create GitHub App
-
Make a note of the App ID. You’ll store this later on as a repository secret.
-
Scroll down to
Private keys
and click onGenerate a private key
. Save the resulting.pem
file locally. -
Click
Install App
-
Install it to the account under which the the
docs-platform
andcode_repo
repos are (i.e.decodeableco
). -
When prompted which repositories it should be installed to, select
Only select repositories
and choosedocs-platform
andcode_repo
-
Configuring Repository Secrets and Variables 🔗
As a repo admin, on the code_repo
repository add the following repository secrets:
Key | Value |
---|---|
|
GitHub App ID |
|
The full text of the .pem, including the |
As a repo admin, on the docs-platform
add the following repository secrets
Key | Value |
---|---|
|
GitHub App ID |
|
The full text of the .pem, including the |
|
API token from Cloudflare |
and the following repository variable
Key | Value |
---|---|
|
Cloudflare Account ID |
Addendum: Deploying Antora using AWS Amplify and GitHub Workflows 🔗
For $REASONS
we ended up using AWS Amplify. You can find the build scripts here. There are three scripts:
-
Preview deployment (triggered by a PR creation)
-
Live deployment (triggered by a merge to
main
) -
Teardown preview (triggered by a PR being closed)