capistrano_multiconfig_parallel 1.7.2 → 2.0.0.beta1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (33) hide show
  1. checksums.yaml +4 -4
  2. data/Gemfile +0 -2
  3. data/README.md +38 -36
  4. data/V1_README.md +351 -0
  5. data/capistrano_multiconfig_parallel.gemspec +5 -8
  6. data/lib/capistrano_multiconfig_parallel/all.rb +3 -7
  7. data/lib/capistrano_multiconfig_parallel/application.rb +10 -5
  8. data/lib/capistrano_multiconfig_parallel/base.rb +11 -11
  9. data/lib/capistrano_multiconfig_parallel/celluloid/celluloid_manager.rb +3 -2
  10. data/lib/capistrano_multiconfig_parallel/celluloid/celluloid_worker.rb +32 -20
  11. data/lib/capistrano_multiconfig_parallel/celluloid/child_process.rb +3 -0
  12. data/lib/capistrano_multiconfig_parallel/celluloid/state_machine.rb +21 -14
  13. data/lib/capistrano_multiconfig_parallel/celluloid/terminal_table.rb +4 -3
  14. data/lib/capistrano_multiconfig_parallel/classes/cursor.rb +6 -2
  15. data/lib/capistrano_multiconfig_parallel/classes/dependency_tracker.rb +1 -1
  16. data/lib/capistrano_multiconfig_parallel/classes/job.rb +38 -13
  17. data/lib/capistrano_multiconfig_parallel/classes/job_command.rb +154 -15
  18. data/lib/capistrano_multiconfig_parallel/cli.rb +10 -26
  19. data/lib/capistrano_multiconfig_parallel/configuration/default.yml +1 -1
  20. data/lib/capistrano_multiconfig_parallel/helpers/application_helper.rb +26 -2
  21. data/lib/capistrano_multiconfig_parallel/helpers/capistrano_helper.rb +1 -1
  22. data/lib/capistrano_multiconfig_parallel/helpers/configuration.rb +3 -3
  23. data/lib/capistrano_multiconfig_parallel/helpers/gem_helper.rb +2 -1
  24. data/lib/capistrano_multiconfig_parallel/helpers/internal_helper.rb +18 -2
  25. data/lib/capistrano_multiconfig_parallel/version.rb +5 -4
  26. metadata +20 -60
  27. data/lib/capistrano_multiconfig_parallel/celluloid/rake_worker.rb +0 -132
  28. data/lib/capistrano_multiconfig_parallel/classes/input_stream.rb +0 -34
  29. data/lib/capistrano_multiconfig_parallel/classes/output_stream.rb +0 -33
  30. data/lib/capistrano_multiconfig_parallel/classes/rake_task_hooks.rb +0 -88
  31. data/lib/capistrano_multiconfig_parallel/initializers/capistrano2.rb +0 -37
  32. data/lib/capistrano_multiconfig_parallel/initializers/rake.rb +0 -11
  33. data/lib/capistrano_multiconfig_parallel/initializers/websocket.rb +0 -19
@@ -1,6 +1,5 @@
1
1
  # base module that has the statis methods that this gem is using
2
2
  module CapistranoMulticonfigParallel
3
- ENV_KEY_JOB_ID = 'multi_cap_job_id'
4
3
  GITFLOW_TAG_STAGING_TASK = 'gitflow:tag_staging'
5
4
  GITFLOW_CALCULATE_TAG_TASK = 'gitflow:calculate_tag'
6
5
  GITFLOW_VERIFY_UPTODATE_TASK = 'gitflow:verify_up_to_date'
@@ -15,6 +14,10 @@ module CapistranoMulticonfigParallel
15
14
  @config
16
15
  end
17
16
 
17
+ def env_job_key_id
18
+ CapistranoSentinel::RequestHooks::ENV_KEY_JOB_ID
19
+ end
20
+
18
21
  def configuration_flags
19
22
  default_internal_config.each_with_object({}) do |array_item, hash|
20
23
  key = array_item[0].to_s
@@ -29,21 +32,18 @@ module CapistranoMulticonfigParallel
29
32
  set_celluloid_exception_handling
30
33
  end
31
34
 
