Make deployments even more robust

I’m sad to say, but me and inploy parted ways. I just need even more robustness in deployment. I don’t want to get the whole deployment screwed up just because of the git repo cannot be fast-forwarded. I don’t want to screw my deployment on any issue.

I ended up returning to Vlad. The journey to Inploy had it’s advantage though.

What a deployment system should do for us?

Problem: multiple destinations is a must. You might want to deploy to a production server. To a staging server. To a CI server. You name it. Solution: Vlad honors ‘to’ variable. Write your default config to config/deploy.rb, and then, specific config to config/deploy_#{ENV['to']}.rb.

Problem: maybe you have multiple environments (like development server runs passenger, but you run thin with god in staging and production environment). Solution: don’t set :app or :web options in vlad.rake (set them to nil), and load them in the appropriate deploy_*.rb files.

Cleanup or more setup is required at deployment side. Solution: local rake / thor tasks or generators to be run after setup / update.

Make every deployment independent. Solution: Vlad does this. It clones a vanity repo, and it copies data to a unique, timestamped directory.

Support bundler. Solution: Bundler supports Vlad instead.

Support RVM. It’s not a simple answer.

First, bash from FreeBSD ports doesn’t run any rc scripts when running remote commands via ssh. My answer was to switch to zsh. FBSD + ssh + zsh == RVM script is loaded. Another option is to wrap every remote task around with bash -lc “…”.

Second, Vlad’s tasks don’t honor .rvmrc trust prompts, and ‘rvm rvmrc trust <directory>’ is not not quite there. You have to call it twice for every deployment: one for ‘releases/DATE‘, and another for ‘current’. Even so, your trust file will grow by every deployment, but won’t shrink by removing any obsoleted deployments.

There are two possible solutions for this: you can either move project .rvmrc out of your repository (but we all know already, your repo should contain .rvmrc), or you can disable this annoying rvmrc trust altogether by adding ‘rvm_trust_rvmrcs_flag=1′ into your (or the deploy user’s) ~/.rvmrc file.

The article I mentioned above the author comes to the conclusion that project rvmrc files should not contain gemset information, only ruby version information. I have played with the idea, and I found it fits well into a deployment concept.

Gemsets and bundles are basically competing ideas

Of course there are differences, but basically they’re here to solve the very same problem: separating projects’ gem environment from others. RVM goes even further, and it changes bundler behavior to work into a gemset. However, bundler does it’s job better (wrt. dependency resolution), therefore it’s no need to have two gem environment separation technologies around.

Are gemsets have a use then? Of course they have. Using bundler also means you don’t have to pollute your default ruby environment with a ton of not needed gems. Sometimes, however, you want to use a tool or another, which are good to be around. Just like when you create a new rails project.

You don’t have to have the whole rails stack around in your (default) gemset: put it to rails3. And then, when you want to fire up a new project, just type in: ‘rvm 1.9.2@rails3 rails new …’

Set up your environments

That’s alright, but we still have two environments to sort out: a development and a deployment one. Why it matters? Because development environment is about being easily accessible, but your deployment env. is about to be robust. I strongly suggest vendoring your bundles in development:

bundle install --path vendor --binstubs --without production

In deployment, however, I’d use this:

bundle install --path ../../shared --deployment --binstubs --without test development

(you can tweak it for CI for example)

It means all of the binaries will be available in ./bin directory (eg. rake, thor, rspec, all of them). You might want to exclude bundle directory from Vim or Textmate:

Vim:

put ‘set wildignore+=vendor/ruby/**’ into .vimrc

TextMate:

Go TextMate -> Preferences, select Advanced, then Folder Preferences tab. Edit Folder Pattern:

  • enclose patterns between starting ! and trailing $ into parens like this: !(.*/(…))$
  • add ‘|vendor/ruby’ just before closing paren + dollar like this: !(.*(…)|vendor/ruby)$

You might want to repeat this for already created projects: open directory, select root directory in project drawer, click on Information button at the bottom right corner in project drawer, repeat steps above.

Deployed environment differs in two places: it works from Gemfile.lock only, and doesn’t allow any further modifications of Gemfile. The second difference is in bundle directory: we can store bundles in shared directory to save deployment time, it doesn’t change too often anyways. Of course, if you don’t concur, you can always use vendor dir.

This is still a work in progress. Any ideas, comments are highly appreciated.