This post continues my series on GitHub Actions workflows for Java projects. I maintain a few open source Java libraries that I've developed from my research projects. I use GitHub Actions to automate a variety of tasks. I regularly release updates to these libraries to the Maven Central Repository, GitHub Packages, and JitPack. I have a pair of GitHub workflows that automate the deployment process. One of these deploys the library's artifacts itself, and was covered in an earlier DEV post: Deploying Java Artifacts to Multiple Maven Repositories with GitHub Actions. Additionally, I have a workflow that deploys the API documentation of the library to a website hosted on GitHub Pages. My documentation workflow is the subject of this post, and includes steps for generating javadoc documentation, post-processing the output of javadoc to inject canonical links and a few other things into the head of the pages, updating an XML sitemap, and deploying to GitHub Pages.
Table of Contents:
- Workflow Step by Step walks you through my documentation deployment workflow one step at a time.
- Complete Workflow.
- Live Example from one of my projects on GitHub.
- Tools Used in the Workflow.
- Where You Can Find Me.
Workflow Step by Step
I'm going to walk through the full workflow from beginning to end.
Workflow Events
I've designed the workflow to run on several different events, although the specific behavior of the workflow depends on the type of event that triggered it to run:
- It runs on
push
andpull_request
events to the default branch, but for those events the workflow only verifies that the documentation builds without errors or warnings, and does not deploy. I want to keep the documentation website up to date with the latest release, so the deployment related steps are skipped duringpush
andpull_request
events, which you'll see later in the workflow. For pull requests, this workflow is also used as a required check. If the documentation doesn't build without errors or warnings, then this workflow will block the merge. - It also runs on
release
events, specifically when a release is created, in which case the full workflow runs, including the steps related to deployment. - I've also configured it to run on
workflow_dispatch
events so that I have the option to run it manually if necessary, such as to force out an important documentation update that applies to the current release.
name: docs
on:
push:
branches: [ master ]
paths: [ '**.java', '.github/workflows/docs.yml' ]
pull_request:
branches: [ master ]
release:
types: [created]
workflow_dispatch:
Checking Out the Repository
This step is self-explanatory, and is found at the start of most GitHub workflows.
jobs:
docs:
runs-on: ubuntu-latest
steps:
- name: Checkout the repo
uses: actions/checkout@v3
Checking Out the gh-pages
Branch
My documentation deployment workflow includes a second actions/checkout
step. The documentation website is hosted on GitHub Pages, and is served from the gh-pages
branch of the repository. This step checks out the gh-pages
branch to a directory that I've also named gh-pages
(see the path
input to actions/checkout
) nested within the first checkout. One of the later steps of the workflow uses a GitHub Action that I've implemented to generate an XML sitemap for the documentation site. That action uses the commit dates of the files to generate <lastmod>
tags in the sitemap. So the step to checkout the gh-pages
branch uses the input fetch-depth: 0
of actions/checkout
to include the complete git history of the gh-pages
branch.
- name: Checkout the gh-pages branch
uses: actions/checkout@v3
with:
fetch-depth: 0
ref: gh-pages
path: gh-pages
Build the Documentation with Javadoc
Below you see the next two workflow steps. First, I set up the Adoptium distribution of JDK 17 with the actions/setup-java
action. This project uses Maven for builds, so after setting up Java, I run javadoc with mvn compile javadoc:javadoc
.
- name: Set up JDK 17
uses: actions/setup-java@v3
with:
distribution: 'adopt'
java-version: '17'
- name: Build docs with Maven
run: mvn compile javadoc:javadoc
Copy the Documentation to Website Location
This next step is conditional, and only runs on release
and workflow_dispatch
events. Recall that the workflow also runs on push
and pull_request
events to verify that the documentation builds successfully. By using a conditional step here, I ensure that the documentation is only deployed to the website when a release is created to keep the documentation site up to date with the current release. The next several steps are actually conditional as well for the same reason.
Also recall that earlier the gh-pages
branch was checked out to a directory gh-pages
by a second use of actions/checkout
. At the root of that branch I have a webpage (in AMP HTML) about the library, as well as a few other things, such as a sitemap.xml, a robots.txt, etc. The javadoc documentation is in a subdirectory api
. So below the first thing I do is delete the contents of the api
directory with rm -rf gh-pages/api
. The purpose is to avoid accidentally leaving any stray obsolete files (e.g., perhaps the release removed a class). The cp -rf target/site/apidocs/. gh-pages/api
then copies the javadocs from Maven's default location to the api
directory of the gh-pages
branch.
- name: Copy to Documentation Website Location
if: ${{ github.event_name == 'release' || github.event_name == 'workflow_dispatch' }}
run: |
rm -rf gh-pages/api
cp -rf target/site/apidocs/. gh-pages/api
Post-Process the Javadoc Output
The next step uses a GitHub Action that I've implemented javadoc-cleanup to insert canonical URLs into the head of each javadoc page. I also use it to insert a referrer policy of strict-origin-when-cross-origin
into the head of each javadoc page, as well as links to my project's favicon. This step is conditional, like the previous step, and only runs on release
and workflow_dispatch
events.
I have a recent DEV post about javadoc-cleanup if you want to learn more about what you can use it for, and how to use it:
Post-Process Javadoc-Generated Documentation in GitHub Actions Before Deploying to the Web
Vincent A. Cicirello ・ Nov 16 '22
- name: Tidy up the javadocs
if: ${{ github.event_name == 'release' || github.event_name == 'workflow_dispatch' }}
id: tidy
uses: cicirello/javadoc-cleanup@v1
with:
base-url-path: https://chips-n-salsa.cicirello.org/
path-to-root: gh-pages
user-defined-block: |
<meta name="referrer" content="strict-origin-when-cross-origin">
<link rel="icon" href="/images/favicon48.png" sizes="16x16 32x32 48x48" type="image/png">
<link rel="icon" href="/images/favicon96.png" sizes="96x96" type="image/png">
<link rel="icon" href="/images/favicon144.png" sizes="144x144" type="image/png">
<link rel="icon" href="/images/favicon192.png" sizes="192x192" type="image/png">
<link rel="icon" href="/images/favicon384.png" sizes="384x384" type="image/png">
- name: Log javadoc-cleanup output
if: ${{ github.event_name == 'release' || github.event_name == 'workflow_dispatch' }}
run: |
echo "modified-count = ${{ steps.tidy.outputs.modified-count }}"
Commit the Documentation
Just like the prior steps, this next only runs on release
and workflow_dispatch
events. Specifically, I use a simple shell script to commit the javadoc documentation to the gh-pages
branch. The cd gh-pages
ensures I'm in the directory gh-pages
where I've checked out that branch. I do the commit as the GitHub Actions bot. Note that this step doesn't push yet.
- name: Commit documentation changes without pushing yet
if: ${{ github.event_name == 'release' || github.event_name == 'workflow_dispatch' }}
run: |
cd gh-pages
if [[ `git status --porcelain` ]]; then
git config --global user.name 'github-actions'
git config --global user.email '41898282+github-actions[bot]@users.noreply.github.com'
git add -A
git commit -m "Automated API website updates."
fi
cd ..
The reason I didn't push yet is because in the next step you'll see how I generate a sitemap. This step committing the javadocs is necessary first because the sitemap generation relies on the commit dates to generate <lastmod>
tags.
Generate an XML Sitemap
On release
and workflow_dispatch
events, use the generate-sitemap GitHub Action, which I maintain, to generate an XML sitemap for the documentation website. The first step below generates the sitemap, and the second one below just logs some information about that action's run to the workflow run's log (e.g., number of URLs in the sitemap, and number excluded by either robots.txt or noindex
directives).
- name: Generate the sitemap
if: ${{ github.event_name == 'release' || github.event_name == 'workflow_dispatch' }}
id: sitemap
uses: cicirello/generate-sitemap@v1
with:
base-url-path: https://chips-n-salsa.cicirello.org/
path-to-root: gh-pages
- name: Output stats
if: ${{ github.event_name == 'release' || github.event_name == 'workflow_dispatch' }}
run: |
echo "sitemap-path = ${{ steps.sitemap.outputs.sitemap-path }}"
echo "url-count = ${{ steps.sitemap.outputs.url-count }}"
echo "excluded-count = ${{ steps.sitemap.outputs.excluded-count }}"
I have a recent DEV post about generate-sitemap if you want to learn more about it:
Generate an XML Sitemap for a Static Website in GitHub Actions
Vincent A. Cicirello ・ Nov 23 '22
Commit the Sitemap and Push to Website
Finally, we can commit the sitemap generated above to the gh-pages
, by switching to the gh-pages
directory where it is checked out, committing, and then pushing everything.
- name: Commit documentation website sitemap and push all commits
if: ${{ github.event_name == 'release' || github.event_name == 'workflow_dispatch' }}
run: |
cd gh-pages
if [[ `git status --porcelain` ]]; then
git config --global user.name 'github-actions'
git config --global user.email '41898282+github-actions[bot]@users.noreply.github.com'
git add -A
git commit -m "Automated API website sitemap update."
fi
git push
cd ..
The updated documentation has now been deployed to the project's website.
Complete Workflow
Here's my complete Java library documentation deployment workflow.
name: docs
on:
push:
branches: [ master ]
paths: [ '**.java', '.github/workflows/docs.yml' ]
pull_request:
branches: [ master ]
release:
types: [created]
workflow_dispatch:
jobs:
docs:
runs-on: ubuntu-latest
steps:
- name: Checkout the repo
uses: actions/checkout@v3
- name: Checkout the gh-pages branch
uses: actions/checkout@v3
with:
fetch-depth: 0
ref: gh-pages
path: gh-pages
- name: Set up JDK 17
uses: actions/setup-java@v3
with:
distribution: 'adopt'
java-version: '17'
- name: Build docs with Maven
run: mvn compile javadoc:javadoc
- name: Copy to Documentation Website Location
if: ${{ github.event_name == 'release' || github.event_name == 'workflow_dispatch' }}
run: |
rm -rf gh-pages/api
cp -rf target/site/apidocs/. gh-pages/api
- name: Tidy up the javadocs
if: ${{ github.event_name == 'release' || github.event_name == 'workflow_dispatch' }}
id: tidy
uses: cicirello/javadoc-cleanup@v1
with:
base-url-path: https://chips-n-salsa.cicirello.org/
path-to-root: gh-pages
user-defined-block: |
<meta name="referrer" content="strict-origin-when-cross-origin">
<link rel="icon" href="/images/favicon48.png" sizes="16x16 32x32 48x48" type="image/png">
<link rel="icon" href="/images/favicon96.png" sizes="96x96" type="image/png">
<link rel="icon" href="/images/favicon144.png" sizes="144x144" type="image/png">
<link rel="icon" href="/images/favicon192.png" sizes="192x192" type="image/png">
<link rel="icon" href="/images/favicon384.png" sizes="384x384" type="image/png">
- name: Log javadoc-cleanup output
if: ${{ github.event_name == 'release' || github.event_name == 'workflow_dispatch' }}
run: |
echo "modified-count = ${{ steps.tidy.outputs.modified-count }}"
- name: Commit documentation changes without pushing yet
if: ${{ github.event_name == 'release' || github.event_name == 'workflow_dispatch' }}
run: |
cd gh-pages
if [[ `git status --porcelain` ]]; then
git config --global user.name 'github-actions'
git config --global user.email '41898282+github-actions[bot]@users.noreply.github.com'
git add -A
git commit -m "Automated API website updates."
fi
cd ..
- name: Generate the sitemap
if: ${{ github.event_name == 'release' || github.event_name == 'workflow_dispatch' }}
id: sitemap
uses: cicirello/generate-sitemap@v1
with:
base-url-path: https://chips-n-salsa.cicirello.org/
path-to-root: gh-pages
- name: Output stats
if: ${{ github.event_name == 'release' || github.event_name == 'workflow_dispatch' }}
run: |
echo "sitemap-path = ${{ steps.sitemap.outputs.sitemap-path }}"
echo "url-count = ${{ steps.sitemap.outputs.url-count }}"
echo "excluded-count = ${{ steps.sitemap.outputs.excluded-count }}"
- name: Commit documentation website sitemap and push all commits
if: ${{ github.event_name == 'release' || github.event_name == 'workflow_dispatch' }}
run: |
cd gh-pages
if [[ `git status --porcelain` ]]; then
git config --global user.name 'github-actions'
git config --global user.email '41898282+github-actions[bot]@users.noreply.github.com'
git add -A
git commit -m "Automated API website sitemap update."
fi
git push
cd ..
Live Example
To see a live example, consult the docs.yml workflow of one of my projects. Here is the GitHub repository:
cicirello / Chips-n-Salsa
A Java library of Customizable, Hybridizable, Iterative, Parallel, Stochastic, and Self-Adaptive Local Search Algorithms
Copyright (C) 2002-2024 Vincent A. Cicirello.
Website: https://chips-n-salsa.cicirello.org/
API documentation: https://chips-n-salsa.cicirello.org/api/
How to Cite
If you use this library in your research, please cite the following paper:
Cicirello, V. A., (2020). Chips-n-Salsa: A Java Library of Customizable, Hybridizable, Iterative, Parallel, Stochastic, and Self-Adaptive Local Search Algorithms. Journal of Open Source Software, 5(52), 2448, https://doi.org/10.21105/joss.02448 .
Overview
Chips-n-Salsa is a Java library of customizable, hybridizable, iterative, parallel, stochastic, and self-adaptive local search algorithms. The library includes implementations of several stochastic local search algorithms, including simulated annealing, hill climbers, as well as constructive search algorithms such as stochastic sampling. Chips-n-Salsa now also includes genetic algorithms as well as evolutionary algorithms more generally. The library very extensively supports simulated annealing. It includes several classes for representing solutions to a variety of optimization problems…
Tools Used in the Workflow
More information about the javadoc-cleanup and generate-sitemap GitHub Actions is found on a website about the various GitHub Actions that I maintain, as well as within the corresponding GitHub repositories:
cicirello / javadoc-cleanup
Create mobile-friendly documentation sites by post-processing javadocs in GitHub Actions
javadoc-cleanup
Check out all of our GitHub Actions: https://actions.cicirello.org/
About
The javadoc-cleanup GitHub action is a utility to tidy up javadocs prior to deployment to an API documentation website, assumed hosted on GitHub Pages. It performs the following functions:
- Improves mobile browsing experience: It
inserts
<meta name="viewport" content="width=device-width, initial-scale=1">
within the<head>
of each html file that was generated by javadoc, if not already present. Beginning with Java 16, javadoc properly defines the viewport, whereas prior to Java 16, it does not. - Strips out any timestamps inserted by javadoc: The timestamps cause a variety of version control
issues for documentation sites maintained in git repositories. Javadoc has an option
-notimestamp
to direct javadoc not to insert timestamps (which we recommend that you also use). However, at the present time there appears to be a bug (in OpenJDK 11's javadoc, and possibly other versions)…
cicirello / generate-sitemap
Generate an XML sitemap for a GitHub Pages site using GitHub Actions
generate-sitemap
Check out all of our GitHub Actions: https://actions.cicirello.org/
About
The generate-sitemap GitHub action generates a sitemap for a website hosted on GitHub Pages, and has the following features:
- Support for both xml and txt sitemaps (you choose using one of the action's inputs).
- When generating an xml sitemap, it uses the last commit date of
each file to generate the
<lastmod>
tag in the sitemap entry. If the file was created during that workflow run, but not yet committed, then it instead uses the current date (however, we recommend if possible committing newly created files first). - Supports URLs for html and pdf files in the sitemap, and has inputs to control the included file types (defaults include both html and pdf files in the sitemap).
- Now also supports including URLs for a user specified list of additional file extensions in the sitemap.
- …
Where You Can Find Me
Follow me here on DEV:
Follow me on GitHub:
Vincent A Cicirello
View My Detailed GitHub Activity
If you want to generate the equivalent to the above for your own GitHub profile, check out the cicirello/user-statistician GitHub Action.
Or visit my website:
Top comments (0)