Bootstrapping a Jekyll Blog using Gitlab

This is one of those odd posts that came about because I starting playing with one idea and something else came about all-together. It started with me considering blogging options besides WordPress. There’s lots of platforms out there, and some of them are quite good. I’m also looking for something that I can run for free. Right now my WordPress.com premium account is paid for until almost the end of the year. So I have plenty of time to consider other options.

One of those is Jekyll. It’s basically a set of templates and a script to build them into a website. It’s not like WordPress as it doesn’t have a database holding the pages. Jekyll’s files are just plain HTML. So there’s no need for the webserver to interpret a scripting language (PHP, etc.) or interface with a database. This makes the Jekyll-generated site portable to any webhost.

Of course this comes with a downside. There’s no fancy management interface or app to do the heavy lifting of getting a post out the door. The usual workflow is to create/edit the pages on your local machine then run Jekyll. This will create the finished HTML that can be uploaded to the server. In most examples I’ve seen either Git or rsync is used to do the actual moving. Jekyll also has an option to act as a local webserver so the pages can be previewed before the upload. Jekyll really came into its own when used with Github. There, Github Pages takes most of the work out of the running of Jekyll. The only thing the user needed to do was to write new pages and posts and then git push them. Github takes over and runs Jekyll and serves up the generated HTML.

Overall, this is a simple and fast way to get a site online. I have a GitHub account, but I don’t use it much as I do most of my personal work on GitLab now. I chose GitLab because it doesn’t charge for private repositories. But I found it can automatically keep a repository in sync with another. This is one place GitHub falls down. If you were to fork a project on GitHub (the upstream), there’s no way to synchronize that fork without pulling it down to the local machine and then pushing the changes to the fork in your GitHub account. Granted it’s the traditional way to do things, but when working away from my laptop using an iPad app it’s an impossible task. It’s much easier to mirror the GitHub repository to Gitlab and have it do a pull every hour. Then my Gitlab repository is always synchronized without any effort on my part.

Plus Gitlab has built-in continuous integration (CI) runners that are easy to configure. These features make it a platform that suits my needs better than GitHub.

So Jekyll then...

Once I started messing about with the Jekyll setup, I made a few sites and tried out Gitlab’s CI runners for the first time. This is an extra step in Gitlab as GitHub punishes the site automatically.

Using the CI runners worked really well, and the setup wasn’t as bad as I was imagining. So I got to thinking that maybe it could be used to do the initial site building in case a local Ruby/Jekyll/Git installation wasn’t available.

At first it seemed like a pure thought experiment. But then I realized a couple of things.

  • Every repo can have its own blog.
  • I might want to set one up when I'm away from a computer that has Ruby installed on it.
  • Gitlab will save the output files for downloading after the build is complete.

The CI runners provided by Gitlab have the option of being “native” ruby. Behind the scenes this means that the Docker image used is the one with the latest version of Ruby. There’s no configuration needed beyond specifying which version you’d like to use. It’s all very convenient and hides the hassle of dealing with a Docker image directly.

The first test I did was to see if the CI runner could install Jekyll the same as you’d do from the command line. And it worked. Then it was simply a matter of adding an output folder and putting the result of jekyll new into it. There’s a few tricks along the way worth knowing, which I’ll go over in the next section.

How to

The first step is to log into your Gitlab account and click the green New Project button at the top right of the project list page.

The Gitlab new project button.

Give it a name, set your privacy level, and click the Create Project button near the bottom of the page. Since the CI runner is part of Gitlab, it can see your private projects. This way you can keep your source files out of the public eye.

The new project is setup empty, as expected. The catch here is that the Repository tab which shows the files isn’t present. But there are links on the main project page to create a README.md file. Click this, and enter some text. Any amount will do, and then save it. Now you can see the Repository tab at the top.

Location of the Gitlab Repository button.

Click it, and next to the drop down menu to choose the branch, there’s a large + button. Clicking it will let you create a new file.