32
- def job_id
33
- original_args_hash.fetch(CapistranoMulticonfigParallel::ENV_KEY_JOB_ID, nil)
34
- end
35
-
36
35
  def original_args_hash
37
36
  multi_fetch_argv((original_args || ARGV).dup)
38
37
  end
39
38
 
40
- def capistrano_version
41
- find_loaded_gem_property('capistrano', 'version')
42
- end
39
+ # def capistrano_version
40
+ # find_loaded_gem_property('capistrano', 'version')
41
+ # end
42
+ #
43
+ # def capistrano_version_2?
44
+ # capistrano_version.blank? ? false : verify_gem_version(capistrano_version, '3.0', operator: '<')
45
+ # end
43
46
 
44
- def capistrano_version_2?
45
- capistrano_version.blank? ? false : verify_gem_version(capistrano_version, '3.0', operator: '<')
46
- end
47
47
 
48
48
  private
49
49
 
@@ -6,7 +6,7 @@ module CapistranoMulticonfigParallel
6
6
  # manager class that handles workers
7
7
  class CelluloidManager
8
8
  include CapistranoMulticonfigParallel::BaseActorHelper
9
- attr_accessor :jobs, :job_to_worker, :worker_to_job, :job_to_condition, :mutex, :registration_complete, :workers_terminated, :stderr_buffer
9
+ attr_accessor :jobs, :job_to_worker, :worker_to_job, :job_to_condition, :mutex, :registration_complete, :workers_terminated, :stderr_buffer
10
10
 
11
11
  attr_reader :worker_supervisor, :workers
12
12
  trap_exit :worker_died
@@ -26,10 +26,10 @@ module CapistranoMulticonfigParallel
26
26
  Actor.current.link @workers
27
27
  setup_actor_supervision(@worker_supervisor, actor_name: :terminal_server, type: CapistranoMulticonfigParallel::TerminalTable, args: [Actor.current, @job_manager, configuration.fetch(:terminal, {})])
28
28
  setup_actor_supervision(@worker_supervisor, actor_name: :web_server, type: CapistranoMulticonfigParallel::WebServer, args: websocket_config)
29
-
30
29
  # Get a handle on the PoolManager
31
30
  # http://rubydoc.info/gems/celluloid/Celluloid/PoolManager
32
31
  # @workers = workers_pool.actor
32
+
33
33
  @stderr_buffer = StringIO.new
34
34
  @conditions = []
35
35
  @jobs = {}
@@ -221,6 +221,7 @@ module CapistranoMulticonfigParallel
221
221
  def worker_died(worker, reason)
222
222
  job = @worker_to_job[worker.mailbox.address]
223
223
  return true if job.blank? || job.rolling_back? || job.action != 'deploy'
224
+ job.rollback_changes_to_application
224
225
  mailbox = worker.mailbox
225
226
  @worker_to_job.delete(mailbox.address)
226
227
  log_to_file("RESTARTING: worker job #{job.inspect} with mailbox #{mailbox.inspect} and #{mailbox.address.inspect} died for reason: #{reason}")
@@ -20,10 +20,10 @@ module CapistranoMulticonfigParallel
20
20
  include CapistranoMulticonfigParallel::BaseActorHelper
21
21
  class TaskFailed < StandardError; end
22
22
 
23
- attr_accessor :job, :manager, :job_id, :app_name, :env_name, :action_name, :env_options, :machine, :client, :task_argv,
24
- :rake_tasks, :current_task_number, # tracking tasks
25
- :successfull_subscription, :subscription_channel, :publisher_channel, # for subscriptions and publishing events
26
- :job_termination_condition, :worker_state, :invocation_chain, :filename, :worker_log, :exit_status
23
+ attr_accessor :job, :manager, :job_id, :app_name, :env_name, :action_name, :env_options, :machine, :socket_connection, :task_argv,
24
+ :rake_tasks, :current_task_number, # tracking tasks
25
+ :successfull_subscription, :subscription_channel, :publisher_channel, # for subscriptions and publishing events
26
+ :job_termination_condition, :worker_state, :invocation_chain, :filename, :worker_log, :exit_status
27
27
 
28
28
  def initialize(*args)
29
29
  end
@@ -35,12 +35,13 @@ module CapistranoMulticonfigParallel
35
35
  @manager = manager
