catalyst で作ったアプリを capistrano でデプロイ
capistrano インストール
gem install capistrano
Capfile を配置
capify コマンドで Capfile と config/deploy.rb を作ってくれるが、今回は自前で用意する。
好きなディレクトリに下記内容の Capfile ファイルを配置する。
catalyst.pl コマンドで作ったディレクトリの直下が解りやすいかも。
load 'conf/deploy'
これで、conf/deploy.rb を読み込んでくれる。
deploy.rb を配置
Capfile で指定した path に下記内容の deploy.rb ファイルを配置する。
require 'capistrano/recipes/deploy/scm' require 'capistrano/recipes/deploy/strategy' set :application, "AppName" set :repository, "http://domain/path/to/AppName/" set :scm, :subversion set :deploy_via, :checkout set(:deploy_to) { "/path/to/#{application}" } set(:revision) { source.head } set(:source) { Capistrano::Deploy::SCM.new(scm, self) } set(:real_revision) { source.local.query_revision(revision) { |cmd| with_env("LC_ALL", "C") {`#{cmd}`} } } set(:strategy) { Capistrano::Deploy::Strategy.new(deploy_via, self) } set(:release_name) { set :deploy_timestamped, true; Time.now.utc.strftime("%Y%m%d%H%M%S") } set(:releases_path) { File.join(deploy_to, "releases") } set(:current_path) { File.join(deploy_to, "current") } set(:release_path) { File.join(releases_path, release_name) } set(:releases) { capture("ls -x #{releases_path}").split.sort } set(:current_release) { File.join(releases_path, releases.last) } set(:previous_release) { File.join(releases_path, releases[-2]) } set(:current_revision) { capture("cat #{current_path}/REVISION").chomp } set(:latest_revision) { capture("cat #{current_release}/REVISION").chomp } set(:previous_revision) { capture("cat #{previous_release}/REVISION").chomp} set(:latest_release) { exists?(:deploy_timestamped) ? release_path : current_release } set(:run_method) { fetch(:use_sudo, true) ? :sudo : :run } def with_env(name, value) saved, ENV[name] = ENV[name], value yield ensure ENV[name] = saved end role :servers, "domain name or ip address", "domain name or ip address" namespace :deploy do desc "deploy." task :default do update restart end task :update do transaction do update_code symlink end end task :update_code, :except => { :no_release => true } do on_rollback { run "rm -rf #{release_path}; true" } strategy.deploy! finalize_update end task :finalize_update, :except => { :no_release => true } do stamp = Time.now.utc.strftime("%Y%m%d%H%M.%S") asset_paths = %w(images css js).map { |p| "#{latest_release}/root/static/#{p}" }.join(" ") run "find #{asset_paths} -exec touch -t #{stamp} {} ';'; true", :env => { "TZ" => "UTC" } end task :symlink, :except => { :no_release => true } do on_rollback { run "rm -f #{current_path}; ln -s #{previous_release} #{current_path}; true" } run "rm -f #{current_path} && ln -s #{latest_release} #{current_path}" end task :restart do sudo "/usr/local/etc/rc.d/apache22 stop" sudo "/usr/local/etc/rc.d/apache22 start" end desc "rollback." task :rollback do rollback_code restart end task :rollback_code, :except => { :no_release => true } do if releases.length < 2 abort "could not rollback the code because there is no prior release" else run "rm #{current_path}; ln -s #{previous_release} #{current_path} && rm -rf #{current_release}" end end desc "setup." task :setup, :except => { :no_release => true } do dirs = [deploy_to, releases_path] run "umask 02 && mkdir -p #{dirs.join(' ')}" end desc "cleanup." task :cleanup, :except => { :no_release => true } do count = fetch(:keep_releases, 5).to_i if count >= releases.length logger.important "no old releases to clean up" else logger.info "keeping #{count} of #{releases.length} deployed releases" directories = (releases - releases.last(count)).map { |release| File.join(releases_path, release) }.join(" ") invoke_command "rm -rf #{directories}", :via => run_method end end end
capistrano/recipes/deploy.rb まる写し。
cap の引数 | 説明 |
---|---|
deploy | サーバにアプリを配置して、apache を再起動 |
deploy:rollback | リリースを一世代戻して、apache を再起動 |
deploy:setup | サーバに配置先のディレクトリを作成する |
deploy:cleanup | 五世代前のリリースを削除する |
role を app と web に分けたら実用に近づきそう。
古い subversion を使っていると、svn info を実行する際にエラーが出るので注意。