dark-capistrano-recipes 0.6.4

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,62 @@
1
+ rails_root = '<%= "#{deploy_to}/current" %>'
2
+ rails_env = '<%= environment %>'
3
+ pid_file = '<%= unicorn_pid %>'
4
+ socket_file= '<%= unicorn_socket %>'
5
+ log_file = "#{rails_root}/log/unicorn.log"
6
+ username = '<%= unicorn_user %>'
7
+ group = '<%= unicorn_group %>'
8
+ old_pid = pid_file + '.oldbin'
9
+
10
+
11
+ timeout <%= unicorn_workers_timeout %>
12
+
13
+ worker_processes <%= unicorn_workers %>
14
+
15
+ # Listen on a Unix data socket
16
+ listen socket_file, :backlog => 1024
17
+ pid pid_file
18
+
19
+ stderr_path log_file
20
+ stdout_path log_file
21
+
22
+ preload_app true
23
+ ##
24
+ # REE
25
+
26
+ GC.copy_on_write_friendly = true if GC.respond_to?(:copy_on_write_friendly=)
27
+
28
+ before_fork do |server, worker|
29
+ # the following is highly recomended for Rails + "preload_app true"
30
+ # as there's no need for the master process to hold a connection
31
+ defined?(ActiveRecord::Base) and
32
+ ActiveRecord::Base.connection.disconnect!
33
+
34
+
35
+ ##
36
+ # When sent a USR2, Unicorn will suffix its pidfile with .oldbin and
37
+ # immediately start loading up a new version of itself (loaded with a new
38
+ # version of our app). When this new Unicorn is completely loaded
39
+ # it will begin spawning workers. The first worker spawned will check to
40
+ # see if an .oldbin pidfile exists. If so, this means we've just booted up
41
+ # a new Unicorn and need to tell the old one that it can now die. To do so
42
+ # we send it a QUIT.
43
+ #
44
+ # Using this method we get 0 downtime deploys.
45
+
46
+ if File.exists?(old_pid) && server.pid != old_pid
47
+ begin
48
+ Process.kill("QUIT", File.read(old_pid).to_i)
49
+ rescue Errno::ENOENT, Errno::ESRCH
50
+ # someone else did our job for us
51
+ end
52
+ end
53
+ end
54
+
55
+
56
+ after_fork do |server, worker|
57
+ defined?(ActiveRecord::Base) and
58
+ ActiveRecord::Base.establish_connection
59
+
60
+
61
+ worker.user(username, group) if Process.euid == 0 && rails_env == 'production'
62
+ end
@@ -0,0 +1,5 @@
1
+ require 'capistrano'
2
+ require 'capistrano/cli'
3
+ require 'helpers'
4
+
5
+ Dir.glob(File.join(File.dirname(__FILE__), '/recipes/*.rb')).sort.each { |f| load f }
data/lib/helpers.rb ADDED
@@ -0,0 +1,70 @@
1
+ # =========================================================================
2
+ # These are helper methods that will be available to your recipes.
3
+ # =========================================================================
4
+
5
+ # automatically sets the environment based on presence of
6
+ # :stage (multistage gem), :rails_env, or RAILS_ENV variable; otherwise defaults to 'production'
7
+ def environment
8
+ if exists?(:stage)
9
+ stage
10
+ elsif exists?(:rails_env)
11
+ rails_env
12
+ elsif(ENV['RAILS_ENV'])
13
+ ENV['RAILS_ENV']
14
+ else
15
+ "production"
16
+ end
17
+ end
18
+
19
+ def is_using_nginx
20
+ is_using('nginx',:web_server)
21
+ end
22
+
23
+ def is_using_unicorn
24
+ is_using('unicorn',:server)
25
+ end
26
+
27
+ def is_using_god
28
+ is_using('god',:monitorer)
29
+ end
30
+
31
+ def is_using(something, with_some_var)
32
+ exists?(with_some_var.to_sym) && fetch(with_some_var.to_sym).to_s.downcase == something
33
+ end
34
+
35
+ # Path to where the generators live
36
+ def templates_path
37
+ expanded_path_for('../generators')
38
+ end
39
+
40
+ def docs_path
41
+ expanded_path_for('../doc')
42
+ end
43
+
44
+ def expanded_path_for(path)
45
+ e = File.join(File.dirname(__FILE__),path)
46
+ File.expand_path(e)
47
+ end
48
+
49
+ def parse_config(file)
50
+ require 'erb' #render not available in Capistrano 2
51
+ template=File.read(file) # read it
52
+ return ERB.new(template).result(binding) # parse it
53
+ end
54
+
55
+ # Generates a configuration file parsing through ERB
56
+ # Fetches local file and uploads it to remote_file
57
+ # Make sure your user has the right permissions.
58
+ def generate_config(local_file,remote_file)
59
+ temp_file = '/tmp/' + File.basename(local_file)
60
+ buffer = parse_config(local_file)
61
+ File.open(temp_file, 'w+') { |f| f << buffer }
62
+ upload temp_file, remote_file, :via => :scp
63
+ `rm #{temp_file}`
64
+ end
65
+
66
+ # Execute a rake task, example:
67
+ # run_rake log:clear
68
+ def run_rake(task)
69
+ run "cd #{current_path} && rake #{task} RAILS_ENV=#{environment}"
70
+ end
@@ -0,0 +1,68 @@
1
+ Capistrano::Configuration.instance(:must_exist).load do
2
+
3
+ # User settings
4
+ set :user, 'deploy' unless exists?(:user)
5
+ set :group,'www-data' unless exists?(:group)
6
+
7
+
8
+ # Server settings
9
+ set :server, :unicorn unless exists?(:server)
10
+ set :web_server, :nginx unless exists?(:web_server)
11
+ set :runner, user unless exists?(:runner)
12
+
13
+ # The port to listen to, normally we default to 80
14
+ set :application_port, 80 unless exists?(:application_port)
15
+
16
+ # Are we using ssl as well? Used by generators to configure a ssl host
17
+ set :application_uses_ssl, true unless exists?(:application_uses_ssl)
18
+
19
+ # The port to listen to https requests, usually 443
20
+ set :application_port_ssl, 443 unless exists?(:application_port_ssl)
21
+
22
+ # Database settings
23
+ set :database, :mysql unless exists?(:database)
24
+
25
+ # SCM settings
26
+ set :scm, :git
27
+ set :branch, 'master' unless exists?(:branch)
28
+ set :deploy_to, "/var/www/apps/#{application}" unless exists?(:deploy_to)
29
+ set :deploy_via, :remote_cache
30
+ set :keep_releases, 3
31
+ set :git_enable_submodules, true
32
+ set :rails_env, 'production' unless exists?(:rails_env)
33
+ set :use_sudo, false
34
+
35
+ # Git settings for capistrano
36
+ default_run_options[:pty] = true
37
+ ssh_options[:forward_agent] = true
38
+
39
+ # RVM settings
40
+ set :using_rvm, true unless exists?(:using_rvm)
41
+ # Sets the rvm to a specific version (or whatever env you want it to run in)
42
+ set :rvm_ruby_string, 'ree' unless exists?(:rvm_ruby_string)
43
+
44
+ # Daemons settings
45
+ # The unix socket that unicorn will be attached to.
46
+ # Also, nginx will upstream to this guy.
47
+ # The *nix place for socks is /var/run, so we should probably put it there
48
+ # Make sure the runner can access this though.
49
+ set :sockets_path, "/var/run/#{application}" unless exists?(:sockets_path)
50
+
51
+ # Just to be safe, put the pid somewhere that survives deploys. shared/pids is
52
+ # a good choice as any.
53
+ set(:pids_path) { File.join(shared_path, "pids") } unless exists?(:pids_path)
54
+
55
+ set :monitorer, 'god' unless exists?(:monitorer)
56
+
57
+ # Application settings
58
+ set :shared_dirs, %w( config uploads backup bundle tmp ) unless exists?(:shared_dirs)
59
+
60
+ namespace :app do
61
+ task :setup, :roles => :app do
62
+ commands = shared_dirs.map do |path|
63
+ "if [ ! -d '#{path}' ]; then mkdir -p #{path}; fi;"
64
+ end
65
+ run "cd #{shared_path}; #{commands.join(' ')}"
66
+ end
67
+ end
68
+ end
@@ -0,0 +1,19 @@
1
+ Bundler
2
+ # Manages gems in a sane way.
3
+ # http://gembundler.com/
4
+ Capistrano::Configuration.instance(:must_exist).load do
5
+ namespace :bundler do
6
+ set :bundler_ver, '1.0.0'
7
+ desc "Installs bundler gem to your server"
8
+ task :setup, :roles => :app do
9
+ run "if ! gem list | grep --silent -e 'bundler.*#{bundler_ver}'; then #{try_sudo} gem uninstall bundler; #{try_sudo} gem install --no-rdoc --no-ri bundler; fi"
10
+ end
11
+
12
+ # [internal] runs bundle install on the app server
13
+ task :install, :roles => :app, :except => { :no_release => true } do
14
+ run "cd #{current_path} && bundle install --deployment --without=development test"
15
+ end
16
+ end
17
+
18
+ end
19
+
data/lib/recipes/db.rb ADDED
@@ -0,0 +1,105 @@
1
+ require 'erb'
2
+
3
+ Capistrano::Configuration.instance(:must_exist).load do
4
+ namespace :db do
5
+ namespace :mysql do
6
+
7
+ # Sets database variables from remote database.yaml
8
+ def prepare_from_yaml
9
+ set(:db_file) { "#{application}-dump.sql.bz2" }
10
+ set(:db_remote_file) { "#{shared_path}/backup/#{db_file}" }
11
+ set(:db_local_file) { "tmp/#{db_file}" }
12
+ set(:db_user) { db_config[rails_env]["username"] }
13
+ set(:db_pass) { db_config[rails_env]["password"] }
14
+ set(:db_host) { db_config[rails_env]["host"] }
15
+ set(:db_name) { db_config[rails_env]["database"] }
16
+ end
17
+
18
+ def db_config
19
+ @db_config ||= fetch_db_config
20
+ end
21
+
22
+ def fetch_db_config
23
+ require 'yaml'
24
+ file = capture "cat #{shared_path}/config/database.yml"
25
+ db_config = YAML.load(file)
26
+ end
27
+
28
+ desc <<-EOF
29
+ Performs a compressed database dump. \
30
+ WARNING: This locks your tables for the duration of the mysqldump.
31
+ Don't run it madly!
32
+ EOF
33
+ task :dump, :roles => :db, :only => { :primary => true } do
34
+ prepare_from_yaml
35
+ run "mysqldump --user=#{db_user} -p --host=#{db_host} #{db_name} | bzip2 -z9 > #{db_remote_file}" do |ch, stream, out|
36
+ ch.send_data "#{db_pass}\n" if out =~ /^Enter password:/
37
+ puts out
38
+ end
39
+ end
40
+
41
+ desc "Restores the database from the latest compressed dump"
42
+ task :restore, :roles => :db, :only => { :primary => true } do
43
+ prepare_from_yaml
44
+ run "bzcat #{db_remote_file} | mysql --user=#{db_user} -p --host=#{db_host} #{db_name}" do |ch, stream, out|
45
+ ch.send_data "#{db_pass}\n" if out =~ /^Enter password:/
46
+ puts out
47
+ end
48
+ end
49
+
50
+ desc "Downloads the compressed database dump to this machine"
51
+ task :fetch_dump, :roles => :db, :only => { :primary => true } do
52
+ prepare_from_yaml
53
+ download db_remote_file, db_local_file, :via => :scp
54
+ end
55
+
56
+
57
+ desc "Create MySQL database and user for this environment using prompted values"
58
+ task :setup, :roles => :db, :only => { :primary => true } do
59
+ prepare_for_db_command
60
+
61
+ sql = <<-SQL
62
+ CREATE DATABASE #{db_name};
63
+ GRANT ALL PRIVILEGES ON #{db_name}.* TO #{db_user}@localhost IDENTIFIED BY '#{db_pass}';
64
+ SQL
65
+
66
+ run "mysql --user=#{db_admin_user} -p --execute=\"#{sql}\"" do |channel, stream, data|
67
+ if data =~ /^Enter password:/
68
+ pass = Capistrano::CLI.password_prompt "Enter database password for '#{db_admin_user}':"
69
+ channel.send_data "#{pass}\n"
70
+ end
71
+ end
72
+ end
73
+ end
74
+
75
+ desc "Create database.yml in shared path with settings for current stage and test env"
76
+ task :create_yaml do
77
+ set(:db_user) { Capistrano::CLI.ui.ask "Enter #{environment} database username:" }
78
+ set(:db_pass) { Capistrano::CLI.password_prompt "Enter #{environment} database password:" }
79
+
80
+ db_config = ERB.new <<-EOF
81
+ base: &base
82
+ adapter: mysql
83
+ username: #{db_user}
84
+ password: #{db_pass}
85
+
86
+ #{environment}:
87
+ database: #{application}_#{environment}
88
+ <<: *base
89
+
90
+ test:
91
+ database: #{application}_test
92
+ <<: *base
93
+ EOF
94
+
95
+ put db_config.result, "#{shared_path}/config/database.yml"
96
+ end
97
+ end
98
+
99
+ def prepare_for_db_command
100
+ set :db_name, "#{application}_#{environment}"
101
+ set(:db_admin_user) { Capistrano::CLI.ui.ask "Username with priviledged database access (to create db):" }
102
+ set(:db_user) { Capistrano::CLI.ui.ask "Enter #{environment} database username:" }
103
+ set(:db_pass) { Capistrano::CLI.password_prompt "Enter #{environment} database password:" }
104
+ end
105
+ end
@@ -0,0 +1,105 @@
1
+ Capistrano::Configuration.instance(:must_exist).load do
2
+ set :shared_children, %w(system log pids config)
3
+
4
+ after "deploy:setup" do
5
+ db.create_yaml if Capistrano::CLI.ui.agree("Create database.yml in app's shared path?")
6
+ end
7
+
8
+
9
+ namespace :deploy do
10
+ desc "Deploy it, github-style."
11
+ task :default do
12
+ update
13
+ restart
14
+ end
15
+
16
+ desc "[Seppuku] Destroy everything"
17
+ task :seppuku do
18
+ run "rm -rf #{current_path}; rm -rf #{shared_path}"
19
+ rubycas.seppuku
20
+ end
21
+
22
+ task :setup_dirs, :except => { :no_release => true } do
23
+ commands = shared_dirs.map do |path|
24
+ "mkdir -p #{shared_path}/#{path}"
25
+ end
26
+ run commands.join(" && ")
27
+ end
28
+
29
+ desc "Uploads your local config.yml to the server"
30
+ task :configure, :except => { :no_release => true } do
31
+ generate_config('config/config.yml', "#{shared_path}/config/config.yml")
32
+ end
33
+
34
+ desc "Setup a GitHub-style deployment."
35
+ task :setup, :except => { :no_release => true } do
36
+ run "rm -rf #{current_path}"
37
+ setup_dirs
38
+ run "git clone #{repository} #{current_path}"
39
+ end
40
+
41
+ desc "Update the deployed code."
42
+ task :update_code, :except => { :no_release => true } do
43
+ run "cd #{current_path}; git fetch origin; git reset --hard #{branch}"
44
+ end
45
+
46
+ task :symlink, :except => { :no_release => true } do
47
+ symlinks.make
48
+ end
49
+
50
+ desc "[Obsolete] Nothing to cleanup when using reset --hard on git"
51
+ task :cleanup, :except => { :no_release => true } do
52
+ #nothing to cleanup, we're not working with 'releases'
53
+ puts "Nothing to cleanup, yay!"
54
+ end
55
+
56
+ namespace :rollback do
57
+ desc "Rollback a single commit."
58
+ task :default, :except => { :no_release => true } do
59
+ set :branch, "HEAD^"
60
+ deploy.default
61
+ end
62
+ end
63
+
64
+ desc <<-DESC
65
+ Restarts your application. This depends heavily on what server you're running.
66
+ If you are running Phusion Passenger, you can explicitly set the server type:
67
+
68
+ set :server, :passenger
69
+
70
+ ...which will touch tmp/restart.txt, a file monitored by Passenger.
71
+
72
+ If you are running Unicorn, you can set:
73
+
74
+ set :server, :unicorn
75
+
76
+ ...which will use unicorn signals for restarting its workers.
77
+
78
+ Otherwise, this command will call the script/process/reaper \
79
+ script under the current path.
80
+
81
+ If you are running with Unicorn, you can set the server type as well:
82
+
83
+ set :server, :unicorn
84
+
85
+ By default, this will be invoked via sudo as the `app' user. If \
86
+ you wish to run it as a different user, set the :runner variable to \
87
+ that user. If you are in an environment where you can't use sudo, set \
88
+ the :use_sudo variable to false:
89
+
90
+ set :use_sudo, false
91
+ DESC
92
+ task :restart, :roles => :app, :except => { :no_release => true } do
93
+ if exists?(:server)
94
+ case fetch(:server).to_s.downcase
95
+ when 'passenger'
96
+ passenger.bounce
97
+ when 'unicorn'
98
+ is_using_god ? god.restart_unicorn : unicorn.restart
99
+ end
100
+ else
101
+ puts "Dunno how to restart your internets! kthx!"
102
+ end
103
+ end
104
+ end
105
+ end
@@ -0,0 +1,114 @@
1
+ # God Monitoring Tool
2
+ # God is a Process Monitoring Framework written in Ruby
3
+ # More info at http://god.rubyforge.org/
4
+ #------------------------------------------------------------------------------
5
+ Capistrano::Configuration.instance(:must_exist).load do
6
+ _cset(:god_local_config) { "#{templates_path}/app.god.erb" }
7
+ _cset(:god_remote_config) { "#{shared_path}/config/app.god" }
8
+
9
+ namespace :god do
10
+
11
+ desc "Parses and uploads god configuration for this app"
12
+ task :setup do
13
+ generate_config(god_local_config, god_remote_config)
14
+ end
15
+
16
+ _cset(:bin_god) { defined?(:rvm_ruby_string) ? 'bootup_god' : 'god' }
17
+ _cset(:server_name) { "#{application}-unicorn" }
18
+ _cset(:god_init_local) { "#{docs_path}/god/god.init" }
19
+ _cset :god_init_temp, '/tmp/god.init'
20
+ _cset :god_init_remote, '/etc/init.d/god'
21
+ _cset(:god_defo_local) { "#{docs_path}/god/god"}
22
+ _cset :god_defo_temp, '/tmp/god'
23
+ _cset :god_defo_remote, '/etc/default/god'
24
+ _cset(:god_conf_local) { "#{docs_path}/god/god.conf" }
25
+ _cset :god_conf_temp, '/tmp/god.conf'
26
+ _cset :god_conf_remote, '/etc/god/god.conf'
27
+
28
+
29
+ task :setup_temp, :roles => :app do
30
+ sudo "rm -f #{god_conf_remote} #{god_init_remote} #{god_defo_remote}"
31
+ end
32
+
33
+ task :setup_conf, :roles => :app do
34
+ upload god_conf_local, god_conf_temp, :via => :scp
35
+ sudo "mkdir -p #{File.dirname(god_conf_remote)}"
36
+ sudo "mv #{god_conf_temp} #{god_conf_remote}"
37
+ end
38
+
39
+ task :setup_init, :roles => :app do
40
+ upload god_init_local, god_init_temp, :via => :scp
41
+ sudo "mv #{god_init_temp} #{god_init_remote}"
42
+ # Allow executing the init.d script
43
+ sudo "chmod +x #{god_init_remote}"
44
+ # Make it run at bootup
45
+ sudo "update-rc.d god defaults"
46
+ end
47
+
48
+ task :setup_defo, :roles => :app do
49
+ upload god_defo_local, god_defo_temp, :via => :scp
50
+ sudo "mv #{god_defo_temp} #{god_defo_remote}"
51
+ end
52
+
53
+ desc "Bootstraps god on your server. Be careful with this."
54
+ task :bootstrap, :roles => :app do
55
+ setup_temp
56
+ setup_defo
57
+ setup_init
58
+ setup_conf
59
+
60
+ puts "God is bootstrapped. To remove use 'cap god:implode'"
61
+ end
62
+
63
+ desc "(Seppuku) Completely remove god from the system init"
64
+ task :implode, :roles => :app do
65
+ # Removing any system startup links for /etc/init.d/god ...
66
+ sudo "update-rc.d -f god remove"
67
+
68
+ # Suicide follows.
69
+ sudo "rm -f #{god_conf_remote}"
70
+ sudo "rm -f #{god_defo_remote}"
71
+ sudo "rm -f #{god_init_remote}"
72
+ puts "God is no more."
73
+ end
74
+
75
+ task :restart_unicorn, :roles => :app do
76
+ sudo "#{bin_god} restart #{server_name}"
77
+ end
78
+
79
+ task :log, :roles => :app do
80
+ sudo "#{bin_god} log #{application}"
81
+ end
82
+
83
+ desc "Reload config"
84
+ task :reload, :roles => :app do
85
+ sudo "#{bin_god} load #{god_remote_config}"
86
+ end
87
+
88
+ desc "Start god service"
89
+ task :start, :roles => :app do
90
+ sudo "service god start"
91
+ end
92
+
93
+ desc "Stops god service"
94
+ task :stop, :roles => :app do
95
+ sudo "service god stop"
96
+ end
97
+
98
+ desc "Quit god, but not the processes it's monitoring"
99
+ task :quit, :roles => :app do
100
+ sudo "#{bin_god} quit"
101
+ end
102
+
103
+ desc "Terminate god and all monitored processes"
104
+ task :terminate, :roles => :app do
105
+ sudo "#{bin_god} terminate"
106
+ end
107
+
108
+ desc "Describe the status of the running tasks"
109
+ task :status, :roles => :app do
110
+ sudo "#{bin_god} status"
111
+ end
112
+ end
113
+ after 'deploy:setup', 'god:setup' if is_using_god
114
+ end