capistrano-unicorn 0.1.10 → 0.2.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.
@@ -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