A custom GitHub Actions workflow for static HTML pages
I build files for this website locally and push the static HTML files to GitHub Pages, using the default workflow. At some point, I started noticing that the deploys were taking long to complete. A deploy from today shows that it took 40 seconds to render.
I decided to looked into the workflow itself using GitHub’s command line tool, and found a job called build, which takes the most time to complete.
$ gh run list --limit 1
STATUS TITLE WORKFLOW BRANCH EVENT ID ELAPSED AGE
✓ pages build an... pages-build... main dynamic 19118060009 40s about 50 mi...
$ gh run view 19118060009
✓ main pages-build-deployment · 19118060009
Triggered via dynamic about 50 minutes ago
JOBS
✓ build in 22s (ID 54632038648)
✓ deploy in 8s (ID 54632067818)
✓ report-build-status in 5s (ID 54632067849)
...
What does build do? It builds the site with Jekyll.
$ gh run view --job=54632038648
✓ main pages-build-deployment · 19118060009
Triggered via dynamic about 50 minutes ago
✓ build in 22s (ID 54632038648)
✓ Set up job
✓ Pull ghcr.io/actions/jekyll-build-pages:v1.0.13
✓ Checkout
✓ Build with Jekyll
✓ Upload artifact
✓ Post Checkout
✓ Complete job
My website doesn’t need to be built by Jekyll, and GitHub provides a solution to skip it—to add a file called .nojekyll to the root of the repository.
So I added the file and pushed the changes, and the workflow ran again. This time it took 20 seconds to run, half the time from the last workflow! However, the build step is still there.
$ gh run list --limit 1
STATUS TITLE WORKFLOW BRANCH EVENT ID ELAPSED AGE
✓ pages build an... pages-build... main dynamic 19118930798 20s about 9 min...
$ gh run view 19118930798
✓ main pages-build-deployment · 19118930798
Triggered via dynamic about 9 minutes ago
JOBS
✓ build in 4s (ID 54634839639)
✓ report-build-status in 3s (ID 54634846786)
✓ deploy in 8s (ID 54634846814)
...
I was under the assumption that .nojekyll would remove the build job, but that wasn’t the case. Instead, it only removes one of the steps in the job, that runs Jekyll.
$ gh run view --job=54634839639
✓ main pages-build-deployment · 19118930798
Triggered via dynamic about 9 minutes ago
✓ build in 4s (ID 54634839639)
✓ Set up job
✓ Checkout
✓ Upload artifact
✓ Post Checkout
✓ Complete job
...
It feels unnecessary to have to run the build and report-build-status jobs each time, when all I really need is just the deploy job. I wanted to speed this up further.
After some clickity-clakity on the Pages section of the repository settings page, I discovered two options for deployment. One is to deploy from a branch, which is the default GitHub Pages workflow. The other is GitHub Actions, which allows for custom workflows.
After clicking on GitHub Actions option, the displays some suggested templates. One of those templates is Static HTML, which sounds like exactly what I’m looking for! You can view the full template on the starter-workflows repository on GitHub, but here is an excerpt:
jobs:
# Single deploy job since we're just deploying
deploy:
environment:
name: github-pages
url: ${{ steps.deployment.outputs.page_url }}
runs-on: ubuntu-latest
steps:
- name: Checkout
uses: actions/checkout@v4
- name: Setup Pages
uses: actions/configure-pages@v5
- name: Upload artifact
uses: actions/upload-pages-artifact@v3
with:
# Upload entire repository
path: '.'
- name: Deploy to GitHub Pages
id: deployment
uses: actions/deploy-pages@v4
In comparison to the GitHub Pages workflow which had three jobs, this has only one - deploy - exactly what I needed! I took the template as is and added it to my repository at .github/workflows/. One thing I changed was commenting out the Setup Pages step, as I didn’t recall seeing that step in the previous workflows.
I expected a much bigger speedup for the custom workflow run, but it was only a second faster.
$ gh run list --limit 1
✓ * Deploy stat... main push 19120221474 18s about 15 mi...
$ gh run view 19120221474
✓ main Deploy static content to Pages · 19120221474
Triggered via push about 15 minutes ago
JOBS
✓ deploy in 14s (ID 54638891569)
...
Looking closely at the steps, I get a better sense why. The deploy job is now a mix of the build and deploy jobs from the second workflow run (after adding .nojekyll). The number of jobs may have reduced from three to one, but the number of steps are more or less the same.
Combining all steps into one job does reduce some overhead. The original workflow ran a setup and cleanup for each of the three jobs. With one job, the setup and cleanup happens only once, which saves a second or two.
I’ll take a few seconds of speedup as a win.