36
36
  @job_confirmation_conditions = []
37
37
  log_to_file("worker #{@job_id} received #{job.inspect}")
38
- @subscription_channel = "worker_#{@job_id}"
38
+ @subscription_channel = "#{CapistranoSentinel::RequestHooks::PUBLISHER_PREFIX}#{@job_id}"
39
39
  @machine = CapistranoMulticonfigParallel::StateMachine.new(@job, Actor.current)
40
40
  @manager.setup_worker_conditions(@job)
41
41
  manager.register_worker_for_job(job, Actor.current)
42
42
  end
43
43
 
44
+
44
45
  def worker_state
45
46
  if Actor.current.alive?
46
47
  @machine.state.to_s.green
@@ -51,21 +52,18 @@ module CapistranoMulticonfigParallel
51
52
  end
52
53
 
53
54
  def start_task
54
- log_to_file("exec worker #{@job_id} starts task")
55
- @client = CelluloidPubsub::Client.new(actor: Actor.current, enable_debug: debug_websocket?, channel: subscription_channel, log_file_path: websocket_config.fetch('log_file_path', nil))
55
+ log_to_file("exec worker #{@job_id} starts task and subscribes to #{@subscription_channel}")
56
+ @socket_connection = CelluloidPubsub::Client.new(actor: Actor.current, enable_debug: debug_websocket?, channel: subscription_channel, log_file_path: websocket_config.fetch('log_file_path', nil))
56
57
  end
57
58
 
58
59
  def publish_rake_event(data)
59
- @client.publish(rake_actor_id(data), data)
60
- end
61
-
62
- def rake_actor_id(_data)
63
- "rake_worker_#{@job_id}"
60
+ log_to_file("worker #{@job_id} rties to publish into channel #{CapistranoSentinel::RequestHooks::SUBSCRIPTION_PREFIX}#{@job_id} data #{data.inspect}")
61
+ @socket_connection.publish("#{CapistranoSentinel::RequestHooks::SUBSCRIPTION_PREFIX}#{@job_id}", data)
64
62
  end
65
63
 
66
64
  def on_message(message)
67
65
  log_to_file("worker #{@job_id} received: #{message.inspect}")
68
- if @client.succesfull_subscription?(message)
66
+ if @socket_connection.succesfull_subscription?(message)
69
67
  @successfull_subscription = true
70
68
  execute_after_succesfull_subscription
71
69
  else
@@ -89,7 +87,8 @@ module CapistranoMulticonfigParallel
89
87
  def execute_deploy
90
88
  log_to_file("invocation chain #{@job_id} is : #{@rake_tasks.inspect}")
91
89
  check_child_proces
92
- command = job.command.to_s
90
+ job.command.prepare_application_for_deployment
91
+ command = job.command.fetch_deploy_command
93
92
  log_to_file("worker #{@job_id} executes: #{command}")
94
93
  @child_process.async.work(@job, command, actor: Actor.current, silent: true)
95
94
  end
@@ -98,7 +97,7 @@ module CapistranoMulticonfigParallel
98
97
  @child_process = CapistranoMulticonfigParallel::ChildProcess.new
99
98
  Actor.current.link @child_process
100
99
  @child_process
101
- end
100
+ end
102
101
 
103
102
  def on_close(code, reason)
104
103
  log_to_file("worker #{@job_id} websocket connection closed: #{code.inspect}, #{reason.inspect}")
@@ -119,18 +118,29 @@ module CapistranoMulticonfigParallel
119
118
  elsif message_is_for_stdout?(message)
120
119
  result = Celluloid::Actor[:terminal_server].show_confirmation(message['question'], message['default'])
121
120
  publish_rake_event(message.merge('action' => 'stdin', 'result' => result, 'client_action' => 'stdin'))
121
+ elsif message_from_bundler?(message)
122
+
123
+ #gem_messsage = job.gem_specs.find{|spec| message['task'].include?(spec.name) }
124
+ # if gem_messsage.present?
125
+ # async.update_machine_state("insta")
126
+ # else
127
+ async.update_machine_state(message['task'])
128
+ #end
122
129
  else
123
- log_to_file(message, @job_id)
130
+ log_to_file(message, job_id: @job_id)
124
131
  end
125
132
  end
