Deploying a Jekyll Blog to GitHub Pages using Travis CI

Jekyll is a “blog-aware static site generator” written in Ruby that will generate a responsive website from Markdown and YAML files.

GitHub Pages offers you a free way to host static websites. And while they will automatically publish files in a repository named <your-github-id>.github.io, sometimes you need a little more control over the process.

Project Tooling

After running $ jekyll new, the base project structure will be generated for you. To simplify setup on additional machines and enable continuous deployment, I like to add additional tooling to this project.

Gemfile

A Gemfile is used to specify the Ruby dependencies (“Gems”) required to generate the website. In addition to jekyll, I’m using:

Gemfile.lock

The Gemfile.lock is automatically generated when installing Gems. It’s purpose is to record of the exact version of each dependency the last time the project was successfully deployed.

Bundler

bundler is a tool that can install and run particular versions of Gems for a project. I prefer to store the Gems locally via $ bundler install --path vendor so that I’m not polluting my system directories and can completely delete all files a project creates.

Makefile

A Makefile puts everything together. I like using make (rather than rake) because it doesn’t depend on Ruby itself, does a good of tracking when files have changed, and provides a standard interface between projects.

I’ll highlight the important parts of this file. This reinstalls the dependencies whenever they change:

VENDOR_DIR := vendor
INSTALLED_FLAG := $(VENDOR_DIR)/.installed

.PHONY: install
install: $(INSTALLED_FLAG)
$(INSTALLED_FLAG): Gemfile* Makefile
    bundle install --path vendor
    @ touch $(INSTALLED_FLAG)

This builds the site and validates the generated HTML:

.PHONY: build
build: install
    bundle exec jekyll build --quiet
    bundle exec htmlproof _site --only-4xx

And these targets provide a way to run the site locally:

.PHONY: run
run: install
    bundle exec jekyll serve --future --drafts

.PHONY: launch
launch: install
    eval "sleep 5; open http://localhost:4000" & make run

See the source code that generates this site as an example of how these files are used together in practice.

Developing Locally

Using the above tooling, my only system dependencies are ruby, bundler, and make. To work on a new blog entry, I simply run:

$ make launch

to bring up a local instance of the site that is regenerated whenever I edit content.

When I am done editing, running $ make ci will confirm that the site is ready to be published.

Deploying with Travis CI

Travis CI offers free continuous integration for open source projects that can be used to deploy software after running a number of checks.

Adding a .travis.yml to your project tells Travis CI how to build and deploy your site. This specifies which commands to run to install and validate each commit:

install:
- make install

script:
- make ci

If a new commit passes those checks, the following shell script is run:

# Generate HTML
make build ;
# Configure Git with Travis CI information
git config --global user.email "[email protected]" ;
git config --global user.name "travis-ci" ;
# Delete the current repository
rm -rf .git ;
# Rebuild the repository from the generated files and push to GitHub pages
cd _site ;
git init ;
git add . ;
git commit -m "Deploy Travis CI build $TRAVIS_BUILD_NUMBER to GitHub pages" ;
git push -f https://${GH_TOKEN}@github.com/${TRAVIS_REPO_SLUG} main:gh-pages ;

which will publish the generated files to the gh-pages branch on GitHub.

GH_TOKEN is an encrypted access token to grant Travis CI permissions to modify files in your repository. This site provides a good overview on generating these tokens.

After deployment, you should now see your Jekyll blog live at:

https://<your-github-id>.github.io/<your-repository-name>


See a typo? Help me edit this post.