DEV Community

Vincent A. Cicirello
Vincent A. Cicirello

Posted on • Updated on

Deploy a Documentation Website for a Java Library Using GitHub Actions

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

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 and pull_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 during push and pull_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:
Enter fullscreen mode Exit fullscreen mode

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
Enter fullscreen mode Exit fullscreen mode

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
Enter fullscreen mode Exit fullscreen mode

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
Enter fullscreen mode Exit fullscreen mode

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
Enter fullscreen mode Exit fullscreen mode

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:

    - 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 }}"
Enter fullscreen mode Exit fullscreen mode

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 ..
Enter fullscreen mode Exit fullscreen mode

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 }}"
Enter fullscreen mode Exit fullscreen mode

I have a recent DEV post about generate-sitemap if you want to learn more about it:

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 ..
Enter fullscreen mode Exit fullscreen mode

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 ..
Enter fullscreen mode Exit fullscreen mode

Live Example

To see a live example, consult the docs.yml workflow of one of my projects. Here is the GitHub repository:

GitHub logo cicirello / Chips-n-Salsa

A Java library of Customizable, Hybridizable, Iterative, Parallel, Stochastic, and Self-Adaptive Local Search Algorithms

Chips-n-Salsa - A Java library of customizable, hybridizable, iterative, parallel, stochastic, and self-adaptive local search algorithms

Chips-n-Salsa Mentioned in Awesome Machine Learning

Copyright (C) 2002-2023 Vincent A. Cicirello.

Website: https://chips-n-salsa.cicirello.org/

API documentation: https://chips-n-salsa.cicirello.org/api/

Publications About the Library DOI
Packages and Releases Maven Central GitHub release (latest by date) JitPack
Build Status build docs CodeQL
JaCoCo Test Coverage coverage branches coverage
Security Snyk security score Snyk Known Vulnerabilities
DOI DOI
Other Information GitHub style
Support GitHub Sponsors Liberapay Ko-Fi

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:

Vincent Cicirello - Open source GitHub Actions for workflow automation

Features information on several open source GitHub Actions for workflow automation that we have developed to automate parts of the CI/CD pipeline, and other repetitive tasks. The GitHub Actions featured include jacoco-badge-generator, generate-sitemap, user-statistician, and javadoc-cleanup.

favicon actions.cicirello.org

GitHub logo cicirello / javadoc-cleanup

Create mobile-friendly documentation sites by post-processing javadocs in GitHub Actions

javadoc-cleanup

cicirello/javadoc-cleanup - Create mobile-friendly documentation sites by post-processing javadocs in GitHub Actions

Check out all of our GitHub Actions: https://actions.cicirello.org/

About

GitHub Actions GitHub release (latest by date) Count of Action Users
Build Status build CodeQL
Source Info License GitHub top language
Support GitHub Sponsors Liberapay Ko-Fi

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)…

GitHub logo cicirello / generate-sitemap

Generate an XML sitemap for a GitHub Pages site using GitHub Actions

generate-sitemap

cicirello/generate-sitemap - Generate XML sitemaps for static websites in GitHub Actions

Check out all of our GitHub Actions: https://actions.cicirello.org/

About

GitHub Actions GitHub release (latest by date) Count of Action Users
Build Status build CodeQL
Source Info GitHub GitHub top language
Support GitHub Sponsors Liberapay Ko-Fi

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:

GitHub logo cicirello / cicirello

My GitHub Profile

Vincent A Cicirello

Vincent A. Cicirello

Sites where you can find me or my work
Web and social media Personal Website LinkedIn DEV Profile
Software development Github Maven Central PyPI Docker Hub
Publications Google Scholar ORCID DBLP ACM Digital Library IEEE Xplore ResearchGate arXiv

My bibliometrics

My 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:

Vincent A. Cicirello - Professor of Computer Science

Vincent A. Cicirello - Professor of Computer Science at Stockton University - is a researcher in artificial intelligence, evolutionary computation, swarm intelligence, and computational intelligence, with a Ph.D. in Robotics from Carnegie Mellon University. He is an ACM Senior Member, IEEE Senior Member, AAAI Life Member, EAI Distinguished Member, and SIAM Member.

favicon cicirello.org

Top comments (0)