Static site generator candidate software: Jekyll

Contents

Jekyll was a popular choice on the Hacker News discussion. As of September 2019, its [web page][J] has this to say about its features:

  • Simple: No more databases, comment moderation, or pesky updates to install—just your content.
  • Static: Markdown, Liquid, HTML & CSS go in. Static sites come out ready for deployment.
  • Blog-aware: Permalinks, categories, pages, posts, and custom layouts are all first-class citizens here.

Installation

I instantly ran into a problem installing Jekyll because it required Ruby version 2.4, while the CentOS test system I had initally set up had only Ruby 2.0 on it. 2.0 is so old that it’s considered obsolete as of February 2016—over three years ago! Why on Earth is CentOS including this ancient software in its distribution?

With the Fedora 30 VM in place, the installation process was:

  • As root, install RPMs ruby, rub-devel, and @development-tools
  • As a regular user, add two lines to ~/.bashrc to install Ruby gems (packages) into the user’s home directory:
      export GEM_HOME="$HOME/gems"
      export PATH="$HOME/gems/bin:$PATH"
  • Install Jekyll:
      gem install jekyll bundler

The Jekyll installer ran into a problem when it tried to compile some things:

gcc: fatal error: cannot read spec file '/usr/lib/rpm/redhat/redhat-hardened-cc1': No such file or directory

A quick search on Duck Duck Go turned up a suggestion to install the package redhat-rpm-config, which I did.

The installer failed again:

make: g++: Command not found

Installing the g++ package fixed that error and lead to a successful install. The following gems were installed:

bundler-2.0.2
concurrent-ruby-1.1.5
em-websocket-0.5.1
eventmachine-1.2.7
ffi-1.11.1
forwardable-extended-2.6.0
i18n-1.6.0
jekyll-4.0.0
jekyll-sass-converter-2.0.0
jekyll-watch-2.2.1
kramdown-2.1.0
kramdown-parser-gfm-1.1.0
liquid-4.0.3
listen-3.1.5
mercenary-0.3.6
pathutil-0.16.2
rb-fsevent-0.10.3
rb-inotify-0.10.0
rouge-3.10.0
ruby_dep-1.5.0
safe_yaml-1.0.5
sassc-2.2.0
terminal-table-1.8.0
unicode-display_width-1.6.0

Jekyll commands

From the web site, Jekyll uses the following commands:

  • jekyll new - Creates a new Jekyll site with default gem-based theme
  • jekyll new --blank - Creates a new blank Jekyll site scaffold
  • jekyll build or jekyll b - Performs a one off build your site to ./_site (by default)
  • jekyll serve or jekyll s - Builds your site any time a source file changes and serves it locally
  • jekyll doctor - Outputs any deprecation or configuration issues
  • jekyll clean - Removes all generated files: destination folder, metadata file, Sass and Jekyll caches.
  • jekyll help - Shows help, optionally for a given subcommand, e.g. jekyll help build
  • jekyll new-theme - Creates a new Jekyll theme scaffold

Typically one uses jekyll serve while developing locally and jekyll build when to generate the site for production.

To change Jekyll’s default build behavior, have a look through the configuration options.

jekyll command failure

The jekyll command failed the fist time I ran it:

