Deploying GitHub Pages sites with GitHub Workflows

Dave Cross - Mar 20 '23 - - Dev Community

I've written before about how I use GitHub Workflows to keep "semi-static" web sites up to date. It's a technique that I've found really useful. When I wrote that blog post, things were pretty simple - you chose which branch held your web site (there was a tradition for a while to use gh-pages) and whether the web site pages were in the repos root directory or the directory called /docs. I usually put my web site files into the /docs directory in the master (now main) branch and things worked just fine.

The reason for storing the site in /docs was so that there was a separation between the files that were used to generate the site from the generated output site itself. Many of my repos would have a /tt directory that contained templates, a /data directory which contains JSON files or an SQLite database and a /bin directory with a build program that pulls all that stuff together and generates a pile of HTML files that end up in the /docs directory. In my original blog post on this subject, I demonstrated a GitHub Workflow definition that would regenerate the site (when input files changed or on a schedule) and committed any changed files in the /docs directory. Some GitHub magic would then ensure that the new version of the site was deployed to the GitHub Pages server. All was well with the world.

Then, a few months ago, things got a little more complicated. We gained options about how your GitHub Pages site was deployed. The standard version that I'd be using before was called "deploy from a branch" but there was another option called "GitHub Actions". It seemed likely to me that I really needed to start using the "GitHub Actions" option, but things were still working the old way, and I had far more interesting things to investigate, so I left things the way they were.

Well, I say things were still working in the old way... They were, but something was a bit different. It seemed that the old method was being powered by a new GitHub Workflow called "pages-build-deployment" that had been automatically added to all the repos that needed it. And looking into the details of that workflow, I noticed that it was doing some things that were unnecessary in my repos - for example it assumed that the site was being built using Jekyll and that was only true for a couple of my repos. For most of them, that was unnecessary work. So I needed to look into the new deployment option in more detail.

I started a couple of weeks ago, by simply switching the option from "deploy from a branch" to "GitHub Actions" in the hope that, because I was already using GitHub Actions, things would Just Work. But, unfortunately, that wasn't the case. My new site was being generated and committed to the repo - but the changes weren't showing up on the live site. So I switched things back until I had time to look into in it more detail.

That time was today. It seemed that I needed to include code in my GitHub Workflow that would actually handle the deployment of the site to the GitHub Pages servers. A quick search of the GitHub Actions marketplace found the Deploy GitHub Pages site action which seemed to be the right thing. But reading the documentation, I worked out that it wanted to deploy the site from an artifact, so I needed to create that first. And then I found Upload GitHub Pages artifact which did the right thing. So it was just a case of adding these two actions to my workflows in the correct way.

Previously, my workflows for these sites just needed a single job (called build) but now I added a deploy job which depended on build. For example, the workflow that builds Planet Perl now looks like this:

name: Generate web page

on:
  push:
    branches: '*'
  schedule:
    - cron: '37 */4 * * *'
  workflow_dispatch:

jobs:
  build:
    runs-on: ubuntu-latest
    container: davorg/perl-perlanet:latest

    steps:
    - name: Checkout
      uses: actions/checkout@v3

    - name: Create pages
      run: |
        mkdir -p docs
        perlanet > perlanet.log 2>&1
    - name: Commit new page
      if: github.repository == 'davorg/planetperl'
      run: |
        git config --global --add safe.directory /__w/planetperl/planetperl
        GIT_STATUS=$(git status --porcelain)
        echo $GIT_STATUS
        git config user.name github-actions[bot]
        git config user.email 41898282+github-actions[bot]@users.noreply.github.com
        git add docs/
        if [ "$GIT_STATUS" != "" ]; then git commit -m "Automated Web page generation"; fi
        if [ "$GIT_STATUS" != "" ]; then git push; fi
    - name: Archive perlanet logs
      uses: actions/upload-artifact@v3
      with:
        name: perlanet.log
        path: ./perlanet.log
        retention-days: 3

    - name: Update pages artifact
      uses: actions/upload-pages-artifact@v1
      with:
        path: docs/

  deploy:
    needs: build
    permissions:
      pages: write
      id-token: write
    environment:
      name: github-pages
      url: ${\{ steps.deployment.outputs.page_url }}
    runs-on: ubuntu-latest
    steps:
    - name: Deploy to GitHub Pages
      id: deployment
      uses: actions/deploy-pages@v1
Enter fullscreen mode Exit fullscreen mode

The bits that I've added are the final step in the build job ("Update pages artifact") and the new deploy job. All of the code is largely copied from the documentation of the two actions I mentioned above.

Having made this changes to one of my planet sites, I switched the deployment method and forced the workflow to run. And was very happy to see it ran successfully and the new version of the site appeared at the live URL as soon as the deployment had changed.

This makes me happy as I feel I'm using the GitHub Pages deployment the way that they're supposed to be used. I've updated all of my planet sites to use this method, but I have several other sites that I'll need to get round to switching at some point.

As always when I find out something new about a GitHub feature, it leaves me with a couple of other suggestions for improvements:

  • It's possible to call one workflow from another. The planet workflows are all very similar. I wonder if I can define a single workflow that does all of the work and just call that from the individual workflow definition - passing in parameters to handle the differences.
  • Now I'm deploying the sites from artifacts, there is no need for the generated site to actually exist in the repo. That might well make a few things quite a bit easier.

Anyway, I thought I'd share what I had discovered today. Is anyone else generating web sites this way? How do you do it?

. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .