Easily deploy your site with grunt

17 February 2014

Today I want to share an easy and flexible way to deploy websites from your local machine to your own server using Grunt.

This is by no means a full deployment strategy, but it allows you to entirely skip the slow, error-prone and tedious process of manually moving files around with FTP. I'm currently using it for this site.

This post will only make sense for you if you're using Grunt to build your site. If you've never heard of Grunt or haven't looked into it yet, you should definitely check out Chris Coyier's Article on 24ways.

How does it work?

We'll use the grunt-build-control task to push our /build folder to a remote repository, where we'll take advantage of post-receive hooks to run the commands we need for deploying the site. Don't worry, the setup is pretty straight forward.

What you'll need

First of all you'll need a build process that outputs the minified and optimized version of your site into a separate subdirectory in your project. grunt-build-control will put everything in that folder under version control and push it to a remote repository. Ideally you don't want your build to appear in the main repository, so you should exclude it in your .gitignore file.

In order to create and setup the remote repository, you'll need to have ssh access to your server and git installed. To avoid the annoying password prompts you should generate a public ssh key and add it to your servers authorized keys under .ssh/authorized_keys.

Let’s roll up our sleeves and get down to work

Installing and setting up grunt-build-control

First, you'll need to install grunt-build-control and add the necessary configurations and tasks to your Gruntfile.js:

buildcontrol: {
  options: {
    dir: 'path/to/your/build',
    commit: true,
    push: true,
    message: 'Built %sourceName% from commit %sourceCommit% on branch %sourceBranch%'
  },
  your-server: {
    options: {
      remote: 'ssh://user@your-server.com/path/to/git/your-repository.git',
      branch: 'master'
    }
  }
}

To make grunt-build-control work properly, you have to make sure your build process doesn't delete the .git directory inside the /build directory.

Now you can either run it manually with grunt buildcontrol:your-server or create a new deploy task and use it as the last step after building, which could look something like this:

grunt.registerTask('deploy', [
  'check',
  'test',
  'build',
  'buildcontrol:your-server'
]);

Setting up the server-side

First of all, we need to create a new repository (which we specified in our buildcontrol task) on our server to push to.

I like to have all my repositories in one place, which in this case is just a git folder inside my servers' root directory, but your mileage may vary.

$ cd git/
$ mkdir mysite.git
$ cd mysite.git
$ git init --bare

This creates a new bare repository, which means it only contains the .git folder but no working copies of your source files. This allows us to point its working directory to where we want to deploy our site.

Now let's create and set up a new post-receive hook to tell the repository what to do when a new version gets pushed:

$ touch hooks/post-receive

Open up the file in your preferred editor and add:

export GIT_WORK_TREE=/path/to/deploy/
export GIT_DIR=/path/to/mysite.git/

cd $GIT_WORK_TREE
git checkout -f
git clean -f -d

path/to/deploy/ is usually something like home/user/html/

Let's see what this does: First we set the path of our working directory to where we want to deploy our site and enter the path to our repository. Make sure you set both, and set set them to an absolute path. Now we switch to our working directory, where git checkout -f is used to update all the files in the working tree and throw away any local changes, while git clean -f -d removes all files and folders, which are not / no longer under version control.

When it comes to working with hooks, I would recommend trying things out in a subdirectory first. Make sure to always test your hooks carefully, probably starting with a script that just outputs dummy text, like this echo its running.

We're pretty much set now, we just need to make sure our new hook is executable:

$ chmod +x hooks/post-receive

Excluding files

There are probably some files you need on your server but don't want in your repository, like .htaccess files or a download folders for your clients.

To make sure those files won't be deleted when running git clean, you can either exclude them by using the -e flag, like git clean -e .htaccess -e /downloads or (even better!) add them to your repository's exclude file, which you'll find at .git/info/exclude.

To fine tune the git clean task, have a look at the docs.

Connect the dots

That's it! Now you should be able to run grunt buildcontrol:your-server to deploy your site from every branch of your project. To level up, you could use different repositories, branches and settings for testing, staging and production environments.

There you go

A pretty simple deployment setup, which integrates nicely with your existing grunt configuration.

If you have any questions, thoughts, improvements, modifications or disagreements, hit me up on twitter.

image of post author

About the author

My name is Marius Scheel. I'm a designer currently living and working in Berlin, Germany. Learn more about me or have a look at my work.