Using Grunt for WordPress Theme Development and Deployments
Keep reading to find out how to use Grunt with your WordPress themes to speed up your development and allow you to deploy your code to your servers.
[Update 2013-05-15: I’m now using grunt-contrib-watch instead of grunt-regarde and grunt-livereload. It has a built in LiveReload server and is more streamlined. I updated the Gists, the tutorials, and my WordPress Starter Theme, Genesis Starter Child Theme, and Drupal 7 Starter Theme.]
What is Grunt?
Grunt is an elegant task runner from Ben Alman written in Node. In it’s latest version 0.4, Grunt has become an elegant wrapper for any task you can imagine. There is a large community behind Grunt with many contributed plugins that you can add to your projects. Whether you’re looking to create a full build process or just watch files for changes, Grunt has you covered.
I’m not going to get too in depth on how to install Grunt - check out their Getting Started guide for getting up and running on your system.
What Can Grunt Do?
All of this is accomplished by adding two files to your theme, a
package.json to define which Grunt plugins to use as dependencies, and a
Gruntfile.js to define our tasks and options. For more information on getting that all setup, my friend Jonathan Christopher has a great article about using Grunt with WordPress.
Using Grunt with WordPress Themes
Before we begin, I’d like to note that I have two WordPress starter themes setup that you use: one for use with most WordPress themes and one for use with the Genesis Framework. Both utilize all of the Grunt tasks we’re going to go talk about in this article, with plenty of theme functions and grids to get you started.
Let’s get going, shall we? Make sure Node and Grunt are installed on your system, and then create a
package.json file in the root of your theme to define your dependencies.
[* Please note, package.json is subject to change. Check my Grunt WordPress and Drupal repositories on Github for the latest versions]
With that in your theme, you can then open the theme folder in your Terminal and run:
This will tell NPM to download all dependencies and put them in a
node_modules folder. Now we’re ready to create our
[* Please note, Gruntfile.js is subject to change. Check my Grunt WordPress and Drupal repositories on Github for the latest versions]
livereload task, but give it no options (you can change the port here if you feel the need).
all setting, followed by configuring Uglify to create source maps and concatenate and minify our files. In my setup, I have it automatically pull
assets/js/source/plugins.js and all files inside
assets/js/vendor other than Modernizr and put them into
assets/js/plugins.min.js, as well as taking our main.js script and minifying.
Next, we tell Compass to use the settings in our external
config.rb file for processing our SCSS. I chose to break
config.rb out into a separate file instead of putting the options directly inside the
Gruntfile.js in case I wanted to use the Compass ruby gem directly or use CodeKit, as both will work using the
The next section sets up Regarde for our watch task to monitor for changed files and triggering our tasks. The reason for using Regarde instead of Grunt Watch is that Watch doesn’t give access to changed files because it spawns tasks in subprocesses, which doesn’t play nice with LiveReload. It’s essentially a drop-in replacement, so swapping back to Watch won’t be an issue if the way it spawns processes changes. We tell Regarde to fire the
livereload tasks whenever an SCSS file is changed, and to fire
livereload whenever a JS file is changed.
[Update: Watch now works with LiveReload and has it built in, so the above Regarde code is swapped out for Watch. It’s essentially the same code, we’re just adding an option to LiveReload the browser.]
The last task for this section is imagemin, which doesn’t automatically run as file are changed, but run manually with:
At the end of the
Gruntfile.js, we load all our tasks and register our default task. To run Grunt, at the terminal, just enter
Now, let’s get right on the most fun part: using Grunt to deploy your theme to your servers.
Deploying Your WordPress Theme with Grunt
There are a number of deployment choices when it comes to Grunt tasks. You can use SSH to connect and run commands or transfer files over SFTP, you can use FTP to transfer files if you don’t have SSH access, or you can use Rsync to sync up your directories over an SSH connection. After trying all three, I settled on Rsync as it provided the best and quickest way to update my theme files on the servers I use most. I typically have SSH or SFTP access, so it’s not an issue for me, but if you have only have FTP access, it’s trivial to swap out the Rsync plugin with the FTP plugin for your deployments. I’ve used this method for client sites in the past and it works seemlessly.
Gruntfile.js, we have two servers setup: staging and production. Each sets the source as the root of our theme project locally and sets a destination and hostname for our remote server. Setting
recursive tells rsync to traverse all subdirectories to make sure we’re getting all of our files, and
syncDest will delete any files on the remote server that no longer exist in your local repository. I’m excluding a number of files as they aren’t needed for the theme to run on the server.
To trigger a deployment, at the Terminal, enter:
And like that, you’ve now deployed your files to your server!
I’ve changed over my deployments to using Grunt now instead of setting up Git post-commit hooks and bare repositories. Having the general framework in Grunt makes it quicker than configuring post-commit hooks on multiple servers, and it works just as well. With so many tasks available to extend and customize Grunt, it’s an invaluable tool for any development process.
This method can be easily tweaked to work with any development process, from Drupal themes to ExpressionEngine sites to static site builds and use in other systems. I love the flexibility of Grunt and plan to keep using and evolving my Gruntfile going forward. I hope you do the same!