capistrano_multiconfig_parallel 0.2.1 → 0.4.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/README.md +5 -1
- data/lib/capistrano_multiconfig_parallel/base.rb +5 -14
- data/lib/capistrano_multiconfig_parallel/celluloid/celluloid_manager.rb +68 -33
- data/lib/capistrano_multiconfig_parallel/celluloid/celluloid_worker.rb +35 -25
- data/lib/capistrano_multiconfig_parallel/celluloid/child_process.rb +5 -6
- data/lib/capistrano_multiconfig_parallel/celluloid/terminal_table.rb +32 -12
- data/lib/capistrano_multiconfig_parallel/cli.rb +1 -0
- data/lib/capistrano_multiconfig_parallel/configuration.rb +40 -31
- data/lib/capistrano_multiconfig_parallel/helpers/base_manager.rb +25 -12
- data/lib/capistrano_multiconfig_parallel/helpers/multi_app_manager.rb +14 -1
- data/lib/capistrano_multiconfig_parallel/initializers/default.yml +3 -2
- data/lib/capistrano_multiconfig_parallel/initializers/rake.rb +2 -2
- data/lib/capistrano_multiconfig_parallel/version.rb +2 -2
- metadata +1 -1
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 807e40ee77401d31f5ca168c79daa993f0742d2e
|
4
|
+
data.tar.gz: f0fcf399fd6dde6a96fdc40ae10fa6c6e593bd5d
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 5e4d5b0d0e9869d781067480e025b25ac187c67a196247fd8f07c2691adb8f968425c297f4b3d5d788068b01b226a7668a32af096cab4b0cdadedf0dfbf2cb94
|
7
|
+
data.tar.gz: 1ce688cb8219b2bb26ade54ca01af7a198497ed9932ba03242a9c385f951bdc681417b42da32fbb298d613ffd6955020ff4cf29dbf448e1f2cc8906e5df399d0
|
data/README.md
CHANGED
@@ -98,7 +98,8 @@ websocket_server:
|
|
98
98
|
development_stages:
|
99
99
|
- development
|
100
100
|
- webdev
|
101
|
-
|
101
|
+
|
102
|
+
syncronize_confirmation: true
|
102
103
|
task_confirmation_active: false
|
103
104
|
task_confirmations:
|
104
105
|
- deploy:symlink:release
|
@@ -128,6 +129,9 @@ application_dependencies: []
|
|
128
129
|
* --development_stages
|
129
130
|
* if option is present and has value an ARRAY of STRINGS, each of them will be used as a development stage
|
130
131
|
|
132
|
+
* --syncronize_confirmation
|
133
|
+
* if option is present and has value TRUE, all workers will be synchronized to wait for same task from the ***task_confirmations** Array before they execute it
|
134
|
+
|
131
135
|
* --task_confirmation_active
|
132
136
|
* if option is present and has value TRUE, will enable user confirmation dialogs before executing each task from option **--task_confirmations**
|
133
137
|
|
@@ -23,7 +23,7 @@ module CapistranoMulticonfigParallel
|
|
23
23
|
}
|
24
24
|
|
25
25
|
class << self
|
26
|
-
attr_accessor :show_task_progress, :
|
26
|
+
attr_accessor :show_task_progress, :execute_in_sequence, :logger, :original_args
|
27
27
|
|
28
28
|
def root
|
29
29
|
File.expand_path(File.dirname(__dir__))
|
@@ -33,14 +33,6 @@ module CapistranoMulticonfigParallel
|
|
33
33
|
Ask.input message, default: default
|
34
34
|
end
|
35
35
|
|
36
|
-
def verify_app_dependencies(stages)
|
37
|
-
applications = stages.map { |stage| stage.split(':').reverse[1] }
|
38
|
-
wrong = CapistranoMulticonfigParallel.configuration.application_dependencies.find do |hash|
|
39
|
-
!applications.include?(hash[:app]) || (hash[:dependencies].present? && hash[:dependencies].find { |val| !applications.include?(val) })
|
40
|
-
end
|
41
|
-
raise ArgumentError, "invalid configuration for #{wrong.inspect}" if wrong.present?
|
42
|
-
end
|
43
|
-
|
44
36
|
def log_directory
|
45
37
|
File.join(CapistranoMulticonfigParallel.detect_root.to_s, 'log')
|
46
38
|
end
|
@@ -56,14 +48,13 @@ module CapistranoMulticonfigParallel
|
|
56
48
|
def enable_logging
|
57
49
|
CapistranoMulticonfigParallel.configuration_valid?
|
58
50
|
return unless CapistranoMulticonfigParallel::CelluloidManager.debug_enabled
|
59
|
-
FileUtils.mkdir_p(log_directory) unless
|
51
|
+
FileUtils.mkdir_p(log_directory) unless File.directory?(log_directory)
|
60
52
|
FileUtils.touch(main_log_file) unless File.file?(main_log_file)
|
61
|
-
if
|
53
|
+
if ENV[CapistranoMulticonfigParallel::ENV_KEY_JOB_ID].blank?
|
62
54
|
log_file = File.open(main_log_file, 'w')
|
63
55
|
log_file.sync = true
|
64
56
|
end
|
65
57
|
self.logger = ::Logger.new(main_log_file)
|
66
|
-
Celluloid.logger = logger
|
67
58
|
end
|
68
59
|
|
69
60
|
def log_message(message)
|
@@ -90,11 +81,11 @@ module CapistranoMulticonfigParallel
|
|
90
81
|
try_detect_capfile
|
91
82
|
end
|
92
83
|
end
|
93
|
-
|
84
|
+
|
94
85
|
def try_detect_capfile
|
95
86
|
root = Pathname.new(FileUtils.pwd)
|
96
87
|
root = root.parent unless root.directory?
|
97
|
-
root = root.parent until root.children.find{|f| f.file? &&
|
88
|
+
root = root.parent until root.children.find { |f| f.file? && f.basename.to_s.downcase == 'capfile' }.present? || root.root?
|
98
89
|
raise "Can't detect Rails application root" if root.root?
|
99
90
|
root
|
100
91
|
end
|
@@ -24,6 +24,7 @@ module CapistranoMulticonfigParallel
|
|
24
24
|
@mutex = Mutex.new
|
25
25
|
# http://rubydoc.info/gems/celluloid/Celluloid/SupervisionGroup/Member
|
26
26
|
@workers = @worker_supervisor.pool(CapistranoMulticonfigParallel::CelluloidWorker, as: :workers, size: 10)
|
27
|
+
Actor.current.link @workers
|
27
28
|
# Get a handle on the PoolManager
|
28
29
|
# http://rubydoc.info/gems/celluloid/Celluloid/PoolManager
|
29
30
|
# @workers = workers_pool.actor
|
@@ -83,59 +84,76 @@ module CapistranoMulticonfigParallel
|
|
83
84
|
@worker_to_job[worker.mailbox.address] = job
|
84
85
|
debug("worker #{worker.job_id} registed into manager") if self.class.debug_enabled?
|
85
86
|
Actor.current.link worker
|
87
|
+
worker.async.start_task unless syncronized_confirmation?
|
86
88
|
if @job_manager.jobs.size == @job_to_worker.size
|
87
89
|
@registration_complete = true
|
88
90
|
end
|
89
91
|
end
|
90
92
|
end
|
91
93
|
|
92
|
-
def process_jobs
|
93
|
-
|
94
|
-
|
95
|
-
|
94
|
+
def process_jobs
|
95
|
+
if syncronized_confirmation?
|
96
|
+
@job_to_worker.pmap do |_job_id, worker|
|
97
|
+
worker.async.start_task
|
98
|
+
end
|
99
|
+
wait_task_confirmations
|
96
100
|
end
|
97
|
-
|
98
|
-
until
|
101
|
+
condition = @job_to_worker.all? { |_job_id, worker| worker.alive? && worker.worker_state == 'finished' }
|
102
|
+
until condition == true
|
99
103
|
sleep(0.1) # keep current thread alive
|
100
104
|
end
|
101
|
-
|
102
|
-
@job_manager.condition.signal(
|
105
|
+
debug("all jobs have completed #{condition}") if self.class.debug_enabled?
|
106
|
+
@job_manager.condition.signal('completed') if condition
|
103
107
|
end
|
104
|
-
|
105
|
-
def
|
108
|
+
|
109
|
+
def syncronized_confirmation?
|
110
|
+
CapistranoMulticonfigParallel.configuration.syncronize_confirmation.to_s.downcase == 'true' && !@job_manager.executes_deploy_stages?
|
111
|
+
end
|
112
|
+
|
113
|
+
def setup_worker_conditions(worker)
|
106
114
|
hash_conditions = {}
|
107
115
|
if need_confirmations?
|
108
116
|
CapistranoMulticonfigParallel.configuration.task_confirmations.each do |task|
|
109
|
-
hash_conditions[task] = { condition:
|
117
|
+
hash_conditions[task] = { condition: Celluloid::Condition.new, status: 'unconfirmed' }
|
110
118
|
end
|
111
119
|
end
|
112
|
-
@job_to_condition[job_id] = hash_conditions
|
120
|
+
@job_to_condition[worker.job_id] = hash_conditions
|
113
121
|
end
|
114
122
|
|
115
123
|
def need_confirmations?
|
116
124
|
CapistranoMulticonfigParallel.configuration.task_confirmation_active.to_s.downcase == 'true'
|
117
125
|
end
|
118
|
-
|
119
|
-
def mark_completed_remaining_tasks
|
126
|
+
|
127
|
+
def mark_completed_remaining_tasks(worker)
|
120
128
|
return unless need_confirmations?
|
121
|
-
CapistranoMulticonfigParallel.configuration.task_confirmations.each_with_index do |task,
|
122
|
-
|
123
|
-
|
124
|
-
|
125
|
-
|
126
|
-
|
127
|
-
task_confirmation[:condition].signal(fake_result)
|
128
|
-
end
|
129
|
+
CapistranoMulticonfigParallel.configuration.task_confirmations.each_with_index do |task, _index|
|
130
|
+
fake_result = proc { |sum| sum }
|
131
|
+
task_confirmation = @job_to_condition[worker.job_id][task]
|
132
|
+
if task_confirmation[:status] != 'confirmed'
|
133
|
+
task_confirmation[:status] = 'confirmed'
|
134
|
+
task_confirmation[:condition].signal(fake_result)
|
129
135
|
end
|
130
136
|
end
|
131
137
|
end
|
132
|
-
|
138
|
+
|
139
|
+
def wait_task_confirmations_worker(worker)
|
140
|
+
return if !need_confirmations? || syncronized_confirmation?
|
141
|
+
CapistranoMulticonfigParallel.configuration.task_confirmations.each_with_index do |task, _index|
|
142
|
+
result = wait_condition_for_task(worker.job_id, task)
|
143
|
+
confirm_task_approval(result, task, worker) if result.present?
|
144
|
+
end
|
145
|
+
end
|
146
|
+
|
147
|
+
def wait_condition_for_task(job_id, task)
|
148
|
+
@job_to_condition[job_id][task][:condition].wait
|
149
|
+
end
|
150
|
+
|
133
151
|
def wait_task_confirmations
|
134
152
|
return unless need_confirmations?
|
135
|
-
CapistranoMulticonfigParallel.configuration.task_confirmations.each_with_index do |task,
|
153
|
+
CapistranoMulticonfigParallel.configuration.task_confirmations.each_with_index do |task, _index|
|
136
154
|
results = []
|
137
155
|
@jobs.pmap do |job_id, _job|
|
138
|
-
result =
|
156
|
+
result = wait_condition_for_task(job_id, task)
|
139
157
|
results << result
|
140
158
|
end
|
141
159
|
if results.size == @jobs.size
|
@@ -144,11 +162,13 @@ module CapistranoMulticonfigParallel
|
|
144
162
|
end
|
145
163
|
end
|
146
164
|
|
147
|
-
|
148
|
-
|
149
|
-
|
150
|
-
|
151
|
-
|
165
|
+
def confirm_task_approval(result, task, worker = nil)
|
166
|
+
return unless result.present?
|
167
|
+
unless result.is_a?(Proc)
|
168
|
+
message = "Do you want to continue the deployment and execute #{task.upcase}"
|
169
|
+
message += " for JOB #{worker.job_id}" if worker.present?
|
170
|
+
message += '?'
|
171
|
+
set :apps_symlink_confirmation, CapistranoMulticonfigParallel.ask_confirm(message, 'Y/N')
|
152
172
|
until fetch(:apps_symlink_confirmation).present?
|
153
173
|
sleep(0.1) # keep current thread alive
|
154
174
|
end
|
@@ -157,9 +177,9 @@ module CapistranoMulticonfigParallel
|
|
157
177
|
@jobs.pmap do |job_id, job|
|
158
178
|
worker = get_worker_for_job(job_id)
|
159
179
|
worker.publish_rake_event('approved' => 'yes',
|
160
|
-
|
161
|
-
|
162
|
-
|
180
|
+
'action' => 'invoke',
|
181
|
+
'job_id' => job['id'],
|
182
|
+
'task' => task
|
163
183
|
)
|
164
184
|
end
|
165
185
|
end
|
@@ -177,6 +197,21 @@ module CapistranoMulticonfigParallel
|
|
177
197
|
end
|
178
198
|
end
|
179
199
|
|
200
|
+
def process_job(job)
|
201
|
+
env_options = {}
|
202
|
+
job['env_options'].each do |key, value|
|
203
|
+
env_options[key] = value if value.present?
|
204
|
+
end
|
205
|
+
{
|
206
|
+
'job_id' => job['id'],
|
207
|
+
'app_name' => job['app'],
|
208
|
+
'env_name' => job['env'],
|
209
|
+
'action_name' => job['action'],
|
210
|
+
'env_options' => env_options,
|
211
|
+
'task_arguments' => job['task_arguments'],
|
212
|
+
}
|
213
|
+
end
|
214
|
+
|
180
215
|
# lookup status of job by asking actor running it
|
181
216
|
def get_job_status(job)
|
182
217
|
status = nil
|
@@ -44,6 +44,7 @@ module CapistranoMulticonfigParallel
|
|
44
44
|
end
|
45
45
|
|
46
46
|
def start_task
|
47
|
+
@manager.setup_worker_conditions(Actor.current)
|
47
48
|
debug("exec worker #{@job_id} starts task with #{@job.inspect}") if debug_enabled?
|
48
49
|
@client = CelluloidPubsub::Client.connect(actor: Actor.current, enable_debug: @manager.class.debug_websocket?) do |ws|
|
49
50
|
ws.subscribe(@subscription_channel)
|
@@ -77,8 +78,8 @@ module CapistranoMulticonfigParallel
|
|
77
78
|
@task_argv << 'count_rake=true'
|
78
79
|
@child_process = CapistranoMulticonfigParallel::ChildProcess.new
|
79
80
|
Actor.current.link @child_process
|
80
|
-
debug("worker #{@job_id} executes:
|
81
|
-
@child_process.async.work(
|
81
|
+
debug("worker #{@job_id} executes: #{generate_command}") if debug_enabled?
|
82
|
+
@child_process.async.work(generate_command, actor: Actor.current, silent: true, dry_run: true)
|
82
83
|
else
|
83
84
|
async.execute_deploy
|
84
85
|
end
|
@@ -88,9 +89,23 @@ module CapistranoMulticonfigParallel
|
|
88
89
|
@rake_tasks ||= []
|
89
90
|
end
|
90
91
|
|
92
|
+
def generate_command
|
93
|
+
<<-CMD
|
94
|
+
bundle exec ruby -e "require 'bundler' ; Bundler.with_clean_env { %x[cd #{CapistranoMulticonfigParallel.detect_root.to_s} && bundle install && RAILS_ENV=#{@env_name} bundle exec multi_cap #{@task_argv.join(' ')} ] } "
|
95
|
+
CMD
|
96
|
+
end
|
97
|
+
|
91
98
|
def execute_deploy
|
92
99
|
@execute_deploy = true
|
93
100
|
debug("invocation chain #{@job_id} is : #{@rake_tasks.inspect}") if debug_enabled? && CapistranoMulticonfigParallel.show_task_progress
|
101
|
+
check_child_proces
|
102
|
+
setup_task_arguments
|
103
|
+
debug("worker #{@job_id} executes: #{generate_command}") if debug_enabled?
|
104
|
+
@child_process.async.work(generate_command, actor: Actor.current, silent: true)
|
105
|
+
@manager.wait_task_confirmations_worker(Actor.current) unless @manager.syncronized_confirmation?
|
106
|
+
end
|
107
|
+
|
108
|
+
def check_child_proces
|
94
109
|
if !defined?(@child_process) || @child_process.nil?
|
95
110
|
@child_process = CapistranoMulticonfigParallel::ChildProcess.new
|
96
111
|
Actor.current.link @child_process
|
@@ -98,9 +113,6 @@ module CapistranoMulticonfigParallel
|
|
98
113
|
@client.unsubscribe("rake_worker_#{@job_id}_count")
|
99
114
|
@child_process.exit_status = nil
|
100
115
|
end
|
101
|
-
setup_task_arguments
|
102
|
-
debug("worker #{@job_id} executes: bundle exec multi_cap #{@task_argv.join(' ')}") if debug_enabled?
|
103
|
-
@child_process.async.work("bundle exec multi_cap #{@task_argv.join(' ')}", actor: Actor.current, silent: true)
|
104
116
|
end
|
105
117
|
|
106
118
|
def on_close(code, reason)
|
@@ -171,19 +183,25 @@ module CapistranoMulticonfigParallel
|
|
171
183
|
end
|
172
184
|
|
173
185
|
def process_job(job)
|
174
|
-
|
175
|
-
@
|
176
|
-
@
|
177
|
-
@
|
178
|
-
@
|
179
|
-
|
180
|
-
|
181
|
-
|
182
|
-
|
186
|
+
processed_job = @manager.process_job(job)
|
187
|
+
@job_id = processed_job['job_id']
|
188
|
+
@app_name = processed_job['app_name']
|
189
|
+
@env_name = processed_job['env_name']
|
190
|
+
@action_name = processed_job['action_name']
|
191
|
+
@env_options = processed_job['env_options']
|
192
|
+
@task_arguments = processed_job['task_arguments']
|
193
|
+
end
|
194
|
+
|
195
|
+
def crashed?
|
196
|
+
@action_name == 'deploy:rollback' || @action_name == 'deploy:failed'
|
183
197
|
end
|
184
198
|
|
185
|
-
def
|
186
|
-
@
|
199
|
+
def finish_worker
|
200
|
+
@manager.mark_completed_remaining_tasks(Actor.current)
|
201
|
+
@worker_state = 'finished'
|
202
|
+
@manager.job_to_worker.each do|_job_id, worker|
|
203
|
+
debug("worker #{worker.job_id}has state #{worker.worker_state}") if worker.alive? && ebug_enabled?
|
204
|
+
end
|
187
205
|
end
|
188
206
|
|
189
207
|
def notify_finished(exit_status)
|
@@ -194,16 +212,8 @@ module CapistranoMulticonfigParallel
|
|
194
212
|
else
|
195
213
|
update_machine_state('FINISHED')
|
196
214
|
debug("worker #{job_id} notifies manager has finished") if debug_enabled?
|
197
|
-
|
198
|
-
if debug_enabled?
|
199
|
-
debug("worker #{job_id}has state #{@worker_state}")
|
200
|
-
@manager.job_to_worker.each{|job_id, worker|
|
201
|
-
debug("worker #{worker.job_id}has state #{worker.worker_state}") if worker.alive?
|
202
|
-
}
|
203
|
-
end
|
215
|
+
finish_worker
|
204
216
|
end
|
205
217
|
end
|
206
|
-
|
207
|
-
|
208
218
|
end
|
209
219
|
end
|
@@ -4,13 +4,12 @@ module CapistranoMulticonfigParallel
|
|
4
4
|
include Celluloid
|
5
5
|
include Celluloid::Logger
|
6
6
|
|
7
|
-
attr_accessor :actor, :pid, :exit_status, :process, :filename
|
8
|
-
|
7
|
+
attr_accessor :actor, :pid, :exit_status, :process, :filename, :worker_log
|
9
8
|
|
10
9
|
def work(cmd, options = {})
|
11
10
|
@options = options
|
12
11
|
@actor = @options.fetch(:actor, nil)
|
13
|
-
|
12
|
+
set_worker_log
|
14
13
|
EM.run do
|
15
14
|
EM.next_tick do
|
16
15
|
start_async_deploy(cmd, options)
|
@@ -33,8 +32,8 @@ module CapistranoMulticonfigParallel
|
|
33
32
|
@worker_log = ::Logger.new(@filename)
|
34
33
|
@worker_log.level = ::Logger::Severity::DEBUG
|
35
34
|
@worker_log.formatter = proc do |severity, datetime, progname, msg|
|
36
|
-
date_format = datetime.strftime(
|
37
|
-
|
35
|
+
date_format = datetime.strftime('%Y-%m-%d %H:%M:%S')
|
36
|
+
"[#{date_format}] #{severity} (#{progname}): #{msg}\n"
|
38
37
|
end
|
39
38
|
end
|
40
39
|
|
@@ -96,7 +95,7 @@ module CapistranoMulticonfigParallel
|
|
96
95
|
end
|
97
96
|
|
98
97
|
def io_callback(io, data)
|
99
|
-
|
98
|
+
@worker_log.debug("#{io.upcase} ---- #{data}")
|
100
99
|
end
|
101
100
|
end
|
102
101
|
end
|
@@ -50,22 +50,42 @@ module CapistranoMulticonfigParallel
|
|
50
50
|
puts "\n"
|
51
51
|
sleep(1)
|
52
52
|
end
|
53
|
-
|
54
|
-
def
|
53
|
+
|
54
|
+
def get_worker_details(job_id)
|
55
|
+
job = @manager.jobs[job_id]
|
56
|
+
processed_job = @manager.process_job(job)
|
55
57
|
worker = @manager.get_worker_for_job(job_id)
|
56
|
-
|
58
|
+
if worker.alive?
|
59
|
+
state = worker.machine.state.to_s
|
60
|
+
state = worker_crashed?(worker) ? state.red : state.green
|
61
|
+
else
|
62
|
+
state = "dead".upcase.red
|
63
|
+
end
|
57
64
|
worker_optons = ''
|
58
|
-
|
65
|
+
processed_job['env_options'].each do |key, value|
|
59
66
|
worker_optons << "#{key}=#{value}\n"
|
60
67
|
end
|
61
|
-
|
62
|
-
|
63
|
-
|
64
|
-
|
65
|
-
|
66
|
-
|
67
|
-
|
68
|
-
|
68
|
+
action = processed_job['task_arguments'].present? ? "#{processed_job['action_name']}[#{processed_job['task_arguments'].join(',')}]" : processed_job['action_name']
|
69
|
+
|
70
|
+
{
|
71
|
+
'job_id' => job_id,
|
72
|
+
'app_name' =>processed_job['app_name'],
|
73
|
+
'env_name' =>processed_job['env_name'],
|
74
|
+
'action_name' => action,
|
75
|
+
'env_options' => worker_optons,
|
76
|
+
'task_arguments' => job['task_arguments'],
|
77
|
+
'state' => state
|
78
|
+
}
|
79
|
+
end
|
80
|
+
|
81
|
+
def add_job_to_table(table, job_id)
|
82
|
+
details = get_worker_details(job_id)
|
83
|
+
row = [{ value: job_id.to_s },
|
84
|
+
{ value: "#{details['app_name']}\n#{details['env_name']}" },
|
85
|
+
{ value: details['action_name'] },
|
86
|
+
{ value: details['env_options'] },
|
87
|
+
{ value: "#{details['state']}" }
|
88
|
+
]
|
69
89
|
if CapistranoMulticonfigParallel.show_task_progress
|
70
90
|
row << { value: worker.rake_tasks.size }
|
71
91
|
row << { value: worker_progress(worker) }
|
@@ -13,18 +13,17 @@ module CapistranoMulticonfigParallel
|
|
13
13
|
command_line_params.each do |param|
|
14
14
|
@config.define param[:name], type: param[:type], description: param[:description], default: param[:default]
|
15
15
|
end
|
16
|
-
|
16
|
+
|
17
|
+
ARGV.clear
|
18
|
+
CapistranoMulticonfigParallel.original_args.each { |a| ARGV << a }
|
17
19
|
@config.read config_file if File.file?(config_file)
|
18
20
|
@config.merge(Settings.use(:commandline).resolve!)
|
19
|
-
|
21
|
+
|
20
22
|
@config.use :config_block
|
21
23
|
@config.finally do |c|
|
22
24
|
check_configuration(c)
|
23
25
|
end
|
24
26
|
@config.resolve!
|
25
|
-
rescue => ex
|
26
|
-
puts ex.inspect
|
27
|
-
puts ex.backtrace if ex.respond_to?(:backtrace)
|
28
27
|
end
|
29
28
|
|
30
29
|
def default_config
|
@@ -42,65 +41,78 @@ module CapistranoMulticonfigParallel
|
|
42
41
|
{
|
43
42
|
name: 'multi_debug',
|
44
43
|
type: :boolean,
|
45
|
-
description: '
|
44
|
+
description: 'if option is present and has value TRUE , will enable debugging of workers',
|
46
45
|
default: default_config[:multi_debug]
|
47
46
|
},
|
48
47
|
{
|
49
48
|
name: 'multi_progress',
|
50
49
|
type: :boolean,
|
51
|
-
description: "
|
52
|
-
|
53
|
-
|
50
|
+
description: "if option is present and has value TRUE will first execute before any process
|
51
|
+
\t same task but with option '--dry-run' in order to show progress of how many tasks
|
52
|
+
\t are in total for that task and what is the progress of executing
|
53
|
+
\t This will slow down the workers , because they will execute twice the same task.",
|
54
54
|
default: default_config[:multi_progress]
|
55
55
|
},
|
56
56
|
{
|
57
57
|
name: 'multi_secvential',
|
58
58
|
type: :boolean,
|
59
|
-
description: "
|
60
|
-
|
59
|
+
description: "If parallel executing does not work for you, you can use this option so that
|
60
|
+
\t each process is executed normally and ouputted to the screen.
|
61
|
+
\t However this means that all other tasks will have to wait for each other to finish before starting ",
|
61
62
|
default: default_config[:multi_secvential]
|
62
63
|
},
|
63
64
|
{
|
64
65
|
name: 'websocket_server.enable_debug',
|
65
66
|
type: :boolean,
|
66
|
-
description:
|
67
|
+
description: "if option is present and has value TRUE
|
68
|
+
\t will enable debugging of websocket communication between the workers",
|
67
69
|
default: default_config[:websocket_server][:enable_debug]
|
68
70
|
},
|
69
71
|
{
|
70
72
|
name: 'development_stages',
|
71
73
|
type: Array,
|
72
|
-
description:
|
74
|
+
description: "if option is present and has value an ARRAY of STRINGS,
|
75
|
+
\t each of them will be used as a development stage",
|
73
76
|
default: default_config[:development_stages]
|
74
77
|
},
|
75
78
|
{
|
76
79
|
name: 'task_confirmations',
|
77
80
|
type: Array,
|
78
|
-
description:
|
79
|
-
|
81
|
+
description: "if option is present and has value TRUE, will enable user confirmation dialogs
|
82
|
+
\t before executing each task from option **--task_confirmations**",
|
80
83
|
default: default_config[:task_confirmations]
|
81
84
|
},
|
82
85
|
{
|
83
86
|
name: 'task_confirmation_active',
|
84
87
|
type: :boolean,
|
85
|
-
description: "
|
86
|
-
|
87
|
-
|
88
|
-
|
88
|
+
description: "if option is present and has value an ARRAY of Strings, and --task_confirmation_active is TRUE ,
|
89
|
+
\t then will require a confirmation from user before executing the task.
|
90
|
+
\t This will syncronize all workers to wait before executing that task, then a confirmation will be displayed,
|
91
|
+
\t and when user will confirm , all workers will resume their operation",
|
89
92
|
default: default_config[:task_confirmation_active]
|
90
93
|
},
|
94
|
+
{
|
95
|
+
name: 'syncronize_confirmation',
|
96
|
+
type: :boolean,
|
97
|
+
description: "if option is present and has value TRUE, all workers will be synchronized to wait for same task
|
98
|
+
\t from the ***task_confirmations** Array before they execute it ",
|
99
|
+
default: default_config[:syncronize_confirmation]
|
100
|
+
},
|
91
101
|
{
|
92
102
|
name: 'track_dependencies',
|
93
103
|
type: :boolean,
|
94
|
-
description: "
|
95
|
-
|
96
|
-
|
104
|
+
description: "This should be useed only for Caphub-like applications ,
|
105
|
+
\t in order to deploy dependencies of an application in parallel.
|
106
|
+
\t This is used only in combination with option **--application_dependencies** which is described
|
107
|
+
\t at section **[2.) Multiple applications](#multiple_apps)**",
|
97
108
|
default: default_config[:track_dependencies]
|
98
109
|
},
|
99
110
|
{
|
100
111
|
name: 'application_dependencies',
|
101
112
|
type: Array,
|
102
|
-
description: "
|
103
|
-
|
113
|
+
description: "This is an array of hashes. Each hash has only the keys
|
114
|
+
\t 'app' ( app name), 'priority' and 'dependencies'
|
115
|
+
\t ( an array of app names that this app is dependent to) ",
|
104
116
|
default: default_config[:application_dependencies]
|
105
117
|
}
|
106
118
|
]
|
@@ -111,7 +123,7 @@ module CapistranoMulticonfigParallel
|
|
111
123
|
[
|
112
124
|
"--#{param[:name]}[=CAP_VALUE]",
|
113
125
|
"--#{param[:name]}",
|
114
|
-
param[:description],
|
126
|
+
"[MULTI_CAP] #{param[:description]}",
|
115
127
|
lambda do |_value|
|
116
128
|
end
|
117
129
|
]
|
@@ -138,7 +150,7 @@ module CapistranoMulticonfigParallel
|
|
138
150
|
end
|
139
151
|
|
140
152
|
def check_boolean(c, prop)
|
141
|
-
return unless c[prop].present?
|
153
|
+
# return unless c[prop].present?
|
142
154
|
raise ArgumentError, "the property `#{prop}` must be boolean" unless [true, false, 'true', 'false'].include?(c[prop].to_s.downcase)
|
143
155
|
end
|
144
156
|
|
@@ -158,13 +170,10 @@ module CapistranoMulticonfigParallel
|
|
158
170
|
end
|
159
171
|
|
160
172
|
def check_additional_config(c)
|
161
|
-
if c[:multi_debug].to_s.downcase == 'true'
|
162
|
-
CapistranoMulticonfigParallel::CelluloidManager.debug_enabled = true
|
163
|
-
Celluloid.task_class = Celluloid::TaskThread
|
164
|
-
end
|
173
|
+
CapistranoMulticonfigParallel::CelluloidManager.debug_enabled = true if c[:multi_debug].to_s.downcase == 'true'
|
165
174
|
CapistranoMulticonfigParallel.show_task_progress = true if c[:multi_progress].to_s.downcase == 'true'
|
166
175
|
CapistranoMulticonfigParallel.execute_in_sequence = true if c[:multi_secvential].to_s.downcase == 'true'
|
167
176
|
end
|
168
177
|
end
|
169
|
-
|
178
|
+
end
|
170
179
|
end
|
@@ -14,8 +14,6 @@ module CapistranoMulticonfigParallel
|
|
14
14
|
@stages = stages
|
15
15
|
@jobs = []
|
16
16
|
CapistranoMulticonfigParallel.enable_logging
|
17
|
-
CapistranoMulticonfigParallel.configuration_valid?
|
18
|
-
CapistranoMulticonfigParallel.verify_app_dependencies(@stages) if CapistranoMulticonfigParallel.configuration.track_dependencies
|
19
17
|
end
|
20
18
|
|
21
19
|
def can_start?
|
@@ -31,11 +29,21 @@ module CapistranoMulticonfigParallel
|
|
31
29
|
CapistranoMulticonfigParallel::CUSTOM_COMMANDS[key]
|
32
30
|
end
|
33
31
|
|
32
|
+
def executes_deploy_stages?
|
33
|
+
@name == custom_commands[:stages]
|
34
|
+
end
|
35
|
+
|
34
36
|
def multi_apps?
|
35
37
|
@cap_app.multi_apps?
|
36
38
|
end
|
37
39
|
|
40
|
+
def configuration
|
41
|
+
CapistranoMulticonfigParallel.configuration
|
42
|
+
end
|
43
|
+
|
38
44
|
def start(&block)
|
45
|
+
CapistranoMulticonfigParallel.configuration_valid?
|
46
|
+
check_before_starting
|
39
47
|
@application = custom_command? ? nil : @top_level_tasks.first.split(':').reverse[1]
|
40
48
|
@stage = custom_command? ? nil : @top_level_tasks.first.split(':').reverse[0]
|
41
49
|
@name, @args = @cap_app.parse_task_string(@top_level_tasks.second)
|
@@ -45,6 +53,15 @@ module CapistranoMulticonfigParallel
|
|
45
53
|
run
|
46
54
|
end
|
47
55
|
|
56
|
+
def check_before_starting
|
57
|
+
@condition = Celluloid::Condition.new
|
58
|
+
@manager = CapistranoMulticonfigParallel::CelluloidManager.new(Actor.current)
|
59
|
+
if CapistranoMulticonfigParallel::CelluloidManager.debug_enabled == true
|
60
|
+
Celluloid.logger = CapistranoMulticonfigParallel.logger
|
61
|
+
Celluloid.task_class = Celluloid::TaskThread
|
62
|
+
end
|
63
|
+
end
|
64
|
+
|
48
65
|
def collect_jobs(options = {}, &block)
|
49
66
|
options = prepare_options(options)
|
50
67
|
block.call(options) if block_given?
|
@@ -52,12 +69,12 @@ module CapistranoMulticonfigParallel
|
|
52
69
|
raise [e, e.backtrace].inspect
|
53
70
|
end
|
54
71
|
|
55
|
-
def process_jobs
|
72
|
+
def process_jobs
|
56
73
|
return unless @jobs.present?
|
57
74
|
if CapistranoMulticonfigParallel.execute_in_sequence
|
58
75
|
@jobs.each { |job| CapistranoMulticonfigParallel::StandardDeploy.execute_standard_deploy(job) }
|
59
76
|
else
|
60
|
-
run_async_jobs
|
77
|
+
run_async_jobs
|
61
78
|
end
|
62
79
|
end
|
63
80
|
|
@@ -91,10 +108,8 @@ module CapistranoMulticonfigParallel
|
|
91
108
|
end
|
92
109
|
end
|
93
110
|
|
94
|
-
def run_async_jobs
|
111
|
+
def run_async_jobs
|
95
112
|
return unless @jobs.present?
|
96
|
-
@condition = Celluloid::Condition.new
|
97
|
-
@manager = CapistranoMulticonfigParallel::CelluloidManager.new(Actor.current)
|
98
113
|
@jobs.pmap do |job|
|
99
114
|
@manager.async.delegate(job)
|
100
115
|
end
|
@@ -102,7 +117,7 @@ module CapistranoMulticonfigParallel
|
|
102
117
|
sleep(0.1) # keep current thread alive
|
103
118
|
end
|
104
119
|
return unless @manager.registration_complete
|
105
|
-
@manager.process_jobs
|
120
|
+
@manager.async.process_jobs
|
106
121
|
wait_jobs_termination
|
107
122
|
end
|
108
123
|
|
@@ -124,8 +139,8 @@ module CapistranoMulticonfigParallel
|
|
124
139
|
env_opts = get_app_additional_env_options(app, message)
|
125
140
|
|
126
141
|
options['env_options'] = options['env_options'].reverse_merge(env_opts.except('BOX'))
|
127
|
-
|
128
|
-
env_options = branch_name.present? ?
|
142
|
+
|
143
|
+
env_options = branch_name.present? ? { 'BRANCH' => branch_name }.merge(options['env_options']) : options['env_options']
|
129
144
|
|
130
145
|
job = {
|
131
146
|
app: app,
|
@@ -164,8 +179,6 @@ module CapistranoMulticonfigParallel
|
|
164
179
|
end
|
165
180
|
end
|
166
181
|
|
167
|
-
|
168
|
-
|
169
182
|
def get_app_additional_env_options(app, app_message)
|
170
183
|
app_name = (app.is_a?(Hash) && app[:app].present?) ? app[:app].camelcase : app
|
171
184
|
app_name = app_name.present? ? app_name : 'current application'
|
@@ -11,6 +11,19 @@ module CapistranoMulticonfigParallel
|
|
11
11
|
@dependency_tracker = CapistranoMulticonfigParallel::DependencyTracker.new(Actor.current)
|
12
12
|
end
|
13
13
|
|
14
|
+
def check_before_starting
|
15
|
+
verify_app_dependencies(@stages) if configuration.present? && configuration.track_dependencies.to_s.downcase == 'true'
|
16
|
+
super
|
17
|
+
end
|
18
|
+
|
19
|
+
def verify_app_dependencies(stages)
|
20
|
+
applications = stages.map { |stage| stage.split(':').reverse[1] }
|
21
|
+
wrong = configuration.application_dependencies.find do |hash|
|
22
|
+
!applications.include?(hash[:app]) || (hash[:dependencies].present? && hash[:dependencies].find { |val| !applications.include?(val) })
|
23
|
+
end
|
24
|
+
raise ArgumentError, "invalid configuration for #{wrong.inspect}" if wrong.present?
|
25
|
+
end
|
26
|
+
|
14
27
|
def run
|
15
28
|
options = {}
|
16
29
|
if custom_command?
|
@@ -54,7 +67,7 @@ module CapistranoMulticonfigParallel
|
|
54
67
|
end
|
55
68
|
end
|
56
69
|
|
57
|
-
|
70
|
+
private
|
58
71
|
|
59
72
|
def multi_collect_and_run_jobs(options = {}, &block)
|
60
73
|
collect_jobs(options) do |new_options|
|
@@ -1,5 +1,5 @@
|
|
1
1
|
multi_debug: true
|
2
|
-
|
2
|
+
multi_progress: false
|
3
3
|
multi_secvential: false
|
4
4
|
websocket_server:
|
5
5
|
enable_debug: false
|
@@ -7,7 +7,8 @@ websocket_server:
|
|
7
7
|
development_stages:
|
8
8
|
- development
|
9
9
|
- webdev
|
10
|
-
|
10
|
+
|
11
|
+
syncronize_confirmation: true
|
11
12
|
task_confirmation_active: false
|
12
13
|
task_confirmations:
|
13
14
|
- deploy:symlink:release
|
@@ -12,7 +12,7 @@ Rake::Task.class_eval do
|
|
12
12
|
end
|
13
13
|
end
|
14
14
|
|
15
|
-
def run_the_actor(job_id
|
15
|
+
def run_the_actor(job_id)
|
16
16
|
rake_actor_id = ENV['count_rake'].present? ? "rake_worker_#{job_id}_count" : "rake_worker_#{job_id}"
|
17
17
|
if Celluloid::Actor[rake_actor_id].blank?
|
18
18
|
CapistranoMulticonfigParallel::RakeWorker.supervise_as rake_actor_id
|
@@ -23,6 +23,6 @@ Rake::Task.class_eval do
|
|
23
23
|
until Celluloid::Actor[rake_actor_id].task_approved
|
24
24
|
sleep(0.1) # keep current thread alive
|
25
25
|
end
|
26
|
-
|
26
|
+
yield if Celluloid::Actor[rake_actor_id].task_approved
|
27
27
|
end
|
28
28
|
end
|