GitHub Actions workflow to build and `rsync` a static site

GitHub Actions workflow to build a static site, and deploy it to the web server using rsync.

name: Deploy

The workflow is triggered when the main branch is pushed to GitHub, or manually:

on:
    push:
        branches:
            - main
    workflow_dispatch:

The main body of the workflow is split into two separate jobs.

The first job (build) builds the static site on the runner, by setting up the necessary Python environment (defined in requirements.txt), then running the build step (mkdocs build). (See this post for details of the site and the build process.) The job is disabled if the repository is a fork.

jobs:
    build:
        runs-on: ubuntu-latest
        if: github.event.repository.fork == false
        steps:
            - uses: actions/checkout@v4
            - name: Setup Python
              id: setup-python
              uses: actions/setup-python@v4
              with:
                  python-version: "3.11"
            - name: Install dependencies
              id: install-dependencies
              run: python3 -m pip install -r requirements.txt
            - name: Build
              id: build
              run: mkdocs build

The final step of this job is to upload the built site (site directory) as an artifact so it can be used by the subsequent deployment job.

            - name: Upload
              id: upload-site
              uses: actions/upload-artifact@v3
              with:
                  name: site
                  path: site

The second (deploy) job begins by downloading the build artifact.

    deploy:
        needs: build
        runs-on: ubuntu-latest
        steps:
            - name: Download
              id: download-site
              uses: actions/download-artifact@v3
              with:
                  name: site

The private SSH key used to access the web server has been stored as a GitHub secret, DEPLOY_SSH_KEY. To use this, we first start the SSH agent, binding it to a fixed socket location which can be accessed later, and import the private key into the agent. The private key is never written to disk. The SSH agent will continue to run and can be used by later steps by setting the SSH_AUTH_SOCK environment variable.

            - name: Setup SSH key
              env:
                  SSH_AUTH_SOCK: /tmp/ssh_agent.sock
              run: |
                  ssh-agent -a "${{ env.SSH_AUTH_SOCK }}" > /dev/null
                  ssh-add - <<< "${{ secrets.DEPLOY_SSH_KEY }}"

The site is then deployed by rsyncing the site directory to the target directory (~/www/) on the web server.

The host name of the target web server and the login name have also been stored in advance on GitHub as secrets called DEPLOY_TARGET_HOST and DEPLOY_TARGET_USER, and are accessed in the workflow via ${{ secrets.DEPLOY_TARGET_HOST }} and ${{ secrets.DEPLOY_TARGET_USER }}. The overall target in the form user@host is stored in the environment variable TARGET which is later accessed via ${{ env.TARGET }}.

            - name: Deploy
              id: deploy
              env:
                  SSH_AUTH_SOCK: /tmp/ssh_agent.sock
                  TARGET: "${{ secrets.DEPLOY_TARGET_USER }}@${{ secrets.DEPLOY_TARGET_HOST }}"
              run: |
                  rsync -ahvz --delete \
                    -e 'ssh -o StrictHostKeyChecking=no' \
                    ./ "${{ env.TARGET }}:www/"