The blog you are reading is a Middleman statically generated website.

It started life as a simple Rails application, till one day I got curious about static site generators and decided that, after all, there was no reason to run a Rails app to serve static content.
I was already writing everything in markdown, I already had my custom Redcarpet + Rouge script to generate the html content, and I had grown tired of my custom admin area.

So I first tried to convert the site to Jekyll, but it required to convert all my ERB templates to use Liquid and it felt a bit too opinionated for my taste.

Middleman, on the other hand, felt just right. It’s flexible, customizable and powerful. It’s meant to support any website structure, and in fact the blogging features are available as a plugin. It’s written in Ruby, the rich internal API allows to tweak it and build extra features, the templating engine supports ERB and it has a Rails-like asset pipeline. It even includes the helpers module from Padrino, which makes migrating from Rails a breeze.

I recommend it to anyone trying to migrate a Rails app to a static site.

Deploying to GitHub Pages

The only problem, of course, is that it can’t be automatically compiled on GitHub Pages, which support raw HTML content and Jekyll source files.
That’s not a big deal, it just means that instead of pushing the Middleman source (templates, configuration, markdown text, etc) we have to push the generated static HTML files and assets.

To push them, however, they need to be in a git repository, and maintaining two repositories for the same site is tedious. It’s much better to keep everything in different branches in the same repo, which is possible as long as the branches have a completely different commit history.

Depending on the type of GitHub Pages site one particular branch will be used for publication. This branch should contain the static site files only, which means it must have a completely independent commit history.
To do that, we need to create it as an orphan branch.

From man git-checkout:

--orphan <new_branch>
Create a new orphan branch, named <new_branch>, started from <start_point> and switch to it. The first commit made on this new branch will have no parents and it will be the root of a new history totally disconnected from all the other branches and commits.
(…)
If you want to start a disconnected history that records a set of paths that is totally different from the one of <start_point>, then you should clear the index and the working tree right after creating the orphan branch by running “git rm -rf .” from the top level of the working tree. Afterwards you will be ready to prepare your new files, repopulating the working tree, by copying them from elsewhere, extracting a tarball, etc.

Thus, if our source branch is, let’s say, source, and our publishing branch is master, we can:

1
2
3
4
5
6
7
8
9
10
git checkout source
git branch -d master          # aim away from face
git checkout --orphan master
git rm --cached -r .          # unstage everything
rm -rf *                      # delete everything
# also delete the .hidden files, but NOT .git/
touch .gitkeep
git add .
git commit -m "prepared the deploy branch"
git push -f                   # aim away from face

That’s it. git branch will show the new branch, and if you inspect the history you’ll see that master now only includes that first commit.

From there, every time we build the static site we have to ensure that the contents of Middleman’s build/ directory are used to update the master branch, and push that branch do deploy the website to GitHub Pages.

To make it easier, it’s important to add the build/ diretory to the .gitignore file which will be present on the source branch, but missing on the master branch. This means that, after running middleman build, the compiled static files will be visible only in the master branch, which is exactly what we want.
After the build, all we have to do is to checkout master, move the static files out of build/ and into the root level of the repository, and commit.

I’ve automated the process with this shell script:

Happy blogging!