126
133
 
134
+
127
135
  def executed_task?(task)
128
136
  rake_tasks.present? && rake_tasks.index(task.to_s).present?
129
137
  end
130
138
 
131
139
  def task_approval(message)
132
140
  job_conditions = @manager.job_to_condition[@job_id]
141
+ log_to_file("worker #{@job_id} checks if task : #{message['task'].inspect} is included in #{configuration.task_confirmations.inspect}")
133
142
  if job_conditions.present? && configuration.task_confirmations.include?(message['task']) && message['action'] == 'invoke'
143
+ log_to_file("worker #{@job_id} signals approval for task : #{message['task'].inspect}")
134
144
  task_confirmation = job_conditions[message['task']]
135
145
  task_confirmation[:status] = 'confirmed'
136
146
  task_confirmation[:condition].signal(message['task'])
@@ -145,15 +155,17 @@ module CapistranoMulticonfigParallel
145
155
  invocation_chain << message['task'] if invocation_chain.last != message['task']
146
156
  end
147
157
 
148
- def update_machine_state(name)
149
- log_to_file("worker #{@job_id} triest to transition from #{@machine.state} to #{name}")
150
- @machine.go_to_transition(name.to_s)
158
+ def update_machine_state(name, options = {})
159
+ log_to_file("worker #{@job_id} triest to transition from #{@machine.state} to #{name}") unless options[:bundler]
160
+ @machine.go_to_transition(name.to_s, options)
151
161
  error_message = "worker #{@job_id} task #{name} failed "
152
162
  raise(CapistranoMulticonfigParallel::CelluloidWorker::TaskFailed.new(error_message), error_message) if job.failed? # force worker to rollback
153
163
  end
154
164
 
155
165
  def send_msg(channel, message = nil)
156
- publish channel, message.present? && message.is_a?(Hash) ? { job_id: @job_id }.merge(message) : { job_id: @job_id, time: Time.now }
166
+ message = message.present? && message.is_a?(Hash) ? { job_id: @job_id }.merge(message) : { job_id: @job_id, message: message }
167
+ log_to_file("worker #{@job_id} triest to send to #{channel} #{message}")
168
+ publish channel, message
157
169
  end
158
170
 
159
171
  def finish_worker(exit_status)
@@ -25,6 +25,7 @@ module CapistranoMulticonfigParallel
25
25
  @actor = @options.fetch(:actor, nil)
26
26
  @job_id = @job.id
27
27
  @exit_status = nil
28
+ @show_bundler = true
28
29
  end
29
30
 
30
31
  def setup_em_error_handler
@@ -96,6 +97,8 @@ module CapistranoMulticonfigParallel
96
97
  end
97
98
 
98
99
  def on_read_stdout(data)
100
+ @show_bundler = false if data.to_s.include?("The Gemfile's dependencies are satisfied") || data.to_s.include?("Bundle complete")
101
+ @actor.async.update_machine_state(truncate(data, 40), :bundler => true) if @show_bundler == true && data.strip.present? && data.strip != '.'
99
102
  io_callback('stdout', data)
100
103
  end
101
104
 
@@ -11,15 +11,21 @@ module CapistranoMulticonfigParallel
11
11
  machine
12
12
  end
13
13
 
14
- def go_to_transition(action)
14
+ def go_to_transition(action, options = {})
15
15
  transitions.on(action, state.to_s => action)
16
16
  @job.status = action
17
- machine.trigger(action)
17
+ if options[:bundler]
18
+ @job.bundler_status = action
19
+ actor_notify_state_change(state, "preparing_app_bundle_install", action)
20
+ else
21
+ @job.bundler_status = nil
22
+ machine.trigger(action)
23
+ end
18
24
  end
19
25
 
20
26
  def machine
21
27
  @machine ||= ComposableStateMachine::MachineWithExternalState.new(
22
- model, method(:state), method(:state=), state: @initial_state.to_s, callback_runner: self)
28
+ model, method(:state), method(:state=), state: @initial_state.to_s, callback_runner: self)
23
29
  @machine
24
30
  end
25
31
 
@@ -30,23 +36,24 @@ module CapistranoMulticonfigParallel
30
36
 
31
37
  def model
