We use Jekyll to statically build this blog, and have integrated it with GitHub Pages, which takes care of hosting and the CI/CD pipeline. The Jekyll/GitHub Pages solution involves using the github-pages gem in the project, and configuring the GitHub Pages settings for the repository. To build and deploy, all that’s required is to push a commit to the master branch, and the GitHub hosted blog is live in a matter of minutes.

We have been using Jekyll for 5 or 6 years now, and it has made writing our blog easy. We have our own theme and layout which was fairly easy to create, and adding a blog post is as simple as writing a new markdown file. However, we have been stuck on an old version, 3, for some time now, and with a rebrand of the blog, it seemed like a perfect opportunity to address this and migrate to Jekyll 4. The problem is that the GitHub Pages gem doesn’t support Jekyll 4, and doesn’t look like it will anytime soon.

How does the GitHub Pages gem work anyway?

To start with, Jekyll has built-in support for GitHub Pages via the github-pages gem. When a repository is configured to use GitHub Pages, and it is a Jekyll site with the gem applied to it, it will first build the site before deploying the build artifacts. But if all things remain the same, except the gem is not applied to the site, then the entire repository will be deployed. That’s key. The GitHub Pages gem is the CI part of the CI/CD pipeline:

CI/CD pipeline with the gem

GitHub Pages gem GitHub Pages

So, is that it? Are we stuck with Jekyll 3?

As it turns out, there is a very simple alternative to using the gem for CI, and that is using the Jekyll Actions GitHub Action instead, which is the recommended approach from the guys at Jekyll. And the nice thing about this approach is that it keeps everything in GitHub, just like the gem, but without the restrictions.

There is very simple configuration file, plus a requirement of a Personal Access Token with repository permissions. And given this is a company blog, the token belongs to a machine user.

CI/CD pipeline with GitHub Actions

Jekyll GitHub Action GitHub Pages

Okay, but do we really need to migrate to Jekyll 4?

Some of the key features of version 4 for us were:

  • Keeping our software up-to-date with the latest versions
  • Making use of the new gems

The first one shouldn’t be considered unimportant. Old software can hinder developers:

  • New features cannot be taken advantage of
  • Best practices change and these may not be easy to keep up with when using stale software
  • Security holes may not be fixed
  • Documentation for the old version may be difficult to find, especially necessary when things have changed significantly

Perhaps I’m labouring the point. But neither am I advocating living on the bleeding edge, which too has it’s down sides. It’s just much better for everyone to be up-to-date.

So, what exactly are the steps we identified?

1 Perform a dry run, just to ensure the process is understood beforehand, and identify any complications

To ensure the process be well understood beforehand, and to identify any complications, the repository can be duplicated/forked for a trial run. In fact, the following migration steps were refined through doing just that. This built confidence is the migration process and helped distinguish between things that were part of the migration to Jekyll 4 and those that were part of the re-branding/re-theming exercise.

2 Perform the actual migration

2.1 Create a secret from a Personal Access Token with full repo permissions to the repository

A Personal Access Token can be created at https://github.com/settings/tokens, using instructions from authenticating-to-github/creating-a-personal-access-token in GitHub docs. (We already had with full repo permissions to the repository for a machine user.)

With the token at hand, head to the following location within the project repository /settings/secrets/actions/new to create the new secret:

  • Value {the Personal Access Token with repo permissions}

Then click Add secret.

2.2 Add a new GitHub Action workflow file to the repository

The GitHub Action workflow file will be configured to trigger on changes to the master branch, and will then checkout the repository, and build it, using the Jekyll Actions GitHub Action with the Personal Access Token setup earlier (referred to as secrets.JEKYLL_PAT).

The env: PAGES_PAGES_HOSTNAME: xxx is only used if you have a custom domain setup.

NB. The last step copies the build artefacts to the gh-pages branch.


name: Build and deploy Jekyll site to GitHub Pages

      - master

    runs-on: ubuntu-latest
      - uses: actions/checkout@v2
      - uses: helaili/jekyll-action@2.1.0
          token: $
          PAGES_PAGES_HOSTNAME: https://custom-domain

In order for the action to correctly set site.repository (used by things like site.baseurl), while still using the github-pages gem, you will need to add the following to your _config.yml:

+ repository: username/repo-name

This change can now be pushed, even with GitHub Pages continuing to build and deploy off the master branch. The action doesn’t interfere with this, as it only makes changes to the gh-pages branch and this is not what is deployed, yet.

2.3 Configure GitHub Pages to use gh-pages branch

With the GitHub Action successfully setup and running on the repository (which can be checked by both reviewing the Actions tab in the repository, or simply the contents of the gh-pages branch), it’s time to wire this up to GitHub Pages.

Go to the Settings tab in the repository, scroll to the GitHub Pages section, and set the following:

  • Source
    • Branch gh-pages
    • Folder / (root)
  • Custom domain (Set by the CNAME file within the repository, when using a custom domain)

2.4 Upgrade to Jekyll 4 and remove the GitHub Pages gem requirement from the Gemfile

Update the Gemfile by removing the github-pages gem and including the Jekyll 4.2.0 gem:

- ruby '~> 2.3'
+ ruby '~> 2.4'

- gem 'github-pages', group: :jekyll_plugins
+ gem "jekyll", "~> 4.2.0"
+group :jekyll_plugins do
+    gem "jekyll-seo-tag", "~> 2.7.1"

Then run the following to make sure the Gemfile.lock is updated correctly:

bundle install

And check everything is still working okay (hopefully so 🙏) by running the site and viewing it:

bundle exec jekyll serve

Update the .gitignore with the following cache directories generated by Jekyll 4 builds:

+ .jekyll-cache/
+ .sass-cache/

3 Update internal documentation to include the new build step

Add a description of the CI/CD pipeline, how it works and how it can be modified. Some of this blog post and the document’s pipeline section share similar, content, but for differing purposes.

While this appears as a task after the migration, it was completed beforehand.

4 Write a blog post describing our experience (yes, this one!)

As with the internal documentation, it was written out in full before the actual migration was performed (with the exception of the final thoughts 😉), again providing another opportunity to think through the migration in detail and understand it inside out.

5 Analysing the impact with GTM reports

The Google Analytics reports for the blog were reviewed to identify what needed keeping an eye on and ensure no negative impact from the upgrade.

Final thoughts

TBC post-migration!