The location of the Gitlab new file button.

Name the file .gitlab-ci.yml and paste in the following.

image: ruby:2.3

bootstrap:
  stage: build
  script:
    - gem install jekyll minima
    - jekyll new jekyll
  artifacts:
    paths:
      - jekyll

What this does:

  • image: ruby:2.3: selects the Ruby Docker image.
  • bootstrap:: the name of the job, this is arbitrary and can be anything. It's simply a namespace.
  • stage: build: the name of the process, which is also an arbitrary label, but for longer jobs, the Gitlab interface will report which stage is current running.
  • script:: the start of the Ruby scripts to run.
  • - gem install jekyll minima: this installs Jekyll and the default theme into the image.
  • - jekyll new jekyll: this will run Jekyll with the output going into the jekyll folder.
  • artifacts:: this tells the CI runner there's stuff to save.
  • paths:: this will define the paths where the output is saved.
  • - jekyll: this is the output folder, as reference by the new command above.

If you have any doubt about how this is formatted, Gitlab provides a syntax checker for .gitlab-ci.yml files here.

When you save this file, the magic happens. The CI runner will automatically read it and launch a process to create your Jekyll installation. But you do have to have Shared Runners activated in the Runners project settings panel.

Once it runs, you’ll get a result about three minutes later. Clicking on Pipelines and then Build you should see a list of runners with one entry and a green box saying Passed on the left of the screen. At the very right of that same line is a download arrow.

The location of the Gitlab Build and file download buttons.

Clicking that will download a .zip file named jekyll. This is your new Jekyll site all zipped up. You can unzip the file and then use the project’s add file button to upload the files. Sadly it can only do one at a time. Also, the _posts folder has to be made with the web interface, then the example post can be uploaded. But before doing any uploading make sure to delete the gitlab-ci.yml file. We don’t need it anymore, and if it’s still there, it’ll try to run each time a file is uploaded. It’s better to delete it first. You can get it back by looking in the activity history, and clicking View File.

Once this is done, your project repository will look the same as if you had ran Jekyll on the command line and use Git to push the files to Gitlab. It’s not quite as simple, but you also didn’t have to install anything on the computer you’re using.

Make any changes to the _config.yml file as needed. If this is a blog for a project, make sure to set the baseurl: to the project’s name. Jekyll needs this to make sure all the paths are correct. If you don’t the CSS file will show as missing.

The last set is to set up the CI runner to use Jekyll like it was intended and build the site for you. This means creating a new gitlab-ci.yml file that you’ll use for as long as the site is running. This is the one I’m currently using. It will be run for every git push or file saved on the web.

# Require at least Ruby 2.3
image: ruby:2.3

# This job will run for any branch other than master, and
the results can be downloaded for testing. It won't be served onto the web.
test:
  stage: test
  script:
    - bundle install
    - bundle exec jekyll build -d test/
  artifacts:
    paths:
      - test
  except:
    - master

# The 'pages' job will deploy and build your site to the
'public' path and it will be served out onto the web. It 
must be named 'pages' for it to work properly.
pages:
  stage: deploy
  script:
    - bundle install
    - bundle exec jekyll build -d public/
  artifacts:
    paths:
      - public
  only:
    - master # this job will affect only the 'master' branch

In this .config-ci.yml I’m using Bundler to make sure all of the Gems get installed. This way any changes made locally will show up in the Gemfile and there’s no need to have to update the configuration file whenever a plugin is installed or removed.

Conclusion

I haven’t tested build times with a large site, as I still need to see about converting a WordPress export into a Jekyll compatible format. In fact I still need to do that export. It might be worth seeing if the –incremental build flag helps. But I’ll play with that more when I’m ready to make the move.

Once all of this is done, it’s a simple matter to edit the pages as needed and start blogging. As you can see it can all be done with the web interface with the help of some creative use of the CI runners. So if you get caught without your usual tools, there’s a way to get a Jekyll blog up and running.