32
38
  ComposableStateMachine.model(
33
- transitions: transitions,
34
- behaviors: {
35
- enter: {
36
- any: proc do |current_state, event, new_state|
37
- actor_notify_state_change(current_state, event, new_state)
38
- end
39
- }
40
- },
41
- initial_state: @initial_state
39
+ transitions: transitions,
40
+ behaviors: {
41
+ enter: {
42
+ any: proc do |current_state, event, new_state|
43
+ actor_notify_state_change(current_state, event, new_state)
44
+ end
45
+ }
46
+ },
47
+ initial_state: @initial_state
42
48
  )
43
49
  end
44
50
 
45
- private
51
+ private
46
52
 
47
53
  def actor_notify_state_change(current_state, event, new_state)
48
54
  return unless @actor.alive?
49
- @actor.send_msg(CapistranoMulticonfigParallel::TerminalTable.topic, type: 'event', new_state: new_state, message: "Going from #{current_state} to #{new_state} due to a #{event} event")
55
+ @actor.log_to_file("statemachine #{@job.id} triest to transition from #{@current_state} to #{new_state} for event #{event}")
56
+ @actor.async.send_msg(CapistranoMulticonfigParallel::TerminalTable.topic, type: 'event', new_state: new_state , current_state: current_state, event: event, message: "Going from #{current_state} to #{new_state} due to a #{event} event")
50
57
  end
51
58
  end
52
59
  end
@@ -28,6 +28,7 @@ module CapistranoMulticonfigParallel
28
28
  @screen_erased = false
29
29
  async.run
30
30
  rescue => ex
31
+ puts ex.inspect
31
32
  rescue_exception(ex)
32
33
  end
33
34
 
@@ -39,7 +40,7 @@ module CapistranoMulticonfigParallel
39
40
  subscribe(CapistranoMulticonfigParallel::TerminalTable.topic, :notify_time_change)
40
41
  end
41
42
 
42
- def notify_time_change(_channel, _message)
43
+ def notify_time_change(_channel, message)
43
44
  table = Terminal::Table.new(title: 'Deployment Status Table', headings: default_heaadings)
44
45
  jobs = setup_table_jobs(table)
45
46
  display_table_on_terminal(table, jobs)
@@ -90,12 +91,12 @@ module CapistranoMulticonfigParallel
90
91
  end
91
92
 
92
93
  def managers_alive?
93
- @job_manager.alive? && @manager.alive?
94
+ @manager.alive?
94
95
  end
95
96
 
96
97
  def signal_complete
97
98
  if managers_alive? && @manager.all_workers_finished? && workers_terminated.instance_variable_get("@waiters").blank?
98
- condition.signal('completed') if @job_manager.alive? && condition.instance_variable_get("@waiters").present?
99
+ condition.signal('completed') if condition.instance_variable_get("@waiters").present?
99
100
  elsif !managers_alive?
100
101
  terminate
101
102
  end
@@ -6,8 +6,12 @@ module CapistranoMulticonfigParallel
6
6
  include CapistranoMulticonfigParallel::ApplicationHelper
7
7
 
8
8
  def display_on_screen(string, options = {})
9
- options = options.is_a?(Hash) ? options.stringify_keys : {}
10
- handle_string_display(string, options)
9
+ begin
10
+ options = options.is_a?(Hash) ? options.stringify_keys : {}
11
+ handle_string_display(string, options)
12
+ rescue => ex
13
+ rescue_error(ex, 'stderr')
14
+ end
11
15
  end
12
16
 
13
17
  private
@@ -11,7 +11,7 @@ module CapistranoMulticonfigParallel
11
11
  end
12
12
 
13
13
  def fetch_apps_needed_for_deployment(application, action)
14
- return [[], {}] unless @job_manager.multi_apps?
14
+ return [[], {}] unless @job_manager.send(:multi_apps?)
15
15
  if @job_manager.custom_command?
16
16
  show_interactive_menu(action)
17
17
  else
@@ -5,18 +5,21 @@ module CapistranoMulticonfigParallel
5
5
  class Job
6
6
  include CapistranoMulticonfigParallel::ApplicationHelper
7
7
 
8
- attr_reader :options, :application, :manager
9
- attr_writer :status, :exit_status
8
+ attr_reader :options, :application, :manager, :bundler_status
9
+ attr_writer :status, :exit_status, :bundler_status
10
10
 