/home/brian/.gem/ruby/gems/ffi-1.11.1/lib/ffi/library.rb:145:in `block in ffi_lib':
Could not open library '/home/brian/.gem/ruby/gems/sassc-2.2.0/lib/sassc/libsass.so':
  /home/brian/.gem/ruby/gems/sassc-2.2.0/lib/sassc/libsass.so:
  cannot open shared object file: No such file or directory (LoadError)

The libsass.so file appears in two places in my home directory:

/home/brian/.gem/ruby/extensions/x86_64-linux/2.6.0/sassc-2.2.0/sassc/libsass.so
/home/brian/.gem/ruby/gems/sassc-2.2.0/ext/libsass.so

Although they’re the same size, they’re independent files and not hard-links or symlinks. What’s interesting about the error is the program is looking for the .so file on this patth:

/home/brian/.gem/ruby/gems/sassc-2.2.0/lib/sassc/libsass.so

but it’s actually on this path:

/home/brian/.gem/ruby/gems/sassc-2.2.0/ext/libsass.so

Further, /home/brian/.gem/ruby/gems/sassc-2.2.0/lib/sassc/ is valid, but it contains ruby files and not compiled C files. I created a symlink:

cd /home/brian/.gem/ruby/gems/sassc-2.2.0/lib/sassc
ln -s ln -s ../../ext/libsass.so

That done, the jekyll command worked. But I’m disappointed that it took some advanced Linux skills to sort out the problem.

Creating a new blog

Creating the new blocg was as simple as:

[brian@ssg ~]$ jekyll new jekyll-blog
Running bundle install in /home/brian/jekyll-blog... 
  Bundler: Fetching gem metadata from https://rubygems.org/...........
  Bundler: Fetching gem metadata from https://rubygems.org/.
  Bundler: Resolving dependencies...
  Bundler: Using public_suffix 4.0.1
  Bundler: Using addressable 2.7.0
  Bundler: Using bundler 2.0.2
  Bundler: Using colorator 1.1.0
  Bundler: Using concurrent-ruby 1.1.5
  Bundler: Using eventmachine 1.2.7
  Bundler: Using http_parser.rb 0.6.0
  Bundler: Using em-websocket 0.5.1
  Bundler: Using ffi 1.11.1
  Bundler: Using forwardable-extended 2.6.0
  Bundler: Using i18n 1.6.0
  Bundler: Using sassc 2.2.0
  Bundler: Using jekyll-sass-converter 2.0.0
  Bundler: Using rb-fsevent 0.10.3
  Bundler: Using rb-inotify 0.10.0
  Bundler: Using ruby_dep 1.5.0
  Bundler: Using listen 3.1.5
  Bundler: Using jekyll-watch 2.2.1
  Bundler: Using kramdown 2.1.0
  Bundler: Using kramdown-parser-gfm 1.1.0
  Bundler: Using liquid 4.0.3
  Bundler: Using mercenary 0.3.6
  Bundler: Using pathutil 0.16.2
  Bundler: Using rouge 3.10.0
  Bundler: Using safe_yaml 1.0.5
  Bundler: Using unicode-display_width 1.6.0
  Bundler: Using terminal-table 1.8.0
  Bundler: Using jekyll 4.0.0
  Bundler: Using jekyll-feed 0.12.1
  Bundler: Using jekyll-seo-tag 2.6.1
  Bundler: Using minima 2.5.1
  Bundler: Bundle complete! 6 Gemfile dependencies, 31 gems now installed.
  Bundler: Use `bundle info [gemname]` to see where a bundled gem is installed.
New jekyll site installed in /home/brian/jekyll-blog.

Preview server on port 4000

Because the jekyll-blog directory contains only the ingredient files for the site (e.g. index.html exists only as index.markdown,) it can’t be viewed using a regular browser. To get around this, Jekyll includes its own server that lets you preview the site using port 4000:

[brian@ssg ~]$ cd jekyll-blog/
[brian@ssg jekyll-blog]$ bundle exec jekyll serve
Configuration file: /home/brian/jekyll-blog/_config.yml
            Source: /home/brian/jekyll-blog
       Destination: /home/brian/jekyll-blog/_site
 Incremental build: disabled. Enable with --incremental
      Generating... 
       Jekyll Feed: Generating feed for posts
                    done in 0.229 seconds.
 Auto-regeneration: enabled for '/home/brian/jekyll-blog'
    Server address: http://127.0.0.1:4000/
  Server running... press ctrl-c to stop.

Note that the jekyll serve command works by first building the site, then starting the server. By default the server binds only to 127.0.0.1, but that can be changed in the configuration file _config.yml:

detach: true
port: 4000
host: ssg.hvdc.net
baseurl: "" # does not include hostname
show_dir_listing: false

Serving the site using Apache

To serve the site using Apache, I first symlinked the _site dubdirectory to /var/www/html/jekyll-blog:

ln -s /var/www/html/jekyll-blog _site

The site built successfully, but when I went to browse it at http://ssg/jekyll-blog the CSS wasn’t there. That’s because the request for the main index file was:

GET HTTP/1.1 /jekyll-blog/index.html

which is as expected, but resources within the files were rooted a ‘/’ and not ‘/jekyll-blog/`, so the CSS request ended up as:

GET HTTP/1.1 /assets/main.css       <-- Note that /jekyll-blog is missing

which failed.

So I moved the jekyll-blog subdirectory out of /var/www/html and instead put it directly under /var/www, which is how I prefer to set things up on my systems. I do it this way so that all major things served up by Apache, such as phpMyAdmin are under /var/www insead of being buried one level deeper under html. I then updated the symlink in ~/jekyll-blog:

cd ~/jekyll-blog
rm _site && ln -s /var/www/jekyll-blog _site

Then I added a new file to /etc/httpd/conf.d named jekyll-blog.conf:

Alias /jekyll-blog/ /var/www/jekyll-blog/
Alias /blog/ /var/www/jekyll-blog/

<Directory /var/www/jekyll-blog/>
    DirectoryIndex index.html
</Directory>

Show-stopper: absolute vs relative URLs

Unfortunately, the above still didn’t fix the problem with the missing assets. The real problem was the URL for assets was absolute (/assets) indicating the directory is in the server’s DocumentRoot tree as opposed to being relative to the URL currently being addressed.

And this issue is all through Jekyll. By default, Jekyll assumes it’s at the root of the server’s document tree as opposed to simply being a part of it. That means the site needs to be served from a virtual host with its own DocumentRoot directive, or I have to do post-processing on the generated HTML to remove the leading “/” from some URLs:

1
2
3
4
5
6
7
8
#!/bin/bash
cd _site
find . -name '*.html' | while read FILE
do
    echo " $FILE"
    sed --in-place 's,href="/,href=",g' $FILE
done
echo

But the above script failed as soon as it got into a subdirectory, because there the href needed to be prefixed ../.

Now, the paths are an issue only on my systems where I use Apache to serve multiple things. Once the blog is up and running at a hosting site, the default of all files being served direcltly from the DocumentRoot holds true. However, being hopeful other programs would be able to handle URLs more gracefully, I abandoned Jekyll and moved on to Nikola.