Skip to main content

Building a Blog Part 3 - Continuous Integration with Gitlab CI


Why CI a simple blog? #

Just like we write automated unit tests for our applications, anything we’re pushing to the Web should be stable and we’re confident we didn’t break our site with an errant //JS or <HTML> tag. To do this, we’ll attach an integration service listening to our commits to the master branch.

Choosing a Platform #

Since I’m running my blog outside of Github Pages, I can’t depend on integration servies such as Travis CI and Github’s ecosystem of integrations. Have no fear, there are many Continuous Integration solutions out there for a variety of purposes. Here are a few:

  1. Jenkins (used to be Hudson)

jenkins

While I’m familiar with Jenkins, I know from experience it requires quite a bit of RAM as well as needing a JVM. Its a great choice for running Java builds with Gradle.

  1. Travis CI (integrates with Github)

travis-ci

If we were hosting this blog on Github Pages, Travis would be a no-briner.

  1. Gitlab CI

gitlab

Since we spent a whole blog post deploying and securing a Gitlab instance, it makes sense to keep going in this vein.

Installing Gitlab CI #

As the instructions may change, here is the Gitlab Runner repo:

Gitlab CI Runner

This will setup the hooks and listeners for our commits to start a build.

I elected to install Docker on the Gitlab server to isolate CI builds from affecting the server.

curl -sSL https://get.docker.com/ | sh

this gives us more flexibility when creating our .gitlab-ci.yml configurations for our projects.

When the installer asks for the registration details, you’ll find that here in your Gitlab install: Project > Settings > Runners.

Runners #

Runners break down a build into three main phases:

  • Build
  • Test
  • Deploy

Using our knowledge of Docker we can build isolated and reproducable builds. Here’s the one I’ve created for this blog:

.gitlab-ci.yml #

image: ruby:2.3

stages:
  - build
  - test
  - deploy

before_script:
  - apt-get update >/dev/null
  - apt-get install -y locales >/dev/null
  - echo "en_US UTF-8" > /etc/locale.gen
  - locale-gen en_US.UTF-8
  - export LANG=en_US.UTF-8
  - export LANGUAGE=en_US:en
  - export LC_ALL=en_US.UTF-8
  - bundle install --jobs $(nproc) --path=/cache/bundler
  
build-and-lint:
  stage: build
  script:
    - scripts/cibuild
  only:
    - master

The encoding changes you see are a solution to Invalid byte sequence in US-ASCII - an issue I only saw in my Ubuntu environments, not locally on OS X.

cibuild #

#!/usr/bin/env bash

set -e # halt script on error

bundle exec jekyll build
bundle exec htmlproofer _site

based on Jekyll’s recommendation: Jekyll CI

Validation Gotchas #

  1. <img> tags need an alt=

  2. Markdown anchor links are all lowercase with no spaces, periods, or question marks.

a header like # .gitlab-ci.yml resolves to #gitlab-ciyml and ## Installing Gitlab CI resolves to #installing-gitlab-ci

Luckily, the htmlproofer gem catches these sorts of breaks as well as many others.

Commit > Push > Build #

Once we’re ready to commit we merge our feature branched article into trunk and run git push gitlab master we see the build kicking off:

build pending
build running
build failed

oops…

It appears our linter caught a broken link in the site. Lets fix that.


build passed
build passed logs

Troubleshooting #

Invalid byte sequence in US-ASCII #

By default, the ruby:2.x Docker image uses US-ASCII instead of UTF-8, which will cause validation errors when linting the generated site for errors.