I recently migrated my WordPress blog to Octopress, which is a blogging framework for Jekyll, the static site generator.

When I was researching my options for moving my blog to Jekyll, I had some reservations about using Octopress versus just rolling my own layout for Jekyll. I wanted to deploy to Github Pages, but I really hated the deployment strategy that comes with Octopress. Since Github Pages actually runs Jekyll, I didn't like the thought of having to keep my Jekyll source on a 'source' branch and deploying the generated static site to my master branch just to get it to play nice with Github. This was before I realized that I'd probably need at least a few plugins if I wanted to mimic the behavior of my WordPress site. The more I looked at Github Pages, the more I began to think it wasn't for me...

So then I started looking at Heroku, and was confronted with an even uglier (albeit simpler) deployment strategy: check the systematically generated /public directory into source control. With a populated public directory, Octopress is a fully qualified rack application, and Heroku has no problem running it just like any other application. But I didn't want to clutter up my master branch with a public directory where a majority of the files are likely to change on almost every commit. My solution was to merge the Github strategy with the existing Heroku strategy, adding a little extra GIT flavor.

First, I created a _heroku directory, and copied the Octopress config.ru and Gemfile. On the Gemfile, I ditched everything except the Sinatra dependency:

source "http://rubygems.org"
gem 'sinatra', '1.2.6'

I added a public folder and created a "hello world" index.html file in it, just to have something to push up to Heroku before figuring out the actual deploy. Heroku doesn't need anything else to run the static site, so now all I needed was to populate the public folder with my static site output from Jekyll, and push the entire thing to Heroku.

I decided to write a Rake task similar to the :push deploy task for Github Pages that comes with Octopress, but first I needed to create my Heroku application and push up an initial deploy:

gem install heroku
cd _heroku
git init .
git add .
git commit -am "initial commit"
heroku create
git push heroku master

With that completed, I was able to launch my fresh Heroku app in my browser and see "Hello World" from the index.html file I had created. Note that instead of cloning an existing repository or creating a separate branch, I simply initialized a new git repository. This repository will be automatically picked up by my parent "source" repository and committed as a sub-repository which is then tracked by the most recent commit. I feel this is a lot cleaner than committing the static output to my Octopress repository, and is the point of this entire post.

Finally, I created a Rake task to copy my Jekyll /public directory (where the static files are generated) to _heroku/public, commit the result, and then push the sub-repository to Heroku. The code is pretty similar to the Github push method:

  desc "deploy basic rack app to heroku"
multitask :heroku do
puts "## Deploying to Heroku "
(Dir["#{deploy_dir}/public/*"]).each { |f| rm_rf(f) }
system "cp -R #{public_dir}/* #{deploy_dir}/public"
puts "\n## copying #{public_dir} to #{deploy_dir}/public"
cd "#{deploy_dir}" do
system "git add ."
system "git add -u"
puts "\n## Committing: Site updated at #{Time.now.utc}"
message = "Site updated at #{Time.now.utc}"
system "git commit -m '#{message}'"
puts "\n## Pushing generated #{deploy_dir} website"
system "git push heroku #{deploy_branch}"
puts "\n## Heroku deploy complete"

To make this the default deploy method, I changed a few config settings at the top of the Rakefile:

deploy_default = "heroku"
deploy_branch = "master"
deploy_dir = "_heroku" # deploy directory (for Github pages deployment)

And that's it! I gave the new code a try:

rake generate
rake deploy

Everything seemed to go alright, so I fired up my browser and there was my shiny new Octopress blog, with free hosting, and a deployment strategy that doesn't suck.

Know a better way? I'd welcome the input :).