capistrano_multiconfig_parallel 1.7.2 → 2.0.0.beta1

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.
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}")