capistrano-mb 0.22.0

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.
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