I recently battled some serious frustration in my Rails project; I’m posting what I learned here in hopes that it helps someone in the future.
I like to have a single Rake task named
ci for my continuous integration setup to use. Whether it’s a standard Ruby project, a Rails project, or even an Android project, continuous integration should be able to run
rake ci and produce all of the results it needs. No chaining tasks. No chaining commands. No rain dance with environment variables.
rake ci. Here’s how I did it in my Rails 3.2 app.
The problem stems from chaining tasks involving the database, which puts the
Rails.env into the development environment, and RSpec’s tasks, which puts
Rails.env into the test environment. Run either one by itself and it’s fine, but chain them, with say
rake db:migrate spec, and the
spec task doesn’t work properly. This is because the
Rails.env is development, not test.
Specifying the command as
RAILS_ENV=test rake db:migrate spec works, but again, I only want to depend on environment variables as a last resort. Likewise, specifying the command as two chained commands works—
rake db:migrate && rake spec, but I want to avoid chaining as well. My preference is for a developer to type
rake ci and that’s it. No forgetting what to type. No digging through history to remember the proper commands 2 years later. No emailing former developers and hoping they remember. The less typing, the better.
This guy ran into the same problem and wrote about it here ; it sounds like he was going for the same goal—a single
ci task—but ran out of options and fell back on chaining commands. After I read this I almost went with the same approach, but at the last moment remembered
ci task again:
I have the
ci task’s dependences setup to run all of the database migrations and what not. The guts of the task then use
exec to re-exec rake, but with the
RAILS_ENV cleared out. Now the
Now, of course, this isn’t perfect. If I’d wanted to run some code after the
exec, well, that wouldn’t work. And there are some tradeoffs and preferences to be made – I could have set
ENV['RAILS_ENV'] = nil instead of passing
'RAILS_ENV' => nil to
exec. But I like how the environment changes are encapsulated to the
Last but not least, my preference for a single
ci task is also, well, a preference. But it’s worked quite well for me over the last half dozen years. If you share this preference then I hope this use of
exec is helpful.
Edit 3/26/2012: Fixed an accidental textile formatting problem.
Edit 3/27/2012: Corrected the capitalization of a proper noun.