CI: integrity with resque

Я думаю, что в каждой команде в процессе разработки рано или поздно встаёт вопрос о настройке continuous integration. Для руби-проектов существует много инструментов и по статистике ruby toolbox наибольшей популярностью пользуются cjoe, integrity и cruisecontrol.rb. Раньше мы использовали cruisecontrol.rb, но на текущем проекте решили попробовать что-нибуть альтернативное, отчасти потому, что cruisecontrol.rb достаточно достаточно сложно настраивать, там отсутствуют web-hooks для github, а так же он периодически у нас зависал. Сначала я решил попробовать cijoe, который достаточно прост, но не подошел тем, что для каждого бранча нужно было поднимать отдельный экземпляр приложения, а CI у нас планировалось запускать на одном из development серверов.

В итоге выбор пал на integrity. Посмотреть его в действии можно на http://builder.integrityapp.com. Это простой в установке и использовании сервис. Написан на sinatra и легко устанавливается в окружении, позволяющем запуск rack приложений. Внутри integrity очень простой, но в тоже время функциональный. Он предоставляет несколько механизмов уведомления: email, IRC, campfire, TCP, HTTP, notifo and AMQP.

Build можно запустить несколькими способами:

И, наконец, integrity позволяет механизм постановки в очередь и запуска заданий:

Настройка

Так как мы используем ubuntu на сервере, некоторые команды ниже могут быть специфичны для этой системы, но я думаю будет не сложно найти аналогичные для вашей. Для начала нужно установить и настроить resque.

Устанавливаем redis. При этом redis должен создать стартовый скрипт в /etc/rc.*.

# apt-get install redis-server

Далее устанавливаем resque:

# gem install resque

Теперь настроим front-end для него. Мы используем nginx и passenger для запуска rack/rails приложений, так что дальше следует типичная настройка rack приложения для passenger (подробнее можно прочитать в официальной документации).

# mkdir -p /home/deploy/resque/public
# mkdir -p /home/deploy/resque/tmp

Пример файла /home/deploy/resque/config.ru можно взять в репозитории resque. Мы используем такой:

require 'logger'

$LOAD_PATH.unshift File.expand_path(File.dirname(__FILE__) + '/lib')
require 'resque/server'

use Rack::ShowExceptions

Resque::Server.use Rack::Auth::Basic do |username, password|
  password == 'secret' && username = 'admin'
end

run Resque::Server.new

Тут вы можете защитить паролем ваш сервер. В nginx нужно добавить еще одну секцию server:

server {
  listen   80;
  server_name  resque.example.com;
  root /home/deploy/resque/public;
  passenger_enabled on;
}

И после этого можно зайти на front-end в браузере и проверить, что всё ок.

Мониторинг очередей resque

Дальше установим и настроим integrity:

# cd /home/deploy
# git clone git://github.com/integrity/integrity.git
# cd integrity
# mkdir -p tmp/pids

Чтобы включить resque backend для integrity нужно раскоментировать соответствующую строку в Gemfile:

gem "resque"

Для nginx нужно добавить новую секцию server:

server {
  listen 80;
  error_log  logs/integrity.error.log info;
  server_name integrity.example.com;
  passenger_enabled on;
  root /home/deploy/integrity/public;
}

Мы используем следующий конфиг для integrity:

LOAD_PATH.unshift(File.join(File.dirname(__FILE__), "lib"))

require ".bundle/environment"
require "integrity"
require "integrity/notifier/email"

Integrity.configure do |c|
  c.database = "sqlite3:integrity.db"
  c.directory = "builds"
  c.base_url = "http://integrity.example.com"
  c.log = "integrity.log"
  c.github_token = "YOUR_SECRET_GITHUB_TOKEN"
  c.build_all = true
  c.builder = :resque
  c.auto_branch = false
  c.username = "admin"
  c.password = "secret"
end

Теперь осталось только запустить resque worker:

# rake resque:work

Только скорее всего эта команда у вас не запустится, потому что в integrity есть небольшой баг в Rakefile и, я надеюсь, что его скоро исправят. Если нет, то попробуйте вот этот патч:

diff --git a/Rakefile b/Rakefile
index e384f82..4af7479 100644
--- a/Rakefile
+++ b/Rakefile
@@ -53,11 +53,11 @@ end

begin
  namespace :resque do
+    require "init"
    require "resque/tasks"

    desc "Start a Resque worker for Integrity"
    task :work do
-      require "init"
      ENV["QUEUE"] = "integrity"
      Rake::Task["resque:resque:work"].invoke
    end

В github нужно добавить post-receive hook, чтобы тесты запускались каждый раз, когда кто-нибудь заливает новый код. Адрес должен быть в формате http://username:[email protected]/github/:github_token

Ещё можно настроить monit для мониторинга integrity workers. Пример конфигурации которую мы используем /etc/monit/conf.f/resque:

check process resque_worker_integrity
  with pidfile /home/deploy/integrity/tmp/pids/resque_worker_integrity.pid
  start program = "/bin/sh -c 'cd /home/deploy/integrity; COUNT=1 QUEUE=integrity VERBOSE=1 nohup /opt/ruby-enterprise/bin/rake resque:work& > log/resque_worker_integrity.log && echo $! > tmp/pids/resque_worker_integrity.pid'" as uid deploy and gid deploy
  stop program = "/bin/sh -c 'cd /home/deploy/integrity && kill -s QUIT `cat tmp/pids/resque_worker_integrity.pid` && rm -f tmp/pids/resque_worker_integrity.pid; exit 0;'"
  if totalmem is greater than 200 MB for 10 cycles then restart
  group resque_workers

Теперь monit будет следить чтобы было запущено не более одного workers и будет перезапускать их, если количество используемой памяти превышает 200 мегабайт. Если вы хотите использовать больше workers, то вам нужно позаботиться чтобы ваш тестовый rake task учитывал одновременный запуск нескольких копий проекта (использовал разные базы).

Вот и всё, теперь можно зайти на http://integrity.example.com и добавить ваш проект. Надеюсь эта статья поможет вам. Спасибо.

  1. Домашняя страница integrity
  2. Репозиторий cijoe
  3. Статья “Introducing Resque”
  4. Документация по resque
  5. Примеры конфигураций monit