Probably you are familiar with Capistrano. It’s a deployment tool written in Ruby. I used it in several projects to deploy Rails applications. For the default Rails stack with a SQL Database it always worked fine. For a non default Rails stack, not always so good. This is how a capistrano deployment directory looks like on the server.

The current symlink links to a timestamped directory in the “releases” directory, which contains the actual application code. The “releases” directory looks like this:

Each subdirectory contains the timestamp of the deployment as name. The “shared” directory looks like this:

It contains directories which are shared over all “release” directories. For example the “log” directory for logging outputs or the “pid” directory which contains the pid of the current running ruby application server, for example unicorn.

Capistrano creates these directory structure automatically for you. And every time you perform a new deployment it creates a new timestamped directory under “releases” and if the deployment was successfull it links the “current” to the newest “release” directory under “releases”.

A couple months ago I learned Ansible, to automate the whole IT Infrastructure of VersionEye. Ansible is really great! I automated everything with it. But there was a break in the flow. Usually I executed an Ansible script to setup a new Server and then I had to execute capistrano to deploy the application. One day I thought why not implementing the whole deployment with Ansible as well? Why not just execute one single command which sets up EVERYTHING?

So I did. I implemented a capistrano like deployment with Ansible. This is how I did it.

First of all I ensure that the “log” and “pid” directories exist in the “shared” folder. The following commands will create them if they do not exist. These commands create the whole directory path if they do not exist. If they exist nothing happens.

- name: Create log directory file: > state=directory owner=ubuntu group=ubuntu recurse=yes path="/var/www/versioneye/shared/log" - name: Create pids directory file: > state=directory owner=ubuntu group=ubuntu recurse=yes path="/var/www/versioneye/shared/pids"

The next part is a bit more tricky, because we need a timestamp as a variable. This is how it works with Ansible.

- name: Get release timestamp command: date +%Y%m%d%H%M%S register: timestamp

These command takes the current timestamp and registers it in the “timestamp” variable. Now we can use the variable to create a new variable with the full path to the new “release” directory.

- name: Name release directory command: echo "/var/www/versioneye/releases/{{ timestamp.stdout }}" register: release_path

And now we can create the new “release” directory.

- name: Create release directory file: > state=directory owner=ubuntu group=ubuntu recurse=yes path={{ release_path.stdout }}

Allright. Now in the next step we can checkout our source code from git into the new “release” directory we just created.

- name: checkout git repo into release directory git: > repo=git@github.com:versioneye/versioneye.git dest="{{ release_path.stdout }}" version=master accept_hostkey=yes sudo: no

Remember. Ansible works via SSH tunneling. With the right configuration you can auto forward your SSH Agent. That means if you are able to check out that git repository on your localhost, you will be able to check it out on any remote server via Ansible as well.

Now we want to overwrite the “log” and the “pids” directory in the application directory and link them to our “shared” folder.

- name: link log directory file: > state=link path="{{ release_path.stdout }}/log" src="/var/www/versioneye/shared/log" sudo: no

- name: link pids directory file: > state=link path="{{ release_path.stdout }}/pids" src="/var/www/versioneye/shared/pids" sudo: no

Now let’s install the dependencies.

- name: install dependencies shell: cd {{ release_path.stdout }}; bundle install sudo: no

And pre compile the assets.

- name: assets precompile shell: cd {{ release_path.stdout }}; bundle exec rake assets:precompile --trace sudo: no

And finally update the “current” symlink and restart Unicorn, the ruby application server.

- name: Update app version file: > state=link path=/var/www/versioneye/current src={{ release_path.stdout }} notify: restart unicorn

That’s it.

This is how VersionEye was deployed for a couple months, before we moved on to Docker containers. Now we use Ansible to deploy Docker Containers. But that’s another blog post 😉