11
11
  delegate :job_stage,
12
- :capistrano_action,
13
- :execute_standard_deploy,
14
- :setup_command_line,
15
- :gem_specs,
16
- to: :command
12
+ :capistrano_action,
13
+ :execute_standard_deploy,
14
+ :setup_command_line,
15
+ :job_capistrano_version,
16
+ :gem_specs,
17
+ :job_stage_for_terminal,
18
+ :rollback_changes_to_application,
19
+ to: :command
17
20
 
18
21
  delegate :stderr_buffer,
19
- to: :manager
22
+ to: :manager
20
23
 
21
24
  def initialize(application, options)
22
25
  @options = options.stringify_keys
@@ -25,6 +28,7 @@ module CapistranoMulticonfigParallel
25
28
  @gitflow ||= command.gitflow_enabled?
26
29
  end
27
30
 
31
+
28
32
  def save_stderr_error(data)
29
33
  return unless development_debug?
30
34
  return unless @manager.alive?
@@ -35,7 +39,7 @@ module CapistranoMulticonfigParallel
35
39
  end
36
40
 
37
41
  def env_variable
38
- CapistranoMulticonfigParallel::ENV_KEY_JOB_ID
42
+ CapistranoMulticonfigParallel.env_job_key_id
39
43
  end
40
44
 
41
45
  def command
@@ -47,12 +51,27 @@ module CapistranoMulticonfigParallel
47
51
  end
48
52
 
49
53
  def terminal_row
54
+ if bundler_status
55
+ bundler_terminal_row
56
+ else
57
+ [
58
+ { value: id.to_s },
59
+ { value: wrap_string(job_stage_for_terminal) },
60
+ { value: wrap_string(capistrano_action) },
61
+ { value: terminal_env_variables.map { |str| wrap_string(str) }.join("\n") },
62
+ { value: wrap_string(worker_state) }
63
+ ]
64
+ end
65
+ end
66
+
67
+
68
+ def bundler_terminal_row
50
69
  [
51
70
  { value: id.to_s },
52
- { value: wrap_string(job_stage) },
53
- { value: wrap_string(capistrano_action) },
71
+ { value: wrap_string(job_stage_for_terminal) },
72
+ { value: "Preparing app...setting up gems" },
54
73
  { value: terminal_env_variables.map { |str| wrap_string(str) }.join("\n") },
55
- { value: wrap_string(worker_state) }
74
+ { value: wrap_string(status.to_s.green) }
56
75
  ]
57
76
  end
58
77
 
@@ -90,12 +109,18 @@ module CapistranoMulticonfigParallel
90
109
  ].each do |hash|
91
110
  define_method hash[:name] do
92
111
  value = @options.fetch(hash[:name], hash[:default])
93
- value["#{env_variable}"] = id if hash[:name] == 'env_options'
112
+ setup_additional_env_variables(value) if hash[:name] == 'env_options'
94
113
  value = verify_empty_options(value)
95
114
  instance_variable_set("@#{hash[:name]}", instance_variable_get("@#{hash[:name]}") || value)
96
115
  end
97
116
  end
98
117
 
118
+
119
+ def setup_additional_env_variables(value)
120
+ value["#{env_variable}"] = id
121
+ value["capistrano_version"] = job_capistrano_version
122
+ end
123
+
99
124
  def finished?
100
125
  status == 'finished'
101
126
  end
@@ -6,7 +6,7 @@ module CapistranoMulticonfigParallel
6
6
  include FileUtils
7
7
  include CapistranoMulticonfigParallel::ApplicationHelper
8
8
 
9
- attr_reader :job, :job_capistrano_version, :legacy_capistrano
9
+ attr_reader :job, :job_capistrano_version, :legacy_capistrano, :tempfile
10
10
  delegate :id, :app, :stage, :action, :task_arguments, :env_options, :path, to: :job
11
11
 
12
12
  def initialize(job)
@@ -43,15 +43,29 @@ module CapistranoMulticonfigParallel
43
43
  %w(STAGES ACTION)
44
44
  end
45
45
 
