How to Setup a Rails-like Asset Pipeline With Octopress

6 minute read

A few weeks ago I decided to use Octopress for my new blog engine when Posterous shut its doors, and I love it thus far. There are a few things I miss from Rails though… After working in Rails for so many years, I have become accustomed to some of the nicer elements of developing on the platform, such as the Asset Pipeline.

The Problem

I want to use something similar but couldn't find anything nicely packaged up that would do everything I needed it to all in one. My solution needs to:

  1. Compile multiple css and javascript files into a single file
  2. Compress assets by removing whitespace to reduce filesize
  3. Compress assets by gziping them to serve to good browsers
  4. Provide cache-busting urls
  5. Provide asset_path tags for the source pages
  6. Upload assets to S3
  7. Use the S3 path in the final generated posts.

The Solution

There are several plugins that seemed to do most of the above points, but nothing that did it all. I got a great head start with the jekyll-assets gem. With the right configuration, the only thing it was missing was point 6. Let's setup the gem and see how we can add S3 uploading later.

First, as per the jekyll-assets installation guide, add gem 'jekyll-assets' anywhere in the Gemfile. Then run bundle install in terminal to install it.

Create a file in the plugins directory called jekyll_assets.rb. The contents should be a single line: require "jekyll-assets"

Next, you want to setup your _config.yml file to include the correct settings for the gem. You can read all about what each of these variables does over on the jekyll-assets configuration page. Mine looks something like this:

# Plugin: jekyll-assets
assets:
dirname: blog/assets
baseurl: http://cloudfront.coneybeare.me/blog/assets/
sources:
  - _assets/images
  - _assets/imgs
  - _assets/fancybox
  - _assets/javascripts
  - _assets/jwplayer
  - _assets/stylesheets
  - _assets/webfonts
compress:
js: 'yui'
css: 'yui'
cachebust: hard
gzip: [ text/css, application/javascript ]

Next, move all of your assets into the source/_assets folder. If it doesn't exist, create it now. After the move, you should not have an images, stylesheets, javascripts, or any other asset-related directory under the root source directory. Each of those asset-containing folders should now be under _assets so it looks like the sources entry from the config file.

You need to tell the rake generate task to not put the compiled css files in source/stylesheets anymore, but put it in your source/_assets/stylesheets directory instead so that jekyll-assets can find them. Edit the task to look like this:

desc "Generate jekyll site"
task :generate do
  raise "### You haven't set anything up yet. First run `rake install` to set up an Octopress theme." unless File.directory?(source_dir)
  puts "## Generating Site with Jekyll"
  system "compass compile --css-dir #{source_dir}/_assets/stylesheets"
  system "jekyll"
end

If you have any other tasks that call #{source_dir}/stylesheets or something similar, update them as well.

Now that the the plugin and configuration is done, it's time to edit the source files to use the plugin. Start by reading through the lengthy how-to section of the plugin. My setup replaced all css and js files with compiled files in the _assets directory.

In _includes/head.html:

{\% stylesheet app %} <!-- compiles app.css in _assets/stylesheets -->
{\% javascript app %} <!-- compiles app.js in _assets/javascripts -->

In _assets/stylesheets/app.css:

//= require screen

In _assets/javascripts/app.js:

//= require modernizr-2.0
//= require ender
//= require octopress

You also need to replace any references to images, webfonts, javascript files not in the app.js file, css files not in app.css with the correct markup for jekyll-assets. This was the most time-consuming part for my setup. I ended up doing a ton of regex-based find/replacing in TextMate 2 to make it happen, and it still took about an hour. Most of my edits were swapping out image sources in blog posts with {% asset path/to/img %}.

Background images and resources in the stylesheets needed some love too, but there was a bit of an issue with the asset_path liquid tags not being interpreted in the final public stylesheet. Even though the app.css file above was importing the style.css file that compass generated as part of the deploy task, it was not running it through its filters. Turns out this was a pretty easy fix though… jekyll-assets was skipping the stylesheet because it was already compressed by compass, so we just need to make sure that it doesn't get compressed.

In the root config.rb, change the output_style setting to :expanded.

Doing it this way means that we aren't losing any of the power of Octopress themes by still using compass to compile our sass directory, and that we are just using jekyll-assets to handle assets. Win.

Once I had all the source edited correctly, the stylesheets and the javascripts all setup correctly, running rake generate then compiled and moved the assets into the public/blog/assets directory, then ran through jekyll-assets and compressed. I could stop here but then I wouldn't have point 6 from above: S3 syncing.

S3 Asset Sync

In rails apps, I use the amazing asset_sync gem to keep my S3 bucket and my compiled assets in sync. I figured it wouldn't be too hard to adapt it to fit into an Octopress install, and I was right.

First, add gem 'asset_sync' to your Gemfile and execute bundle install. The gem has a bunch of setup instructions on its site, but most of it is very Rails specific. It turns out that you really don't need much to unleash the power of this gem, so here it is in a single rake task:

desc "Sync assets with s3"
task :asset_sync do
  puts "## Syncing assets..."

  # Weird variable to set, but it turns on AssetSync logging
  ENV['RAILS_GROUPS'] = "assets"

  # Setup AssetSync
  AssetSync.configure do |config|
    config.enabled = true # Needs to be set, disabled by default
    config.gzip_compression = true # Compresses the files before uploading, if not already compressed
    config.existing_remote_files = 'keep' # Keeps the old versions online. Necessary when working before deploying
    config.fog_provider = 'AWS' # There are others, I use S3
    config.fog_directory = 'my_bucket'
    config.aws_access_key_id = 'key'
    config.aws_secret_access_key = 'secret'
    config.prefix = 'blog/assets' # Same as jekyll-asset setup in _config.yml
    config.public_path = Pathname('./public') # Should always be this for Octopress
  end

  # Do the work
  AssetSync.sync
end

Once this is done, your local public/blog/assets directory will have synced up to your S3 bucket under the path my_bucket/blog/assets.

Because jekyll-assets is hooking on to the rake generate call, I created a new rake task to generate and sync to S3 in one call.

desc "Generate website and sync s3 servers"
task :gen_sync => [:generate, :asset_sync] do
end

And of course we need to add the new rake task to our rake gen_deploy call.

desc "Generate website and deploy"
task :gen_deploy => [:integrate, :gen_sync, :deploy, :notify] do
end

Hope this helps somebody make their Octopress deploy a bit easier. If you implemented it this way, be sure to let me know how it turned out in the comments. Enjoy!

Lastly, I run a small software company called Urban Apps. It pays the bills so I can take the time to write helpful posts like this one. If you found this page helpful at all, I would really appreciate it if you would check out my Apps on the iTunes App Store.

Was this page helpful for you? Buy me a slice of 🍕 to say thanks!

Comments