capistrano_multiconfig_parallel 0.2.1 → 0.4.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.
- 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
|