46
- def bundle_gemfile_env
47
- "BUNDLE_GEMFILE=#{job_gemfile}"
46
+ def bundle_gemfile_env(gemfile = job_gemfile)
47
+ "BUNDLE_GEMFILE=#{gemfile}"
48
48
  end
49
49
 
50
+
50
51
  def gitflow_enabled?
51
- gitflow_version = job_gem_version("capistrano-gitflow")
52
+ gitflow_version = job_gem_version("capistrano-gitflow")
52
53
  gitflow_version.present? ? true : false
53
54
  end
54
55
 
56
+ def request_handler_gem_name
57
+ "capistrano_sentinel"
58
+ end
59
+
60
+ def request_handler_gem_available?
61
+ gitflow_version = job_gem_version(request_handler_gem_name)
62
+ gitflow_version.present? ? true : false
63
+ end
64
+
65
+ def job_stage_for_terminal
66
+ app.present? ? "#{app}:#{stage}" : "#{stage}"
67
+ end
68
+
55
69
  def job_stage
56
70
  multi_apps?(job_path) && app.present? ? "#{app}:#{stage}" : "#{stage}"
57
71
  end
@@ -96,29 +110,154 @@ module CapistranoMulticonfigParallel
96
110
  path || detect_root
97
111
  end
98
112
 
99
- def command_prefix
100
- bundle_install = path.present? ? "&& #{bundle_gemfile_env} bundle install" : ''
101
- "cd #{job_path} #{bundle_install}"
113
+ def user_home_directory
114
+ user = Etc.getlogin
115
+ Dir.home(user)
116
+ end
117
+
118
+ def rvm_bin_path
119
+ @rvm_path ||= `which rvm`
102
120
  end
103
121
 
104
- def to_s
105
- config_flags = CapistranoMulticonfigParallel.configuration_flags
106
- environment_options = setup_command_line(config_flags).join(' ')
107
- "#{command_prefix} && #{bundle_gemfile_env} bundle exec multi_cap #{job_stage} #{capistrano_action} #{environment_options}"
122
+ def bash_bin_path
123
+ @bash_bin_path ||= `which bash`
108
124
  end
109
125
 
