capistrano-unicorn 0.1.10 → 0.2.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA1:
3
+ metadata.gz: 1a058f515bf172a37d40eedbd47982db1374ffb3
4
+ data.tar.gz: d9e4825ca605a00f559985fd5cbfbac9768cb958
5
+ SHA512:
6
+ metadata.gz: fb193dc5a958641c7febf145c9586b3186b1727fbd59e615022283fba9d7d32f109bd6e2f249f9f8930407f5229bb30e99ee0fa5574384c4a3de16e994d77b1f
7
+ data.tar.gz: 3821b7cf00a08cc83d068881caae0fae99132b091f4ea71d07fb3eeef73ffb6b7af559bf0d57da6300ae79fcb9396a56793166cba3b742ffabec254c68a05676
data/.rspec ADDED
@@ -0,0 +1,2 @@
1
+ --color
2
+ --format=documentation
@@ -0,0 +1,4 @@
1
+ rvm:
2
+ - 1.9.2
3
+ - 1.9.3
4
+ - 2.0.0
data/Gemfile CHANGED
@@ -1,3 +1,9 @@
1
1
  source 'https://rubygems.org'
2
2
 
3
- gemspec
3
+ gemspec
4
+
5
+ group :test do
6
+ gem 'rspec'
7
+ gem 'capistrano-spec'
8
+ gem 'pry-debugger'
9
+ end
data/NEWS.md ADDED
@@ -0,0 +1,27 @@
1
+ # Capistrano Unicorn NEWS
2
+
3
+ ## 0.2.0
4
+
5
+ Significant changes since 0.1.10 are as follows. Backwards-incompatible changes are **in bold**.
6
+
7
+ * Ensured `RAILS_ENV` is set correctly.
8
+ * Embedded multistage docs directly within the [README](README.md) (the wiki page was version-specific and misleading).
9
+ * Significantly tidied up the usage and documentation of configuration variables:
10
+ * **In most cases, it should now be sufficient to simply set `rails_env` correctly,
11
+ and other variables should assume the correct value by default.**
12
+ * **Make `unicorn_env` default to `rails_env` or `'production'`.**
13
+ * **Rename `app_env` to `unicorn_rack_env` and fix default value.**
14
+ * Add `unicorn_options` variable which allows passing of arbitrary options to unicorn.
15
+ * Added `app_subdir` to support app running in a subdirectory.
16
+ * Updated documentation in [README](README.md) to fix inaccuracies and ambiguities.
17
+ * `unicorn_pid` defaults to attempting to auto-detect from unicorn config file.
18
+ This avoids having to keep two paths in sync.
19
+ https://github.com/sosedoff/capistrano-unicorn/issues/7
20
+ * Also added the `unicorn:show_vars` task to make it easier to debug
21
+ config variable values client-side.
22
+ * Defer calculation of `unicorn-roles`.
23
+
24
+ It was noticed that there are a
25
+ [huge number of unmerged forks on github](https://github.com/sosedoff/capistrano-unicorn/issues/45),
26
+ so we also updated the [README](README.md) asking the community to
27
+ contribute back any useful changes they make.
data/README.md CHANGED
@@ -2,16 +2,18 @@
2
2
 
3
3
  Capistrano plugin that integrates Unicorn tasks into capistrano deployment script.
4
4
 
5
- ## Installation
5
+ **Developers:** Please consider contributing your forked changes, or opening an
6
+ issue if there is no existing relevant one. There are a lot of forks--we'd love
7
+ to reabsorb some of the issues/solutions the community has encountered.
6
8
 
7
- Install library from rubygems:
8
-
9
- ```
10
- gem install capistrano-unicorn
11
- ```
9
+ [![Build Status](https://travis-ci.org/sosedoff/capistrano-unicorn.png?branch=master)](https://travis-ci.org/sosedoff/capistrano-unicorn)
10
+ [![Gem Version](https://badge.fury.io/rb/capistrano-unicorn.png)](http://badge.fury.io/rb/capistrano-unicorn)
11
+ [![Code Climate](https://codeclimate.com/github/sosedoff/capistrano-unicorn.png)](https://codeclimate.com/github/sosedoff/capistrano-unicorn)
12
12
 
13
13
  ## Usage
14
14
 
15
+ If you are upgrading from a previous version, please see the [NEWS file](NEWS.md).
16
+
15
17
  ### Setup
16
18
 
17
19
  Add the library to your `Gemfile`:
@@ -31,15 +33,18 @@ require 'capistrano-unicorn'
31
33
  Add unicorn restart task hook:
32
34
 
33
35
  ```ruby
34
- after 'deploy:restart', 'unicorn:reload' # app IS NOT preloaded
35
- after 'deploy:restart', 'unicorn:restart' # app preloaded
36
+ after 'deploy:restart', 'unicorn:reload' # app IS NOT preloaded
37
+ after 'deploy:restart', 'unicorn:restart' # app preloaded
38
+ after 'deploy:restart', 'unicorn:duplicate' # before_fork hook implemented (zero downtime deployments)
36
39
  ```
37
40
 
38
- Create a new configuration file `config/unicorn.rb` or `config/unicorn/STAGE.rb`, where stage is your deployment environment.
41
+ Create a new configuration file `config/unicorn.rb` or `config/unicorn/STAGE.rb`,
42
+ where stage is your deployment environment.
39
43
 
40
- Example config - [examples/rails3.rb](https://github.com/sosedoff/capistrano-unicorn/blob/master/examples/rails3.rb). Please refer to unicorn documentation for more examples and configuration options.
44
+ Example config - [examples/rails3.rb](https://github.com/sosedoff/capistrano-unicorn/blob/master/examples/rails3.rb).
45
+ Please refer to Unicorn documentation for more examples and configuration options.
41
46
 
42
- ### Test
47
+ ### Deploy
43
48
 
44
49
  First, make sure you're running the latest release:
45
50
 
@@ -57,20 +62,43 @@ cap unicorn:reload
57
62
 
58
63
  ## Configuration
59
64
 
60
- You can modify any of the following options in your `deploy.rb` config.
65
+ You can modify any of the following Capistrano variables in your `deploy.rb` config.
66
+ You can use the `unicorn:show_vars` task to check that the values you have specified
67
+ are set correctly.
61
68
 
62
- - `unicorn_env` - Set unicorn environment. Default to `rails_env` variable.
63
- - `unicorn_pid` - Set unicorn PID file path. Default to `current_path/tmp/pids/unicorn.pid`
64
- - `unicorn_bin` - Set unicorn executable file. Default to `unicorn`.
65
- - `unicorn_bundle` - Set bundler command for unicorn. Default to `bundle`.
66
- - `unicorn_user` - Launch unicorn master as the specified user. Default to `user` variable.
67
- - `unicorn_roles` - Define which roles to perform unicorn recpies on. Default to `:app`.
68
- - `unicorn_config_path` - Set the directory where unicorn config files reside. Default to `current_path/config`.
69
- - `unicorn_config_filename` - Set the filename of the unicorn config file. Not used in multistage installations. Default to `unicorn.rb`.
69
+ ### Environment parameters
70
70
 
71
- ### Multistage
71
+ - `unicorn_env` - Set basename of unicorn config `.rb` file to be used loaded from `unicorn_config_path`. Defaults to `rails_env` variable if set, otherwise `production`.
72
+ - `unicorn_rack_env` - Set the value which will be passed to unicorn via [the `-E` parameter as the Rack environment](http://unicorn.bogomips.org/unicorn_1.html). Valid values are `development`, `deployment`, and `none`. Defaults to `development` if `rails_env` is `development`, otherwise `deployment`.
73
+
74
+ ### Execution parameters
75
+
76
+ - `unicorn_user` - Launch unicorn master as the specified user via `sudo`. Defaults to `nil`, which means no use of `sudo`, i.e. run as the user defined by the `user` variable.
77
+ - `unicorn_roles` - Define which roles to perform unicorn recipes on. Defaults to `:app`.
78
+ - `unicorn_bundle` - Set bundler command for unicorn. Defaults to `bundle`.
79
+ - `unicorn_bin` - Set unicorn executable file. Defaults to `unicorn`.
80
+ - `unicorn_options` - Set any additional options to be passed to unicorn on startup.
81
+ - `unicorn_restart_sleep_time` - Number of seconds to wait for (old) pidfile to show up when restarting unicorn. Defaults to 2.
82
+
83
+ ### Relative path parameters
84
+
85
+ - `app_subdir` - If your app lives in a subdirectory 'rails' (say) of your repository, set this to `/rails` (the leading slash is required).
86
+ - `unicorn_config_rel_path` - Set the directory path (relative to `app_path` - see below) where unicorn config files reside. Defaults to `config`.
87
+ - `unicorn_config_filename` - Set the filename of the unicorn config file loaded from `unicorn_config_path`. Should not be present in multistage installations. Defaults to `unicorn.rb`.
72
88
 
73
- If you are using capistrano multistage, please refer to [Using capistrano unicorn with multistage environment](https://github.com/sosedoff/capistrano-unicorn/wiki/Using-capistrano-unicorn-with-multistage-environment).
89
+ ### Absolute path parameters
90
+
91
+ - `app_path` - Set path to app root. Defaults to `current_path + app_subdir`.
92
+ - `unicorn_pid` - Set unicorn PID file path. By default, attempts to auto-detect from unicorn config file. On failure, falls back to value in `unicorn_default_pid`
93
+ - `unicorn_default_pid` - See above. Defaults to `#{current_path}/tmp/pids/unicorn.pid`
94
+ - `bundle_gemfile` - Set path to Gemfile. Defaults to `#{app_path}/Gemfile`
95
+ - `unicorn_config_path` - Set the directory where unicorn config files reside. Defaults to `#{current_path}/config`.
96
+
97
+ ### Zero Downtime Deployment Options
98
+
99
+ * `unicorn:restart`: :-1: This can sort of support it with a configurable timeout, which may not be reliable.
100
+ * `unicorn:reload`: :question: Can anyone testify to its zero-downtime support?
101
+ * `unicorn:duplicate`: :+1: If you install the Unicorn `before_fork` hook, then yes! See: https://github.com/sosedoff/capistrano-unicorn/issues/40#issuecomment-16011353
74
102
 
75
103
  ## Available Tasks
76
104
 
@@ -81,11 +109,69 @@ cap unicorn:add_worker # Add a new worker
81
109
  cap unicorn:remove_worker # Remove amount of workers
82
110
  cap unicorn:reload # Reload Unicorn
83
111
  cap unicorn:restart # Restart Unicorn
112
+ cap unicorn:show_vars # Debug Unicorn variables
84
113
  cap unicorn:shutdown # Immediately shutdown Unicorn
85
114
  cap unicorn:start # Start Unicorn master process
86
115
  cap unicorn:stop # Stop Unicorn
87
116
  ```
88
117
 
118
+ ## Tests
119
+
120
+ To execute test suite run:
121
+
122
+ ```
123
+ bundle exec rake test
124
+ ```
125
+
126
+ ### Multistage
127
+
128
+ The issue here is that capistrano loads default configuration and then
129
+ executes your staging task and overrides previously defined
130
+ variables. The default environment before executing your stage task is
131
+ set to `:production`, so it will use a wrong environment unless you
132
+ take steps to ensure that `:rails_env` and `:unicorn_env` are
133
+ set correctly.
134
+
135
+ Let's say you have a scenario involving two deployment stages: staging
136
+ and production. You’ll need to add `config/deploy/staging.rb` and
137
+ `config/deploy/production.rb` files. However, it makes sense to
138
+ adhere to DRY and avoid duplicating lines between the two files. So
139
+ it would be nicer to keep common settings in `config/deploy.rb`, and
140
+ only put stuff in each staging definition file which is really
141
+ specific to that staging environment. Fortunately this can be done
142
+ using the [lazy evaluation form of `set`](https://github.com/capistrano/capistrano/wiki/2.x-DSL-Configuration-Variables-Set).
143
+
144
+ So `config/deploy.rb` file would contain something like:
145
+
146
+ ```ruby
147
+ set :stages, %w(production staging)
148
+ set :default_stage, "staging"
149
+ require 'capistrano/ext/multistage'
150
+
151
+ role(:web) { domain }
152
+ role(:app) { domain }
153
+ role(:db, :primary => true) { domain }
154
+
155
+ set(:deploy_to) { "/home/#{user}/#{application}/#{fetch :rails_env}" }
156
+ set(:current_path) { File.join(deploy_to, current_dir) }
157
+ ```
158
+
159
+ Then `config/deploy/production.rb` would contain something like:
160
+
161
+ ```ruby
162
+ set :domain, "app.mydomain.com"
163
+ set :rails_env, "production"
164
+ ```
165
+
166
+ and `config/deploy/staging.rb` would only need to contain something like:
167
+
168
+ ```ruby
169
+ set :domain, "app.staging.mydomain.com"
170
+ set :rails_env, "staging"
171
+ ```
172
+
173
+ Nice and clean!
174
+
89
175
  ## License
90
176
 
91
177
  See LICENSE file for details.
data/Rakefile CHANGED
@@ -1,2 +1,10 @@
1
- require 'bundler'
2
- require 'bundler/gem_tasks'
1
+ require "bundler"
2
+ require "bundler/gem_tasks"
3
+ require "rspec/core/rake_task"
4
+
5
+
6
+ RSpec::Core::RakeTask.new(:test) do |spec|
7
+ spec.pattern = "spec/**/*_spec.rb"
8
+ end
9
+
10
+ task :default => :test
@@ -1,21 +1,22 @@
1
1
  # -*- encoding: utf-8 -*-
2
- require File.expand_path('../lib/capistrano-unicorn/version', __FILE__)
2
+ require File.expand_path("../lib/capistrano-unicorn/version", __FILE__)
3
3
 
4
- Gem::Specification.new do |gem|
5
- gem.name = 'capistrano-unicorn'
6
- gem.version = CapistranoUnicorn::VERSION.dup
7
- gem.author = 'Dan Sosedoff'
8
- gem.email = 'dan.sosedoff@gmail.com'
9
- gem.homepage = 'https://github.com/sosedoff/capistrano-unicorn'
10
- gem.summary = %q{Unicorn integration for Capistrano}
11
- gem.description = %q{Capistrano plugin that integrates Unicorn server tasks.}
4
+ Gem::Specification.new do |spec|
5
+ spec.name = "capistrano-unicorn"
6
+ spec.version = CapistranoUnicorn::VERSION.dup
7
+ spec.author = "Dan Sosedoff"
8
+ spec.email = "dan.sosedoff@gmail.com"
9
+ spec.homepage = "https://github.com/sosedoff/capistrano-unicorn"
10
+ spec.summary = %q{Unicorn integration for Capistrano}
11
+ spec.description = %q{Capistrano plugin that integrates Unicorn server tasks.}
12
+ spec.license = "MIT"
12
13
 
13
- gem.files = `git ls-files`.split("\n")
14
- gem.test_files = `git ls-files -- {test,spec,features}/*`.split("\n")
15
- gem.executables = `git ls-files -- bin/*`.split("\n").map{|f| File.basename(f)}
16
- gem.require_paths = ['lib']
14
+ spec.files = `git ls-files`.split("\n")
15
+ spec.test_files = `git ls-files -- {test,spec,features}/*`.split("\n")
16
+ spec.executables = `git ls-files -- bin/*`.split("\n").map{ |f| File.basename(f) }
17
+ spec.require_paths = ["lib"]
17
18
 
18
- gem.add_development_dependency 'rake'
19
-
20
- gem.add_runtime_dependency 'capistrano'
19
+ spec.add_development_dependency "rake"
20
+ spec.add_development_dependency "unicorn"
21
+ spec.add_runtime_dependency "capistrano", "< 3.0"
21
22
  end
@@ -1,6 +1,11 @@
1
+ require 'tempfile'
2
+
1
3
  require 'capistrano'
2
4
  require 'capistrano/version'
3
5
 
6
+ require 'capistrano-unicorn/config'
7
+ require 'capistrano-unicorn/utility'
8
+
4
9
  module CapistranoUnicorn
5
10
  class CapistranoIntegration
6
11
  TASKS = [
@@ -11,142 +16,57 @@ module CapistranoUnicorn
11
16
  'unicorn:reload',
12
17
  'unicorn:shutdown',
13
18
  'unicorn:add_worker',
14
- 'unicorn:remove_worker'
19
+ 'unicorn:remove_worker',
20
+ 'unicorn:show_vars',
15
21
  ]
16
22
 
17
23
  def self.load_into(capistrano_config)
18
24
  capistrano_config.load do
19
- before(CapistranoIntegration::TASKS) do
20
- _cset(:app_env) { (fetch(:rails_env) rescue 'production') }
21
- _cset(:unicorn_pid) { "#{fetch(:current_path)}/tmp/pids/unicorn.pid" }
22
- _cset(:unicorn_env) { fetch(:app_env) }
23
- _cset(:unicorn_bin) { "unicorn" }
24
- _cset(:unicorn_bundle) { fetch(:bundle_cmd) rescue 'bundle' }
25
- _cset(:unicorn_restart_sleep_time) { 2 }
26
- _cset(:unicorn_user) { nil }
27
- _cset(:unicorn_config_path) { "#{fetch(:current_path)}/config" }
28
- _cset(:unicorn_config_filename) { "unicorn.rb" }
29
- end
30
-
31
- # Check if a remote process exists using its pid file
32
- #
33
- def remote_process_exists?(pid_file)
34
- "[ -e #{pid_file} ] && #{try_unicorn_user} kill -0 `cat #{pid_file}` > /dev/null 2>&1"
35
- end
36
-
37
- # Stale Unicorn process pid file
38
- #
39
- def old_unicorn_pid
40
- "#{unicorn_pid}.oldbin"
41
- end
42
25
 
43
- # Command to check if Unicorn is running
44
- #
45
- def unicorn_is_running?
46
- remote_process_exists?(unicorn_pid)
47
- end
48
-
49
- # Command to check if stale Unicorn is running
50
- #
51
- def old_unicorn_is_running?
52
- remote_process_exists?(old_unicorn_pid)
53
- end
54
-
55
- # Get unicorn master process PID (using the shell)
56
- #
57
- def get_unicorn_pid(pid_file=unicorn_pid)
58
- "`cat #{pid_file}`"
59
- end
60
-
61
- # Get unicorn master (old) process PID
62
- #
63
- def get_old_unicorn_pid
64
- get_unicorn_pid(old_unicorn_pid)
65
- end
66
-
67
- # Send a signal to a unicorn master processes
68
- #
69
- def unicorn_send_signal(signal, pid=get_unicorn_pid)
70
- "#{try_unicorn_user} kill -s #{signal} #{pid}"
71
- end
72
-
73
- # Run a command as the :unicorn_user user if :unicorn_user is a string.
74
- # Otherwise run as default (:user) user.
75
- #
76
- def try_unicorn_user
77
- "#{sudo :as => unicorn_user.to_s}" if unicorn_user.kind_of?(String)
78
- end
79
-
80
- # Kill Unicorns in multiple ways O_O
81
- #
82
- def kill_unicorn(signal)
83
- script = <<-END
84
- if #{unicorn_is_running?}; then
85
- echo "Stopping Unicorn...";
86
- #{unicorn_send_signal(signal)};
87
- else
88
- echo "Unicorn is not running.";
89
- fi;
90
- END
91
-
92
- script
93
- end
94
-
95
- # Start the Unicorn server
96
- #
97
- def start_unicorn
98
- primary_config_path = "#{unicorn_config_path}/#{unicorn_config_filename}"
99
- secondary_config_path = "#{unicorn_config_path}/unicorn/#{unicorn_env}.rb"
100
-
101
- script = <<-END
102
- if [ -e #{primary_config_path} ]; then
103
- UNICORN_CONFIG_PATH=#{primary_config_path};
104
- else
105
- if [ -e #{secondary_config_path} ]; then
106
- UNICORN_CONFIG_PATH=#{secondary_config_path};
107
- else
108
- echo "Config file for \"#{unicorn_env}\" environment was not found at either \"#{primary_config_path}\" or \"#{secondary_config_path}\"";
109
- exit 1;
110
- fi;
111
- fi;
112
-
113
- if [ -e #{unicorn_pid} ]; then
114
- if #{try_unicorn_user} kill -0 `cat #{unicorn_pid}` > /dev/null 2>&1; then
115
- echo "Unicorn is already running!";
116
- exit 0;
117
- fi;
118
-
119
- #{try_unicorn_user} rm #{unicorn_pid};
120
- fi;
121
-
122
- echo "Starting Unicorn...";
123
- cd #{current_path} && #{try_unicorn_user} BUNDLE_GEMFILE=#{current_path}/Gemfile #{unicorn_bundle} exec #{unicorn_bin} -c $UNICORN_CONFIG_PATH -E #{app_env} -D;
124
- END
125
-
126
- script
127
- end
128
-
129
- def duplicate_unicorn
130
- script = <<-END
131
- if #{unicorn_is_running?}; then
132
- echo "Duplicating Unicorn...";
133
- #{unicorn_send_signal('USR2')};
134
- else
135
- #{start_unicorn}
136
- fi;
137
- END
138
-
139
- script
26
+ before(CapistranoIntegration::TASKS) do
27
+ Config.load(self)
140
28
  end
141
29
 
142
- def unicorn_roles
143
- fetch(:unicorn_roles, :app)
144
- end
30
+ extend Utility
145
31
 
146
32
  #
147
33
  # Unicorn cap tasks
148
34
  #
149
35
  namespace :unicorn do
36
+ desc 'Debug Unicorn variables'
37
+ task :show_vars, :roles => :app do
38
+ puts <<-EOF.gsub(/^ +/, '')
39
+
40
+ # Environments
41
+ rails_env "#{rails_env}"
42
+ unicorn_env "#{unicorn_env}"
43
+ unicorn_rack_env "#{unicorn_rack_env}"
44
+
45
+ # Execution
46
+ unicorn_user #{unicorn_user.inspect}
47
+ unicorn_bundle "#{unicorn_bundle}"
48
+ unicorn_bin "#{unicorn_bin}"
49
+ unicorn_options "#{unicorn_options}"
50
+ unicorn_restart_sleep_time #{unicorn_restart_sleep_time}
51
+
52
+ # Relative paths
53
+ app_subdir "#{app_subdir}"
54
+ unicorn_config_rel_path "#{unicorn_config_rel_path}"
55
+ unicorn_config_filename "#{unicorn_config_filename}"
56
+ unicorn_config_rel_file_path "#{unicorn_config_rel_file_path}"
57
+ unicorn_config_stage_rel_file_path "#{unicorn_config_stage_rel_file_path}"
58
+
59
+ # Absolute paths
60
+ app_path "#{app_path}"
61
+ unicorn_pid "#{unicorn_pid}"
62
+ bundle_gemfile "#{bundle_gemfile}"
63
+ unicorn_config_path "#{unicorn_config_path}"
64
+ unicorn_config_file_path "#{unicorn_config_file_path}"
65
+ unicorn_config_stage_file_path
66
+ -> "#{unicorn_config_stage_file_path}"
67
+ EOF
68
+ end
69
+
150
70
  desc 'Start Unicorn master process'
151
71
  task :start, :roles => unicorn_roles, :except => {:no_release => true} do
152
72
  run start_unicorn
@@ -0,0 +1,52 @@
1
+ module CapistranoUnicorn
2
+ class Config
3
+ def self.load(config)
4
+ config.instance_eval do
5
+ # Environments
6
+ _cset(:unicorn_env) { fetch(:rails_env, 'production' ) }
7
+ _cset(:unicorn_rack_env) do
8
+ # Following recommendations from http://unicorn.bogomips.org/unicorn_1.html
9
+ fetch(:rails_env) == 'development' ? 'development' : 'deployment'
10
+ end
11
+
12
+ # Execution
13
+ _cset(:unicorn_user) { nil }
14
+ _cset(:unicorn_bundle) { fetch(:bundle_cmd, "bundle") }
15
+ _cset(:unicorn_bin) { "unicorn" }
16
+ _cset(:unicorn_options) { '' }
17
+ _cset(:unicorn_restart_sleep_time) { 2 }
18
+
19
+ # Relative paths
20
+ _cset(:app_subdir) { '' }
21
+ _cset(:unicorn_config_rel_path) { "config" }
22
+ _cset(:unicorn_config_filename) { "unicorn.rb" }
23
+ _cset(:unicorn_config_rel_file_path) \
24
+ { unicorn_config_rel_path + '/' + unicorn_config_filename }
25
+ _cset(:unicorn_config_stage_rel_file_path) \
26
+ { [ unicorn_config_rel_path, 'unicorn',
27
+ "#{unicorn_env}.rb" ].join('/') }
28
+
29
+ # Absolute paths
30
+ # If you find the following confusing, try running 'cap unicorn:show_vars' -
31
+ # it might help :-)
32
+ _cset(:app_path) { current_path + app_subdir }
33
+ _cset(:bundle_gemfile) { app_path + '/Gemfile' }
34
+ _cset(:unicorn_config_path) { app_path + '/' + unicorn_config_rel_path }
35
+ _cset(:unicorn_config_file_path) { app_path + '/' + unicorn_config_rel_file_path }
36
+ _cset(:unicorn_config_stage_file_path) \
37
+ { app_path + '/' + unicorn_config_stage_rel_file_path }
38
+ _cset(:unicorn_default_pid) { app_path + "/tmp/pids/unicorn.pid" }
39
+ _cset(:unicorn_pid) do
40
+ extracted_pid = extract_pid_file
41
+ if extracted_pid
42
+ extracted_pid
43
+ else
44
+ logger.important "err :: failed to auto-detect pid from #{local_unicorn_config}"
45
+ logger.important "err :: falling back to default: #{unicorn_default_pid}"
46
+ unicorn_default_pid
47
+ end
48
+ end
49
+ end
50
+ end
51
+ end
52
+ end
@@ -0,0 +1,147 @@
1
+ module CapistranoUnicorn
2
+ module Utility
3
+
4
+ def local_unicorn_config
5
+ File.exist?(unicorn_config_rel_file_path) ?
6
+ unicorn_config_rel_file_path
7
+ : unicorn_config_stage_rel_file_path
8
+ end
9
+
10
+ def extract_pid_file
11
+ tmp = Tempfile.new('unicorn.rb')
12
+ begin
13
+ conf = local_unicorn_config
14
+ tmp.write <<-EOF.gsub(/^ */, '')
15
+ config_file = "#{conf}"
16
+
17
+ # stub working_directory to avoid chdir failure since this will
18
+ # run client-side:
19
+ def working_directory(path); end
20
+
21
+ instance_eval(File.read(config_file), config_file) if config_file
22
+ puts set[:pid]
23
+ exit 0
24
+ EOF
25
+ tmp.close
26
+ extracted_pid = `unicorn -c "#{tmp.path}"`
27
+ $?.success? ? extracted_pid.rstrip : nil
28
+ rescue StandardError => e
29
+ return nil
30
+ ensure
31
+ tmp.close
32
+ tmp.unlink
33
+ end
34
+ end
35
+
36
+ # Check if a remote process exists using its pid file
37
+ #
38
+ def remote_process_exists?(pid_file)
39
+ "[ -e #{pid_file} ] && #{try_unicorn_user} kill -0 `cat #{pid_file}` > /dev/null 2>&1"
40
+ end
41
+
42
+ # Stale Unicorn process pid file
43
+ #
44
+ def old_unicorn_pid
45
+ "#{unicorn_pid}.oldbin"
46
+ end
47
+
48
+ # Command to check if Unicorn is running
49
+ #
50
+ def unicorn_is_running?
51
+ remote_process_exists?(unicorn_pid)
52
+ end
53
+
54
+ # Command to check if stale Unicorn is running
55
+ #
56
+ def old_unicorn_is_running?
57
+ remote_process_exists?(old_unicorn_pid)
58
+ end
59
+
60
+ # Get unicorn master process PID (using the shell)
61
+ #
62
+ def get_unicorn_pid(pid_file=unicorn_pid)
63
+ "`cat #{pid_file}`"
64
+ end
65
+
66
+ # Get unicorn master (old) process PID
67
+ #
68
+ def get_old_unicorn_pid
69
+ get_unicorn_pid(old_unicorn_pid)
70
+ end
71
+
72
+ # Send a signal to a unicorn master processes
73
+ #
74
+ def unicorn_send_signal(signal, pid=get_unicorn_pid)
75
+ "#{try_unicorn_user} kill -s #{signal} #{pid}"
76
+ end
77
+
78
+ # Run a command as the :unicorn_user user if :unicorn_user is a string.
79
+ # Otherwise run as default (:user) user.
80
+ #
81
+ def try_unicorn_user
82
+ "#{sudo :as => unicorn_user.to_s}" if unicorn_user.kind_of?(String)
83
+ end
84
+
85
+ # Kill Unicorns in multiple ways O_O
86
+ #
87
+ def kill_unicorn(signal)
88
+ script = <<-END
89
+ if #{unicorn_is_running?}; then
90
+ echo "Stopping Unicorn...";
91
+ #{unicorn_send_signal(signal)};
92
+ else
93
+ echo "Unicorn is not running.";
94
+ fi;
95
+ END
96
+
97
+ script
98
+ end
99
+
100
+ # Start the Unicorn server
101
+ #
102
+ def start_unicorn
103
+ %Q%
104
+ if [ -e "#{unicorn_config_file_path}" ]; then
105
+ UNICORN_CONFIG_PATH=#{unicorn_config_file_path};
106
+ else
107
+ if [ -e "#{unicorn_config_stage_file_path}" ]; then
108
+ UNICORN_CONFIG_PATH=#{unicorn_config_stage_file_path};
109
+ else
110
+ echo "Config file for "#{unicorn_env}" environment was not found at either "#{unicorn_config_file_path}" or "#{unicorn_config_stage_file_path}"";
111
+ exit 1;
112
+ fi;
113
+ fi;
114
+
115
+ if [ -e "#{unicorn_pid}" ]; then
116
+ if #{try_unicorn_user} kill -0 `cat #{unicorn_pid}` > /dev/null 2>&1; then
117
+ echo "Unicorn is already running!";
118
+ exit 0;
119
+ fi;
120
+
121
+ #{try_unicorn_user} rm #{unicorn_pid};
122
+ fi;
123
+
124
+ echo "Starting Unicorn...";
125
+ cd #{app_path} && #{try_unicorn_user} RAILS_ENV=#{rails_env} BUNDLE_GEMFILE=#{bundle_gemfile} #{unicorn_bundle} exec #{unicorn_bin} -c $UNICORN_CONFIG_PATH -E #{unicorn_rack_env} -D #{unicorn_options};
126
+ %
127
+ end
128
+
129
+ def duplicate_unicorn
130
+ script = <<-END
131
+ if #{unicorn_is_running?}; then
132
+ echo "Duplicating Unicorn...";
133
+ #{unicorn_send_signal('USR2')};
134
+ else
135
+ #{start_unicorn}
136
+ fi;
137
+ END
138
+
139
+ script
140
+ end
141
+
142
+ def unicorn_roles
143
+ defer{ fetch(:unicorn_roles, :app) }
144
+ end
145
+
146
+ end
147
+ end
@@ -1,5 +1,5 @@
1
1
  module CapistranoUnicorn
2
2
  unless defined?(::CapistranoUnicorn::VERSION)
3
- VERSION = "0.1.10".freeze
3
+ VERSION = "0.2.0".freeze
4
4
  end
5
5
  end
@@ -0,0 +1,84 @@
1
+ require "spec_helper"
2
+
3
+ describe CapistranoUnicorn::CapistranoIntegration, "loaded tasks into capistrano" do
4
+ before do
5
+ @configuration = Capistrano::Configuration.new
6
+ @configuration.extend(Capistrano::Spec::ConfigurationExtension)
7
+ CapistranoUnicorn::CapistranoIntegration.load_into(@configuration)
8
+ end
9
+
10
+ shared_examples_for "a task" do |task_name|
11
+ it "sets attributes in before_task hook" do
12
+ # Environments
13
+ @configuration.should_receive(:_cset).with(:unicorn_env)
14
+ @configuration.should_receive(:_cset).with(:unicorn_rack_env)
15
+
16
+ # Execution
17
+ @configuration.should_receive(:_cset).with(:unicorn_user)
18
+ @configuration.should_receive(:_cset).with(:unicorn_bundle)
19
+ @configuration.should_receive(:_cset).with(:unicorn_bin)
20
+ @configuration.should_receive(:_cset).with(:unicorn_options)
21
+ @configuration.should_receive(:_cset).with(:unicorn_restart_sleep_time)
22
+
23
+ # Relative paths
24
+ @configuration.should_receive(:_cset).with(:app_subdir)
25
+ @configuration.should_receive(:_cset).with(:unicorn_config_rel_path)
26
+ @configuration.should_receive(:_cset).with(:unicorn_config_filename)
27
+ @configuration.should_receive(:_cset).with(:unicorn_config_rel_file_path)
28
+ @configuration.should_receive(:_cset).with(:unicorn_config_stage_rel_file_path)
29
+
30
+ # Absolute paths
31
+ @configuration.should_receive(:_cset).with(:app_path)
32
+ @configuration.should_receive(:_cset).with(:unicorn_pid)
33
+ @configuration.should_receive(:_cset).with(:bundle_gemfile)
34
+ @configuration.should_receive(:_cset).with(:unicorn_config_path)
35
+ @configuration.should_receive(:_cset).with(:unicorn_config_file_path)
36
+ @configuration.should_receive(:_cset).with(:unicorn_config_stage_file_path)
37
+
38
+ @configuration.find_and_execute_task(task_name)
39
+ end
40
+ end
41
+
42
+ describe "task" do
43
+ describe 'unicorn:start' do
44
+ before do
45
+ @configuration.stub(:start_unicorn)
46
+ @configuration.stub(:_cset)
47
+ end
48
+
49
+ it_behaves_like "a task", 'unicorn:start'
50
+
51
+ it "runs start_unicorn command" do
52
+ @configuration.should_receive(:start_unicorn).and_return("start unicorn command")
53
+ @configuration.find_and_execute_task('unicorn:start')
54
+ @configuration.should have_run("start unicorn command")
55
+ end
56
+ end
57
+
58
+ describe 'unicorn:stop' do
59
+ before do
60
+ @configuration.stub(:kill_unicorn)
61
+ @configuration.stub(:_cset)
62
+ end
63
+
64
+ it_behaves_like "a task", 'unicorn:stop'
65
+
66
+ it "runs kill_unicorn command" do
67
+ @configuration.should_receive(:kill_unicorn).with('QUIT').and_return("kill unicorn command")
68
+ @configuration.find_and_execute_task('unicorn:stop')
69
+ @configuration.should have_run("kill unicorn command")
70
+ end
71
+ end
72
+ end
73
+
74
+ describe "#kill_unicorn" do
75
+ before do
76
+ @configuration.stub(:unicorn_pid).and_return(999)
77
+ @configuration.stub(:unicorn_user).and_return("deploy_user")
78
+ end
79
+
80
+ it "generates the kill unicorn command" do
81
+ @configuration.kill_unicorn('QUIT').should match /-u deploy_user kill -s QUIT `cat 999`;/
82
+ end
83
+ end
84
+ end
@@ -0,0 +1,134 @@
1
+ require 'spec_helper'
2
+ describe CapistranoUnicorn::Config, "loaded into a configuration" do
3
+ before do
4
+ @configuration = Capistrano::Configuration.new
5
+ @configuration.extend(Capistrano::Spec::ConfigurationExtension)
6
+ CapistranoUnicorn::CapistranoIntegration.load_into(@configuration)
7
+ end
8
+
9
+ context "testing variables" do
10
+ before do
11
+ # define _cset etc. from capistrano
12
+ @configuration.load 'deploy'
13
+
14
+ # capistrano-unicorn variables are set during a 'before'
15
+ # callback, so in order to be able to test the result, we need
16
+ # to ensure the callback is triggered.
17
+ @configuration.trigger :before
18
+ end
19
+
20
+ describe "app paths" do
21
+ cur_path = '/path/to/myapp'
22
+
23
+ before do
24
+ @configuration.set(:current_path, cur_path)
25
+ end
26
+
27
+ shared_examples_for "an app in path" do |app_path|
28
+ let(:shell) { :` } # ` } work around confused emacs ruby-mode
29
+
30
+ specify "app_path should default to #{app_path}" do
31
+ @configuration.fetch(:app_path).should == app_path
32
+ end
33
+
34
+ it "should default to a sensible pid file when auto-detection failed" do
35
+ @configuration.should_receive(shell).with(/unicorn -c /).and_return('') do |cmd|
36
+ `false` # Simulate failure by setting $?
37
+ end
38
+ @configuration.logger.stub(:important)
39
+ @configuration.fetch(:unicorn_pid).should == app_path + "/tmp/pids/unicorn.pid"
40
+ end
41
+
42
+ shared_examples "auto-detect pid file from unicorn config" do
43
+ |pid_file, primary_exists, config_file|
44
+ which_config = primary_exists ? 'primary' : 'stage'
45
+ it "should auto-detect pid file from #{which_config} unicorn config" do
46
+ # Tempfile.new in Ruby 1.9.2 will call File.exist?
47
+ allow(File).to receive(:exist?).with(/tmp/)
48
+
49
+ File.should_receive(:exist?).with('config/unicorn.rb').and_return(primary_exists)
50
+ tmpfile = nil
51
+ @configuration.should_receive(shell).with(/unicorn -c /) do |cmd|
52
+ (cmd =~ /^unicorn -c "(.+)"$/).should be_true
53
+ tmpfile = $~[1]
54
+ tmpfile.should include("tmp")
55
+ File.read(tmpfile).should include(%!config_file = "#{config_file}"!)
56
+ `true` # Simulate success by setting $?
57
+ pid_file
58
+ end
59
+ @configuration.fetch(:unicorn_pid).should == pid_file
60
+ end
61
+ end
62
+
63
+ include_examples "auto-detect pid file from unicorn config", \
64
+ '/path/to/pid/from/config/file', true, "config/unicorn.rb"
65
+
66
+ include_examples "auto-detect pid file from unicorn config", \
67
+ '/path/to/pid/from/stage/config/file', false, "config/unicorn/production.rb"
68
+
69
+ specify "Gemfile should default correctly" do
70
+ @configuration.fetch(:bundle_gemfile).should == app_path + "/Gemfile"
71
+ end
72
+
73
+ specify "config/ directory should default correctly" do
74
+ @configuration.fetch(:unicorn_config_path).should == app_path + "/config"
75
+ end
76
+
77
+ specify "config file should default correctly" do
78
+ @configuration.fetch(:unicorn_config_file_path).should == app_path + "/config/unicorn.rb"
79
+ end
80
+
81
+ specify "per-stage config file should default correctly" do
82
+ @configuration.fetch(:unicorn_config_stage_file_path).should == app_path + "/config/unicorn/production.rb"
83
+ end
84
+
85
+ specify "per-stage config file should be set correctly for different environment" do
86
+ @configuration.set(:rails_env, 'staging')
87
+ @configuration.fetch(:unicorn_config_stage_file_path).should == app_path + "/config/unicorn/staging.rb"
88
+ end
89
+ end
90
+
91
+ context "app in current_path" do
92
+ it_should_behave_like "an app in path", cur_path
93
+ end
94
+
95
+ context "app in a subdirectory" do
96
+ subdir = 'mysubdir'
97
+
98
+ before do
99
+ @configuration.set(:app_subdir, '/' + subdir)
100
+ end
101
+
102
+ it_should_behave_like "an app in path", cur_path + '/' + subdir
103
+ end
104
+ end
105
+
106
+ describe "unicorn_env" do
107
+ it "should default to value of rails_env if set" do
108
+ @configuration.set(:rails_env, 'staging')
109
+ @configuration.fetch(:unicorn_env).should == \
110
+ @configuration.fetch(:rails_env)
111
+ end
112
+
113
+ it "should default to production if rails_env not set" do
114
+ @configuration.fetch(:unicorn_env).should == 'production'
115
+ end
116
+ end
117
+
118
+ describe "unicorn_rack_env" do
119
+ it "should default to deployment if rails_env not set" do
120
+ @configuration.fetch(:unicorn_rack_env).should == 'deployment'
121
+ end
122
+
123
+ it "should default to development if rails_env set to development" do
124
+ @configuration.set(:rails_env, 'development')
125
+ @configuration.fetch(:unicorn_rack_env).should == 'development'
126
+ end
127
+
128
+ it "should default to deployment if rails_env set to anything else" do
129
+ @configuration.set(:rails_env, 'staging')
130
+ @configuration.fetch(:unicorn_rack_env).should == 'deployment'
131
+ end
132
+ end
133
+ end
134
+ end
@@ -0,0 +1,7 @@
1
+ require 'capistrano-spec'
2
+ require 'capistrano-unicorn'
3
+
4
+ RSpec.configure do |config|
5
+ config.include Capistrano::Spec::Matchers
6
+ config.include Capistrano::Spec::Helpers
7
+ end
metadata CHANGED
@@ -1,48 +1,57 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: capistrano-unicorn
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.1.10
5
- prerelease:
4
+ version: 0.2.0
6
5
  platform: ruby
7
6
  authors:
8
7
  - Dan Sosedoff
9
8
  autorequire:
10
9
  bindir: bin
11
10
  cert_chain: []
12
- date: 2013-07-25 00:00:00.000000000 Z
11
+ date: 2013-10-17 00:00:00.000000000 Z
13
12
  dependencies:
14
13
  - !ruby/object:Gem::Dependency
15
14
  name: rake
16
15
  requirement: !ruby/object:Gem::Requirement
17
- none: false
18
16
  requirements:
19
- - - ! '>='
17
+ - - '>='
20
18
  - !ruby/object:Gem::Version
21
19
  version: '0'
22
20
  type: :development
23
21
  prerelease: false
24
22
  version_requirements: !ruby/object:Gem::Requirement
25
- none: false
26
23
  requirements:
27
- - - ! '>='
24
+ - - '>='
28
25
  - !ruby/object:Gem::Version
29
26
  version: '0'
30
27
  - !ruby/object:Gem::Dependency
31
- name: capistrano
28
+ name: unicorn
32
29
  requirement: !ruby/object:Gem::Requirement
33
- none: false
34
30
  requirements:
35
- - - ! '>='
31
+ - - '>='
36
32
  - !ruby/object:Gem::Version
37
33
  version: '0'
38
- type: :runtime
34
+ type: :development
39
35
  prerelease: false
40
36
  version_requirements: !ruby/object:Gem::Requirement
41
- none: false
42
37
  requirements:
43
- - - ! '>='
38
+ - - '>='
44
39
  - !ruby/object:Gem::Version
45
40
  version: '0'
41
+ - !ruby/object:Gem::Dependency
42
+ name: capistrano
43
+ requirement: !ruby/object:Gem::Requirement
44
+ requirements:
45
+ - - <
46
+ - !ruby/object:Gem::Version
47
+ version: '3.0'
48
+ type: :runtime
49
+ prerelease: false
50
+ version_requirements: !ruby/object:Gem::Requirement
51
+ requirements:
52
+ - - <
53
+ - !ruby/object:Gem::Version
54
+ version: '3.0'
46
55
  description: Capistrano plugin that integrates Unicorn server tasks.
47
56
  email: dan.sosedoff@gmail.com
48
57
  executables: []
@@ -50,43 +59,48 @@ extensions: []
50
59
  extra_rdoc_files: []
51
60
  files:
52
61
  - .gitignore
62
+ - .rspec
63
+ - .travis.yml
53
64
  - Gemfile
54
65
  - LICENSE
66
+ - NEWS.md
55
67
  - README.md
56
68
  - Rakefile
57
69
  - capistrano-unicorn.gemspec
58
70
  - examples/rails3.rb
59
71
  - lib/capistrano-unicorn.rb
60
72
  - lib/capistrano-unicorn/capistrano_integration.rb
73
+ - lib/capistrano-unicorn/config.rb
74
+ - lib/capistrano-unicorn/utility.rb
61
75
  - lib/capistrano-unicorn/version.rb
76
+ - spec/capistrano_integration_spec.rb
77
+ - spec/config_spec.rb
78
+ - spec/spec_helper.rb
62
79
  homepage: https://github.com/sosedoff/capistrano-unicorn
63
- licenses: []
80
+ licenses:
81
+ - MIT
82
+ metadata: {}
64
83
  post_install_message:
65
84
  rdoc_options: []
66
85
  require_paths:
67
86
  - lib
68
87
  required_ruby_version: !ruby/object:Gem::Requirement
69
- none: false
70
88
  requirements:
71
- - - ! '>='
89
+ - - '>='
72
90
  - !ruby/object:Gem::Version
73
91
  version: '0'
74
- segments:
75
- - 0
76
- hash: 759058187647909637
77
92
  required_rubygems_version: !ruby/object:Gem::Requirement
78
- none: false
79
93
  requirements:
80
- - - ! '>='
94
+ - - '>='
81
95
  - !ruby/object:Gem::Version
82
96
  version: '0'
83
- segments:
84
- - 0
85
- hash: 759058187647909637
86
97
  requirements: []
87
98
  rubyforge_project:
88
- rubygems_version: 1.8.25
99
+ rubygems_version: 2.0.5
89
100
  signing_key:
90
- specification_version: 3
101
+ specification_version: 4
91
102
  summary: Unicorn integration for Capistrano
92
- test_files: []
103
+ test_files:
104
+ - spec/capistrano_integration_spec.rb
105
+ - spec/config_spec.rb
106
+ - spec/spec_helper.rb