How I automatically link check my Hugo site
Last updated: Dec 27, 2023
Every time I commit, a program in my CI checks for broken links.
This is going to be a short topic, because my link-checking method is simple. In my Gitlab repo, I have a continuous integration(CI) script that:
- Builds the Hugo site. If something fails, it stops.
- Checks the newly built site for broken links. If any link breaks, it stops.
- Deploys the site to my server.
This is not a particularly robust way to check links, for the reasons I explained in my “Link check your site post”. But it’s good enough for now it’s good enough.
Required software
You’ll need:
- A CI server. I use GitLab, but I’ve done this in Github too.
- A link checker that your CI can download.
For the link checker, I use Linkinator, a TypeScript application. I don’t remember how I started using it, but I keep using it because it’s fast enough for me, simple to use, and reliable.
The important thing is that your link checker can search recursively. That is, for every internal link, you want the link checker to visit that page and check its links too. It doesn’t need to recursively check external links; if it did, it might check the whole internet.
Steps to check links on commits to Gitlab
Here’s how I do it on my site:
Have a Hugo site.
Host it on Gitlab.
In the root of your site’s directory, create a file called
.gitlab-ci.yml
.The following steps all require you to edit this YAML file.
In the YAML file, declare a base image. Install
npm
andhugo
.My CI’s image already comes with Hugo installed. It’s an Alpine Linux image, so I’ll use Alpine’s
apk
package manager to install the software.Use
npm
to install linkinator.Build your Hugo site. Run linkinator recursively on the public folder.
Test that it works. Intentionally add a broken link, and push it to the repo.
If the link checker works, you won’t deploy any more broken links, even if you push them. This will catch newly added links that are improperly formatted, and old links that have sadly rotted.
Example CI script
Here’s a slightly simplified version of what my CI YAML file looks like:
image: registry.gitlab.com/pages/hugo:latest
variables:
GIT\_SUBMODULE\_STRATEGY: recursive
deploy:
timeout: 5 minutes
script:
- apk add npm
- hugo --minify
- npm install -g linkinator
- linkinator --recurse public/
You can see the full file, with deploy, in my site’s repo.