Posts Tagged ‘growl’

How I made Autotest RedGreen and Growl party together

If you’re like me and like autotest as a sort of private continous integration system, and if you like pretty output, you will probably have used some kind of autotest + redgreen and/or growl.

Unfortunately I had strange quirks in my growl notifications. Sometimes they worked, sometimes they didn’t (I was using the standard :ran_command autotest hook at the time), so I switched to the newer :red and :green hooks, and only worsened the problem. No matter what the test output was, I always got a green notification.

I was able to trace the problem to the redgreen gem. Its colorized output wasn’t being recognized correctly by autotest and it kept thinking everything was ok.

A little ruby fiddling and this is my new improved (and working!) .autotest file:

# -*- ruby -*-
 
module Autotest::RedGreen
  Autotest.send(:alias_method, :real_ruby, :ruby)
  Autotest.send(:define_method, :ruby) do |*args|
      real_ruby + %[ -rrubygems -e "require 'redgreen'" ] 
  end
 
  # Clean the output so other modules can work correctly
  Autotest.add_hook :ran_command do |at|
    at.results.each do |r|
      r.gsub!("\033[31m", "")
      r.gsub!("\033[32m", "")
      r.gsub!("\033[33m", "")
      r.gsub!("\033[0m", "")
    end
  end
end
 
module Autotest::Growl
  AUTOTEST_IMAGE_ROOT = "~/.autotest_images"
 
  def self.growl(title, msg, img, pri=0, sticky="")
    system "growlnotify -n autotest --image #{img} -p #{pri} -m '#{msg.inspect} #{title}' #{sticky}"
  end
 
  Autotest.add_hook :red do |at|
    growl("FAIL", "#{get_results(at)}", "#{AUTOTEST_IMAGE_ROOT}/fail.png", 2)
  end
 
  Autotest.add_hook :green do |at|
    growl("Pass", "#{get_results(at)}", "#{AUTOTEST_IMAGE_ROOT}/pass.png")
  end
 
  private
  def self.get_results(at)
    results = [at.results].flatten.join("\n")
 
    if results.include? 'tests'
      output = results.slice(/(\d+)\s+tests?,\s*(\d+)\s+assertions?,\s*(\d+)\s+failures?(,\s*(\d+)\s+errors)?/)
    else
      output = results.slice(/(\d+)\s+examples?,\s*(\d+)\s+failures?(,\s*(\d+)\s+not implemented)?/)
    end
    output
  end
end
 
# Esclusioni
Autotest.add_hook :initialize do |at|
  %w{.hg .git .svn stories tmtags Rakefile Capfile README spec/spec.opts spec/rcov.opts vendor/gems autotest svn-commit .DS_Store }.each do |exception|
    at.add_exception(exception)
  end
 
  at.add_mapping(/spec\/defaults.rb/) do |f, _|
    at.files_matching %r%^spec/(controllers|helpers|lib|models|views)/.*\.rb$%
  end
end

Our setup had apache as webserver so I don’t need any special code to restart the application. In addition to this recipe, made to deploy to production I also wrote a couple of tasks that shine during development.

Drupal has the problem of storing most of the important stuff in the database, and if you do local development, showing the status of the work to your clients can be a chore. The following tasks allow you to easily dump the development db and import the dump to the staging server to show your progresses to your customers and allow testing the site:

# Callbacks
before 'deploy:start', 'drupal:db:import:production'
 
before 'deploy:restart', 'mikamai:permissions:fix', 'mikamai:production:symlink', 'drupal:configure:production'
before 'deploy:start', 'mikamai:permissions:fix', 'mikamai:production:symlink', 'drupal:configure:production'
before 'deploy:cold', 'drupal:db:dump:development'
 
# DB Stuff
 
set :mysqldump, "/opt/local/bin/mysqldump5" # your path to mysqldump
# local db credentials
set :local_db_user, "root"
set :local_db_password, ""
set :local_db_name, "database"
# remote db credentials
set :db_user, "user"
set :db_password, "secret"
set :db_name, "database"
 
namespace :drupal do
 
  namespace :configure do
    task :production do
      sudo "cp #{latest_release}/sites/default/settings.production.php #{latest_release}/sites/default/settings.php"
    end
 
    task :development do
      sudo "cp #{latest_release}/sites/default/settings.development.php #{latest_release}/sites/default/settings.php"
    end
  end
 
  namespace :db do
    namespace :dump do
      task :development do
        raise RuntimeError.new("failed dump") unless system "#{mysqldump} -u #{local_db_user} --password=#{local_db_password} #{local_db_name} > dump.sql"
      end
    end
 
    namespace :import do
      task :production do
        ENV["FILES"] = "dump.sql"
        deploy::upload
        run "mysql -u #{db_user} --password=#{db_password} #{db_name} < #{latest_release}/dump.sql"
      end
    end
  end
end