Friday, February 01, 2013

Capistrano frustrates me

Here's why:

1) It defines its own class path. If you want to load a customised extension, good luck - it is seemingly unaware of gems you add via bundle; pointing at a git repository

2) Task definition and execution are not separated. require is replaced with load - and load means "load and run".

For example -
You cannot "just" load the code and do something like compile assets locally by drawing on commands generated by someone else.

3) The methods defined are too rigid. I would refactor every capistrano recipe out there into behaving in two ways. First, there's a lot of methods that generate commands to be run. Second, there's business logic which decides where those commands should be run.

Looking at the recipe above:

    task :clean, :roles => assets_role, :except => { :no_release => true } do
      run "cd #{latest_release} && #{rake} RAILS_ENV=#{rails_env} #{asset_env} assets:clean"

You get no control there - the run() method will execute that remotely.
More frustrating: the commands are dumb strings. You don't really have the input escaping or knowledge of what's valid to put into the command in the code itself.

There needs to be flexibility, I agree, but some kind of basic input checking or modelling of what's happening should be more widely used.

4) The recipes tend to have no idea of where they are - you might as well write shell scripts to do most of it. Every time you see 'cd {absolute path}' is a good indication of this - smarter recipes would likely check current working directory (pwd); or keep a pointer to that.

The only thing that capistrano does better than pure bash scripts:

  • There's a lot pre-written, so you don't reinvent the wheel.
  • It has a dependency solver (before this do that, after that do this)

No comments: