capistrano-mb 0.22.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (51) hide show
  1. checksums.yaml +7 -0
  2. data/.gitignore +17 -0
  3. data/CHANGELOG.md +114 -0
  4. data/Gemfile +4 -0
  5. data/LICENSE.txt +22 -0
  6. data/README.md +173 -0
  7. data/Rakefile +5 -0
  8. data/capistrano-mb.gemspec +34 -0
  9. data/lib/capistrano/fiftyfive.rb +6 -0
  10. data/lib/capistrano/mb.rb +29 -0
  11. data/lib/capistrano/mb/compatibility.rb +17 -0
  12. data/lib/capistrano/mb/dsl.rb +187 -0
  13. data/lib/capistrano/mb/recipe.rb +48 -0
  14. data/lib/capistrano/mb/templates/crontab.erb +1 -0
  15. data/lib/capistrano/mb/templates/csr_config.erb +10 -0
  16. data/lib/capistrano/mb/templates/delayed_job_init.erb +36 -0
  17. data/lib/capistrano/mb/templates/logrotate.erb +9 -0
  18. data/lib/capistrano/mb/templates/maintenance.html.erb +26 -0
  19. data/lib/capistrano/mb/templates/nginx.erb +64 -0
  20. data/lib/capistrano/mb/templates/nginx_unicorn.erb +109 -0
  21. data/lib/capistrano/mb/templates/pgpass.erb +1 -0
  22. data/lib/capistrano/mb/templates/postgresql-backup-logrotate.erb +11 -0
  23. data/lib/capistrano/mb/templates/rbenv_bashrc +4 -0
  24. data/lib/capistrano/mb/templates/sidekiq_init.erb +100 -0
  25. data/lib/capistrano/mb/templates/ssl_setup +43 -0
  26. data/lib/capistrano/mb/templates/unicorn.rb.erb +71 -0
  27. data/lib/capistrano/mb/templates/unicorn_init.erb +84 -0
  28. data/lib/capistrano/mb/templates/version.rb.erb +3 -0
  29. data/lib/capistrano/mb/version.rb +5 -0
  30. data/lib/capistrano/tasks/aptitude.rake +101 -0
  31. data/lib/capistrano/tasks/crontab.rake +14 -0
  32. data/lib/capistrano/tasks/defaults.rake +122 -0
  33. data/lib/capistrano/tasks/delayed_job.rake +33 -0
  34. data/lib/capistrano/tasks/dotenv.rake +57 -0
  35. data/lib/capistrano/tasks/fiftyfive.rake +59 -0
  36. data/lib/capistrano/tasks/logrotate.rake +16 -0
  37. data/lib/capistrano/tasks/maintenance.rake +28 -0
  38. data/lib/capistrano/tasks/migrate.rake +29 -0
  39. data/lib/capistrano/tasks/nginx.rake +31 -0
  40. data/lib/capistrano/tasks/postgresql.rake +177 -0
  41. data/lib/capistrano/tasks/provision.rake +18 -0
  42. data/lib/capistrano/tasks/rake.rake +20 -0
  43. data/lib/capistrano/tasks/rbenv.rake +93 -0
  44. data/lib/capistrano/tasks/seed.rake +16 -0
  45. data/lib/capistrano/tasks/sidekiq.rake +39 -0
  46. data/lib/capistrano/tasks/ssl.rake +57 -0
  47. data/lib/capistrano/tasks/ufw.rake +32 -0
  48. data/lib/capistrano/tasks/unicorn.rake +42 -0
  49. data/lib/capistrano/tasks/user.rake +32 -0
  50. data/lib/capistrano/tasks/version.rake +34 -0
  51. metadata +165 -0
@@ -0,0 +1,71 @@
1
+ # Use at least one worker per core if you're on a dedicated server,
2
+ # more will usually help for _short_ waits on databases/caches.
3
+ worker_processes <%= fetch(:mb_unicorn_workers) %>
4
+
5
+ # Help ensure your application will always spawn in the symlinked
6
+ # "current" directory that Capistrano sets up.
7
+ working_directory "<%= current_path %>"
8
+
9
+ # listen on both a Unix domain socket
10
+ # we use a shorter backlog for quicker failover when busy
11
+ listen "/tmp/unicorn.<%= application_basename %>.sock", :backlog => 64
12
+
13
+ # nuke workers after <%= fetch(:mb_unicorn_timeout) %> seconds (default is 60)
14
+ timeout <%= fetch(:mb_unicorn_timeout) %>
15
+
16
+ pid "<%= fetch(:mb_unicorn_pid) %>"
17
+
18
+ # By default, the Unicorn logger will write to stderr.
19
+ # Additionally, some applications/frameworks log to stderr or stdout,
20
+ # so prevent them from going to /dev/null when daemonized here:
21
+ stderr_path "<%= fetch(:mb_unicorn_log) %>"
22
+ stdout_path "<%= fetch(:mb_unicorn_log) %>"
23
+
24
+ preload_app true
25
+
26
+ # combine Ruby 2.0.0dev or REE with "preload_app true" for memory savings
27
+ # http://rubyenterpriseedition.com/faq.html#adapt_apps_for_cow
28
+ if GC.respond_to?(:copy_on_write_friendly=)
29
+ GC.copy_on_write_friendly = true
30
+ end
31
+
32
+ before_exec do |server|
33
+ # Ensure unicorn picks up our newest Gemfile
34
+ ENV['BUNDLE_GEMFILE'] = "<%= current_path %>/Gemfile"
35
+ end
36
+
37
+ before_fork do |server, worker|
38
+
39
+ # the following is highly recomended for Rails + "preload_app true"
40
+ # as there's no need for the master process to hold a connection
41
+ if defined? ActiveRecord::Base
42
+ ActiveRecord::Base.connection.disconnect!
43
+ end
44
+
45
+ # This allows a new master process to incrementally
46
+ # phase out the old master process with SIGTTOU to avoid a
47
+ # thundering herd (especially in the "preload_app false" case)
48
+ # when doing a transparent upgrade. The last worker spawned
49
+ # will then kill off the old master process with a SIGQUIT.
50
+ old_pid = "#{server.config[:pid]}.oldbin"
51
+ if File.exists?(old_pid) && server.pid != old_pid
52
+ begin
53
+ sig = (worker.nr + 1) >= server.worker_processes ? :QUIT : :TTOU
54
+ Process.kill(sig, File.read(old_pid).to_i)
55
+ rescue Errno::ENOENT, Errno::ESRCH
56
+ end
57
+ end
58
+
59
+ # Throttle the master from forking too quickly by sleeping. Due
60
+ # to the implementation of standard Unix signal handlers, this
61
+ # helps (but does not completely) prevent identical, repeated signals
62
+ # from being lost when the receiving process is busy.
63
+ sleep 1
64
+ end
65
+
66
+ after_fork do |server, worker|
67
+ # the following is *required* for Rails + "preload_app true"
68
+ if defined?(ActiveRecord::Base)
69
+ ActiveRecord::Base.establish_connection
70
+ end
71
+ end
@@ -0,0 +1,84 @@
1
+ #!/bin/sh
2
+ ### BEGIN INIT INFO
3
+ # Provides: unicorn
4
+ # Required-Start: $remote_fs $syslog
5
+ # Required-Stop: $remote_fs $syslog
6
+ # Default-Start: 2 3 4 5
7
+ # Default-Stop: 0 1 6
8
+ # Short-Description: Manage unicorn server
9
+ # Description: Start, stop, restart unicorn server for a specific application.
10
+ ### END INIT INFO
11
+ set -e
12
+
13
+ # Feel free to change any of the following variables for your app:
14
+ TIMEOUT=${TIMEOUT-60}
15
+ APP_ROOT=<%= current_path %>
16
+ PID=<%= fetch(:mb_unicorn_pid) %>
17
+ CMD="cd <%= current_path %>; bundle exec unicorn -D -c <%= fetch(:mb_unicorn_config) %> -E <%= fetch(:rails_env) %>"
18
+ AS_USER=<%= unicorn_user %>
19
+ set -u
20
+
21
+ OLD_PIN="$PID.oldbin"
22
+
23
+ sig () {
24
+ test -s "$PID" && kill -$1 `cat $PID`
25
+ }
26
+
27
+ oldsig () {
28
+ test -s $OLD_PIN && kill -$1 `cat $OLD_PIN`
29
+ }
30
+
31
+ run () {
32
+ if [ "$(id -un)" = "$AS_USER" ]; then
33
+ eval $1
34
+ else
35
+ su -c "$1" - $AS_USER
36
+ fi
37
+ }
38
+
39
+ case "$1" in
40
+ start)
41
+ sig 0 && echo >&2 "Already running" && exit 0
42
+ run "$CMD"
43
+ ;;
44
+ stop)
45
+ sig QUIT && exit 0
46
+ echo >&2 "Not running"
47
+ ;;
48
+ force-stop)
49
+ sig TERM && exit 0
50
+ echo >&2 "Not running"
51
+ ;;
52
+ restart|reload)
53
+ sig USR2 && echo reloaded OK && exit 0
54
+ echo >&2 "Couldn't reload, starting '$CMD' instead"
55
+ run "$CMD"
56
+ ;;
57
+ upgrade)
58
+ if sig USR2 && sleep 2 && sig 0 && oldsig QUIT
59
+ then
60
+ n=$TIMEOUT
61
+ while test -s $OLD_PIN && test $n -ge 0
62
+ do
63
+ printf '.' && sleep 1 && n=$(( $n - 1 ))
64
+ done
65
+ echo
66
+
67
+ if test $n -lt 0 && test -s $OLD_PIN
68
+ then
69
+ echo >&2 "$OLD_PIN still exists after $TIMEOUT seconds"
70
+ exit 1
71
+ fi
72
+ exit 0
73
+ fi
74
+ echo >&2 "Couldn't upgrade, starting '$CMD' instead"
75
+ run "$CMD"
76
+ ;;
77
+ reopen-logs)
78
+ sig USR1
79
+ ;;
80
+ *)
81
+ echo >&2 "Usage: $0 <start|stop|restart|upgrade|force-stop|reopen-logs>"
82
+ exit 1
83
+ ;;
84
+ esac
@@ -0,0 +1,3 @@
1
+ Rails.application.config.version = "<%= git_version[:tag] %>"
2
+ Rails.application.config.version_date = Date.parse("<%= git_version[:date] %>")
3
+ Rails.application.config.version_time = Time.zone.parse("<%= git_version[:time] %>")
@@ -0,0 +1,5 @@
1
+ module Capistrano
2
+ module MB
3
+ VERSION = "0.22.0"
4
+ end
5
+ end
@@ -0,0 +1,101 @@
1
+ mb_recipe :aptitude do
2
+ during :provision, %w(upgrade install)
3
+ before "provision:14_04", "mb:aptitude:install_postgres_repo"
4
+ before "provision:14_04", "mb:aptitude:change_postgres_packages"
5
+ end
6
+
7
+ namespace :mb do
8
+ namespace :aptitude do
9
+
10
+ desc "Run `aptitude update` and then run `aptitude safe-upgrade`"
11
+ task :upgrade do
12
+ privileged_on roles(:all) do |host|
13
+ _update
14
+ _safe_upgrade
15
+ end
16
+ end
17
+
18
+
19
+ desc "Run `aptitude install` for packages required by the roles of "\
20
+ "each server."
21
+ task :install do
22
+ privileged_on roles(:all) do |host|
23
+ packages_to_install = []
24
+ repos_to_add = []
25
+
26
+ _each_package(host) do |pkg, repo|
27
+ unless _already_installed?(pkg)
28
+ repos_to_add << repo unless repo.nil?
29
+ packages_to_install << pkg
30
+ end
31
+ end
32
+
33
+ repos_to_add.uniq.each { |repo| _add_repository(repo) }
34
+ _update
35
+ packages_to_install.uniq.each { |pkg| _install(pkg) }
36
+ end
37
+ end
38
+
39
+ desc "Add the official apt repository for PostgreSQL"
40
+ task :install_postgres_repo do
41
+ privileged_on roles(:all) do |host|
42
+ _add_repository(
43
+ "deb http://apt.postgresql.org/pub/repos/apt/ trusty-pgdg main",
44
+ :key => "https://www.postgresql.org/media/keys/ACCC4CF8.asc")
45
+ end
46
+ end
47
+
48
+ desc "Change 12.04 PostgreSQL package requirements to 14.04 versions"
49
+ task :change_postgres_packages do
50
+ packages = fetch(:mb_aptitude_packages, {})
51
+ packages = Hash[packages.map do |key, value|
52
+ [key.sub(/@ppa:pitti\/postgresql$/, ""), value]
53
+ end]
54
+ set(:mb_aptitude_packages, packages)
55
+ end
56
+
57
+ def _already_installed?(pkg)
58
+ test(:sudo, "dpkg", "-s", pkg, "2>/dev/null", "|", :grep, "-q 'ok installed'")
59
+ end
60
+
61
+ def _add_repository(repo, options={})
62
+ unless _already_installed?("python-software-properties")
63
+ _install("python-software-properties")
64
+ end
65
+ execute :sudo, "apt-add-repository", "-y '#{repo}'"
66
+
67
+ if (key = options.fetch(:key, nil))
68
+ execute "wget --quiet -O - #{key} | sudo apt-key add -"
69
+ end
70
+ end
71
+
72
+ def _install(pkg)
73
+ with :debian_frontend => "noninteractive" do
74
+ execute :sudo, "aptitude", "-y -q install", pkg
75
+ end
76
+ end
77
+
78
+ def _update
79
+ with :debian_frontend => "noninteractive" do
80
+ execute :sudo, "aptitude", "-q -q -y update"
81
+ end
82
+ end
83
+
84
+ def _safe_upgrade
85
+ with :debian_frontend => "noninteractive" do
86
+ execute :sudo, "aptitude", "-q -q -y safe-upgrade"
87
+ end
88
+ end
89
+
90
+ def _each_package(host)
91
+ return to_enum(:_each_package, host) unless block_given?
92
+ hostname = host.hostname
93
+ fetch(:mb_aptitude_packages).each do |package_spec, *role_list|
94
+ next unless roles(*role_list.flatten).map(&:hostname).include?(hostname)
95
+
96
+ pkg, repo = package_spec.split("@")
97
+ yield(pkg, repo)
98
+ end
99
+ end
100
+ end
101
+ end
@@ -0,0 +1,14 @@
1
+ mb_recipe :crontab do
2
+ during :provision, "mb:crontab"
3
+ end
4
+
5
+ namespace :mb do
6
+ desc "Install crontab using crontab.erb template"
7
+ task :crontab do
8
+ on roles(:cron) do
9
+ tmp_file = "/tmp/crontab"
10
+ template "crontab.erb", tmp_file
11
+ execute "crontab #{tmp_file} && rm #{tmp_file}"
12
+ end
13
+ end
14
+ end
@@ -0,0 +1,122 @@
1
+ namespace :load do
2
+ task :defaults do
3
+
4
+ set :mb_recipes, %w(
5
+ aptitude
6
+ crontab
7
+ dotenv
8
+ logrotate
9
+ migrate
10
+ nginx
11
+ postgresql
12
+ rbenv
13
+ seed
14
+ ssl
15
+ ufw
16
+ unicorn
17
+ user
18
+ version
19
+ )
20
+
21
+ set :mb_privileged_user, "root"
22
+
23
+ set :mb_aptitude_packages,
24
+ "curl" => :all,
25
+ "debian-goodies" => :all,
26
+ "git-core" => :all,
27
+ "libpq-dev@ppa:pitti/postgresql" => :all,
28
+ "nginx@ppa:nginx/stable" => :web,
29
+ "nodejs@ppa:chris-lea/node.js" => :all,
30
+ "postgresql-client@ppa:pitti/postgresql" => :all,
31
+ "postgresql@ppa:pitti/postgresql" => :db,
32
+ "ufw" => :all
33
+
34
+ set :mb_delayed_job_args, "-n 2"
35
+ set :mb_delayed_job_script, "bin/delayed_job"
36
+
37
+ set :mb_dotenv_keys, %w(rails_secret_key_base postmark_api_key)
38
+ set :mb_dotenv_filename, -> { ".env.#{fetch(:rails_env)}" }
39
+
40
+ set :mb_log_file, "log/capistrano.log"
41
+
42
+ set :mb_nginx_force_https, false
43
+ set :mb_nginx_redirect_hosts, {}
44
+
45
+ ask :mb_postgresql_password, nil, :echo => false
46
+ set :mb_postgresql_max_connections, 25
47
+ set :mb_postgresql_pool_size, 5
48
+ set :mb_postgresql_host, "localhost"
49
+ set :mb_postgresql_database,
50
+ -> { "#{application_basename}_#{fetch(:rails_env)}" }
51
+ set :mb_postgresql_user, -> { application_basename }
52
+ set :mb_postgresql_pgpass_path,
53
+ proc{ "#{shared_path}/config/pgpass" }
54
+ set :mb_postgresql_backup_path, -> {
55
+ "#{shared_path}/backups/postgresql-dump.dmp"
56
+ }
57
+ set :mb_postgresql_backup_exclude_tables, []
58
+ set :mb_postgresql_dump_options, -> {
59
+ options = fetch(:mb_postgresql_backup_exclude_tables).map do |t|
60
+ "-T #{t.shellescape}"
61
+ end
62
+ options.join(" ")
63
+ }
64
+
65
+ set :mb_rbenv_ruby_version, -> { IO.read(".ruby-version").strip }
66
+ set :mb_rbenv_vars, -> {
67
+ {
68
+ "RAILS_ENV" => fetch(:rails_env),
69
+ "PGPASSFILE" => fetch(:mb_postgresql_pgpass_path)
70
+ }
71
+ }
72
+
73
+ set :mb_sidekiq_concurrency, 25
74
+ set :mb_sidekiq_role, :sidekiq
75
+
76
+ ask :mb_ssl_csr_country, "US"
77
+ ask :mb_ssl_csr_state, "California"
78
+ ask :mb_ssl_csr_city, "San Francisco"
79
+ ask :mb_ssl_csr_org, "Example Company"
80
+ ask :mb_ssl_csr_name, "www.example.com"
81
+
82
+ # WARNING: misconfiguring firewall rules could lock you out of the server!
83
+ set :mb_ufw_rules,
84
+ "allow ssh" => :all,
85
+ "allow http" => :web,
86
+ "allow https" => :web
87
+
88
+ set :mb_unicorn_workers, 2
89
+ set :mb_unicorn_timeout, 30
90
+ set :mb_unicorn_config, proc{ "#{current_path}/config/unicorn.rb" }
91
+ set :mb_unicorn_log, proc{ "#{current_path}/log/unicorn.log" }
92
+ set :mb_unicorn_pid, proc{ "#{current_path}/tmp/pids/unicorn.pid" }
93
+
94
+ set :bundle_binstubs, false
95
+ set :bundle_flags, "--deployment --retry=3"
96
+ set :deploy_to, -> { "/home/deployer/apps/#{fetch(:application)}" }
97
+ set :keep_releases, 10
98
+ set :linked_dirs, -> {
99
+ ["public/#{fetch(:assets_prefix, 'assets')}"] +
100
+ %w(
101
+ log
102
+ tmp/pids
103
+ tmp/cache
104
+ tmp/sockets
105
+ public/system
106
+ )
107
+ }
108
+ set :linked_files, -> {
109
+ [fetch(:mb_dotenv_filename)] +
110
+ %w(
111
+ config/database.yml
112
+ config/unicorn.rb
113
+ )
114
+ }
115
+ set :log_level, :debug
116
+ set :migration_role, :app
117
+ set :rails_env, -> { fetch(:stage) }
118
+ set :ssh_options, :compression => false, :keepalive => true
119
+
120
+ SSHKit.config.command_map[:rake] = "bundle exec rake"
121
+ end
122
+ end
@@ -0,0 +1,33 @@
1
+ mb_recipe :delayed_job do
2
+ during :provision, "init_d"
3
+ during "deploy:start", "start"
4
+ during "deploy:stop", "stop"
5
+ during "deploy:restart", "restart"
6
+ during "deploy:publishing", "restart"
7
+ end
8
+
9
+ namespace :mb do
10
+ namespace :delayed_job do
11
+ desc "Install delayed_job service script"
12
+ task :init_d do
13
+ privileged_on roles(:delayed_job) do |host, user|
14
+ template "delayed_job_init.erb",
15
+ "/etc/init.d/delayed_job_#{application_basename}",
16
+ :mode => "a+rx",
17
+ :binding => binding,
18
+ :sudo => true
19
+
20
+ execute "sudo update-rc.d -f delayed_job_#{application_basename} defaults"
21
+ end
22
+ end
23
+
24
+ %w[start stop restart].each do |command|
25
+ desc "#{command} delayed_job"
26
+ task command do
27
+ on roles(:delayed_job) do
28
+ execute "service delayed_job_#{application_basename} #{command}"
29
+ end
30
+ end
31
+ end
32
+ end
33
+ end
@@ -0,0 +1,57 @@
1
+ mb_recipe :dotenv do
2
+ during "provision", "update"
3
+ prior_to "deploy:publishing", "update"
4
+ end
5
+
6
+ namespace :mb do
7
+ namespace :dotenv do
8
+ desc "Replace/create .env file with values provided at console"
9
+ task :replace do
10
+ set_up_prompts
11
+
12
+ on release_roles(:all) do
13
+ update_dotenv_file
14
+ end
15
+ end
16
+
17
+ desc "Update .env file with any missing values"
18
+ task :update do
19
+ set_up_prompts
20
+
21
+ on release_roles(:all), :in => :sequence do
22
+ existing_env = if test("[ -f #{shared_dotenv_path} ]")
23
+ download!(shared_dotenv_path)
24
+ end
25
+ update_dotenv_file(existing_env.is_a?(String) ? existing_env : "")
26
+ end
27
+ end
28
+
29
+ def shared_dotenv_path
30
+ "#{shared_path}/#{fetch(:mb_dotenv_filename)}"
31
+ end
32
+
33
+ def set_up_prompts
34
+ fetch(:mb_dotenv_keys).each do |key|
35
+ if key.to_s =~ /key|token|secret|password/i
36
+ ask(key, nil, :echo => false)
37
+ else
38
+ ask(key, nil)
39
+ end
40
+ end
41
+ end
42
+
43
+ def update_dotenv_file(existing="")
44
+ updated = existing.dup
45
+
46
+ fetch(:mb_dotenv_keys).each do |key|
47
+ next if existing =~ /^#{Regexp.escape(key.upcase)}=/
48
+ updated << "\n" unless updated.end_with?("\n")
49
+ updated << "#{key.upcase}=#{fetch(key)}\n"
50
+ end
51
+
52
+ unless existing == updated
53
+ put(updated, shared_dotenv_path, :mode => "600")
54
+ end
55
+ end
56
+ end
57
+ end