110
- def to_json
111
- { command: to_s }
126
+ def rvm_installed?
127
+ rvm_bin_path.present?
128
+ end
129
+
130
+ def create_job_tempfile_command(output)
131
+ @tempfile ||= Tempfile.new(["multi_cap_#{job.id}_command_", ".rb"], encoding: 'utf-8')
132
+ @tempfile.write(output)
133
+ ObjectSpace.undefine_finalizer(@tempfile) # force garbage collector not to remove automatically the file
134
+ @tempfile.close
135
+ end
136
+
137
+ def rvm_scripts_path
138
+ File.join(File.dirname(File.dirname(rvm_bin_path)), 'scripts', 'rvm')
139
+ end
140
+
141
+ def job_rvmrc_file
142
+ File.join(job_path, '.rvmrc')
143
+ end
144
+
145
+ def job_rvmrc_enabled?
146
+ File.exists?(job_rvmrc_file)
147
+ end
148
+
149
+ def rvm_enabled_for_job?
150
+ job_rvmrc_enabled? && rvm_installed? && bash_bin_path.present?
151
+ end
152
+
153
+ def check_rvm_loaded
154
+ return "cd #{job_path}" unless rvm_enabled_for_job?
155
+ "source #{rvm_scripts_path} && rvm rvmrc trust #{job_path} && cd #{job_path} && source #{job_rvmrc_file}"
156
+ end
157
+
158
+ def fetch_deploy_command
159
+ # config_flags = CapistranoMulticonfigParallel.configuration_flags.merge("capistrano_version": job_capistrano_version)
160
+ environment_options = setup_command_line.join(' ')
161
+ command = "#{check_rvm_loaded} && if [ `which bundler |wc -l` = 0 ]; then gem install bundler;fi && (#{bundle_gemfile_env(job_gemfile_multi)} bundle check || #{bundle_gemfile_env(job_gemfile_multi)} bundle install ) && WEBSOCKET_LOGGING=#{debug_websocket?} LOG_FILE=#{websocket_config.fetch('log_file_path', nil)} #{bundle_gemfile_env(job_gemfile_multi)} bundle exec cap #{job_stage} #{capistrano_action} #{environment_options}"
162
+
163
+ command = "bash --login -c '#{command}'" if rvm_enabled_for_job?
164
+ command = command.inspect
165
+
166
+ command_text =<<-CMD
167
+ require 'rubygems'
168
+ require 'bundler'
169
+ Bundler.with_clean_env {
170
+ ENV['BUNDLE_GEMFILE'] = '#{job_gemfile_multi}'
171
+ ENV['#{CapistranoSentinel::RequestHooks::ENV_KEY_JOB_ID}']='#{job.id}'
172
+ Kernel.exec(#{command})
173
+ }
174
+ CMD
175
+
176
+ if rvm_enabled_for_job?
177
+ create_job_tempfile_command(command_text)
178
+ "ruby #{@tempfile.path}"
179
+ else
180
+ <<-CMD
181
+ cd #{job_path} && bundle exec ruby -e "#{command_text}"
182
+ CMD
183
+ end
184
+ end
185
+
186
+
187
+ def job_capfile
188
+ File.join(job_path, "Capfile")
189
+ end
190
+
191
+ def job_gemfile_multi
192
+ File.join(job_path, "Gemfile.multi_cap")
193
+ end
194
+
195
+ def prepare_application_for_deployment
196
+ check_handler_available
197
+ prepare_capfile
198
+ end
199
+
200
+ def check_handler_available
201
+ # '#{find_loaded_gem_property(request_handler_gem_name)}'
202
+ # path: '/home/raul/workspace/github/capistrano_sentinel'
203
+ FileUtils.rm_rf(job_gemfile_multi) if File.exists?(job_gemfile_multi)
204
+ FileUtils.touch(job_gemfile_multi)
205
+ if request_handler_gem_available?
206
+ FileUtils.copy(File.join(job_path, 'Gemfile'), job_gemfile_multi)
207
+ else
208
+ File.open(job_gemfile_multi, 'w') do |f|
209
+ cmd=<<-CMD
210
+ source "https://rubygems.org" do
211
+ gem "#{request_handler_gem_name}", '#{find_loaded_gem_property(request_handler_gem_name)}'
212
+ end
213
+ instance_eval(File.read(File.dirname(__FILE__) + "/Gemfile"))
214
+ CMD
215
+ f.write(cmd)
216
+ end
217
+ end
218
+ FileUtils.copy(File.join(job_path, 'Gemfile.lock'), "#{job_gemfile_multi}.lock")
219
+ end
220
+
221
+ def prepare_capfile
222
+ return if File.foreach(job_capfile).grep(/#{request_handler_gem_name}/).any?
223
+ File.open(job_capfile, 'a+') do |f|
224
+ cmd=<<-CMD
225
+ require "#{request_handler_gem_name}"
226
+ CMD
227
+ f.write(cmd)
228
+ end
229
+ end
230
+
231
+
232
+ def rollback_changes_to_application
233
+ FileUtils.rm_rf(job_gemfile_multi)
234
+ FileUtils.rm_rf("#{job_gemfile_multi}.lock")
235
+ unless request_handler_gem_available?
236
+ File.open(job_capfile, 'r') do |f|
237
+ File.open("#{job_capfile}.tmp", 'w') do |f2|
238
+ f.each_line do |line|
239
+ f2.write(line) unless line.include?(request_handler_gem_name)
240
+ end
241
+ end
242
+ end
243
+ FileUtils.mv "#{job_capfile}.tmp", job_capfile
244
+ FileUtils.rm_rf("#{job_capfile}.tmp")
245
+ end
246
+ FileUtils.rm_rf(@tempfile.path) if defined?(@tempfile) && @tempfile
112
247
  end
113
248
 
114
249
  def execute_standard_deploy(action = nil)
115
- run_shell_command(to_s)
250
+ run_shell_command(fetch_deploy_command)
116
251
  rescue => ex
117
252
  rescue_error(ex, 'stderr')
118
253
  execute_standard_deploy('deploy:rollback') if action.blank? && @name == 'deploy'
119
254
  end
120
255
 
121
- private
256
+ private
257
+
258
+ def get_bash_command(command)
259
+ Shellwords.escape(command)
260
+ end
122
261
 
123
262
  def run_shell_command(command)
124
263
  sh("#{command}")