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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: 5b9578694025dca215377cd4d9d78dea9a0bad48
4
- data.tar.gz: 0de2d1f4b3c3ad520620769ac2e2baf3c2b88e67
3
+ metadata.gz: 807e40ee77401d31f5ca168c79daa993f0742d2e
4
+ data.tar.gz: f0fcf399fd6dde6a96fdc40ae10fa6c6e593bd5d
5
5
  SHA512:
6
- metadata.gz: 2c123c22fc754c732ec4d64be8477f264e2deb67080b638848a23253453aa3e0270281c6867516d618ed427bf86c245e3811b7dc300c692afe1a94f9493dac50
7
- data.tar.gz: 5e8cd281aed6385d4bb2e072767572206a6c1b824bb8a0b4ce0cef170e50e7f6ab9aad40036d17f31fc0a609b3defedd34abb80310d896081a9e6ed322bcfcf3
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, :interactive_menu, :execute_in_sequence, :logger, :show_task_progress_tree
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 File.directory?(log_directory)
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 ENV[CapistranoMulticonfigParallel::ENV_KEY_JOB_ID].blank?
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? && f.basename.to_s.downcase == "capfile"}.present? || root.root?
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(&block)
93
- @job_to_worker.pmap do |job_id, worker|
94
- setup_worker_conditions(job_id)
95
- worker.async.start_task
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
- block_given? ? block.call : wait_task_confirmations
98
- until @job_to_worker.all?{|job_id, worker| worker.alive? && worker.worker_state =='finished'}
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
- mark_completed_remaining_tasks
102
- @job_manager.condition.signal("completed")
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 setup_worker_conditions(job_id)
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: Celluloid::Condition.new, status: 'unconfirmed' }
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, index|
122
- @jobs.pmap do |job_id, _job|
123
- fake_result = proc{ |sum| sum }
124
- task_confirmation = @job_to_condition[job_id][task]
125
- if task_confirmation[:status] != 'confirmed'
126
- task_confirmation[:status] = 'confirmed'
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, index|
153
+ CapistranoMulticonfigParallel.configuration.task_confirmations.each_with_index do |task, _index|
136
154
  results = []
137
155
  @jobs.pmap do |job_id, _job|
138
- result = @job_to_condition[job_id][task][:condition].wait
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
- def confirm_task_approval(results, task)
149
- return unless results.present?
150
- if results.detect {|x| !x.is_a?(Proc)}
151
- set :apps_symlink_confirmation, CapistranoMulticonfigParallel.ask_confirm("Do you want to continue the deployment and execute #{task}?", 'Y/N')
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
- 'action' => 'invoke',
161
- 'job_id' => job['id'],
162
- 'task' => task
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: bundle exec multi_cap #{@task_argv.join(' ')}") if debug_enabled?
81
- @child_process.async.work("bundle exec multi_cap #{@task_argv.join(' ')}", actor: Actor.current, dry_run: true)
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
- @job_id = job['id']
175
- @app_name = job['app']
176
- @env_name = job['env']
177
- @action_name = job['action']
178
- @env_options = {}
179
- job['env_options'].each do |key, value|
180
- @env_options[key] = value if value.present?
181
- end
182
- @task_arguments = job['task_arguments']
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 crashed?
186
- @action_name == 'deploy:rollback'
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
- @worker_state = "finished"
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,:worker_log
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
- set_worker_log
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("%Y-%m-%d %H:%M:%S")
37
- "[#{date_format}] #{severity} (#{progname}): #{msg}\n"
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
- @worker_log.debug("#{io.upcase} ---- #{data}")
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 add_job_to_table(table, job_id)
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
- return unless worker.present?
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
- worker.env_options.each do |key, value|
65
+ processed_job['env_options'].each do |key, value|
59
66
  worker_optons << "#{key}=#{value}\n"
60
67
  end
61
- state = worker.machine.state.to_s
62
- state = worker_crashed?(worker) ? state.red : state.green
63
- row = [{ value: worker.job_id.to_s },
64
- { value: "#{worker.app_name}\n#{worker.env_name}" },
65
- { value: worker.action_name },
66
- { value: worker_optons },
67
- { value: state }
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) }
@@ -10,6 +10,7 @@ module CapistranoMulticonfigParallel
10
10
  if $stdout.isatty
11
11
  $stdout.sync = true
12
12
  end
13
+ CapistranoMulticonfigParallel.original_args = ARGV.dup
13
14
  CapistranoMulticonfigParallel::Application.new.run
14
15
  rescue Interrupt
15
16
  `stty icanon echo`
@@ -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: '[MULTI_CAP] if option is present and has value TRUE , will enable debugging of workers',
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: "[MULTI_CAP] if option is present and has value TRUE will first execute before any process ,
52
- same task but with option '--dry-run' in order to show progress of how many tasks are in total for that task and what is the progress of executing
53
- This will slow down the workers , because they will execute twice the same task.",
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: "[MULTI_CAP] If parallel executing does not work for you, you can use this option so that each process is executed normally and ouputted to the screen.
60
- However this means that all other tasks will have to wait for each other to finish before starting ",
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: '[MULTI_CAP] if option is present and has value TRUE, will enable debugging of websocket communication between the workers',
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: '[MULTI_CAP] if option is present and has value an ARRAY of STRINGS, each of them will be used as a development stage',
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: '[MULTI_CAP] if option is present and has value TRUE, will enable user confirmation dialogs
79
- before executing each task from option **--task_confirmations**',
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: "[MULTI_CAP] if option is present and has value an ARRAY of Strings, and --task_confirmation_active is TRUE ,
86
- then will require a confirmation from user before executing the task.
87
- This will syncronize all workers to wait before executing that task, then a confirmation will be displayed,
88
- and when user will confirm , all workers will resume their operation",
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: "[MULTI_CAP] This should be useed only for Caphub-like applications , in order to deploy dependencies of an application in parallel.
95
- This is used only in combination with option **--application_dependencies** which is described
96
- at section **[2.) Multiple applications](#multiple_apps)**",
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: "[MULTI_CAP] This is an array of hashes. Each hash has only the keys 'app' ( app name), 'priority' and 'dependencies'
103
- ( an array of app names that this app is dependent to) ",
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
- end
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(&block)
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(&block)
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(&block)
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(&block)
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? ? { 'BRANCH' => branch_name }.merge(options['env_options']) : options['env_options']
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
- private
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
- multi_progres: false
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, &block)
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
- block.call if Celluloid::Actor[rake_actor_id].task_approved
26
+ yield if Celluloid::Actor[rake_actor_id].task_approved
27
27
  end
28
28
  end
@@ -7,8 +7,8 @@ module CapistranoMulticonfigParallel
7
7
  # module used for generating the version
8
8
  module VERSION
9
9
  MAJOR = 0
10
- MINOR = 2
11
- TINY = 1
10
+ MINOR = 4
11
+ TINY = 0
12
12
  PRE = nil
13
13
 
14
14
  STRING = [MAJOR, MINOR, TINY, PRE].compact.join('.')
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: capistrano_multiconfig_parallel
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.2.1
4
+ version: 0.4.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - bogdanRada