Class: CapistranoMulticonfigParallel::Application

Inherits:
Object
  • Object
show all
Includes:
ApplicationHelper
Defined in:
lib/capistrano_multiconfig_parallel/application.rb

Overview

finds app dependencies, shows menu and delegates jobs to celluloid manager

Instance Attribute Summary collapse

Instance Method Summary collapse

Methods included from ApplicationHelper

action_confirmed?, fetch_parsed_string, fetch_remaining_arguments, find_remaining_args, get_question_details, message_from_bundler?, message_is_about_a_task?, message_is_for_stdout?, msg_for_stdin?, parse_json, parse_task_string, percent_of, regex_last_match, setup_command_line_standard, truncate, wrap_string

Methods included from CapistranoHelper

env_key_format, env_prefix, filtered_env_keys_format, setup_flags_for_job, trace_flag

Methods included from GemHelper

fetch_gem_version, find_loaded_gem, find_loaded_gem_property, format_gem_version, get_parsed_version, verify_gem_version

Methods included from StagesHelper

app_names_from_stages, application_supports_multi_apps?, check_stage_path, checks_paths, configuration_has_valid_path?, fetch_apps_from_file, fetch_paths_from_file, fetch_stages_app, fetch_stages_paths, independent_deploy?, multi_apps?, sorted_paths, stages, stages_paths, stages_root

Methods included from ParseHelper

check_hash_set, check_numeric, strip_characters_from_string, value_is_array?, verify_array_of_strings, verify_empty_options, warn_array_without_strings

Methods included from CoreHelper

app_debug_enabled?, ask_confirm, ask_stdout_confirmation, check_terminal_tty, debug_websocket?, development_debug?, error_filtered?, execute_with_rescue, find_worker_log, force_confirmation, format_error, log_error, log_output_error, log_to_file, multi_fetch_argv, print_to_log_file, rescue_error, rescue_interrupt, setup_filename_logger, setup_logger_formatter, show_warning, terminal_actor, terminal_errors?, websocket_config, websocket_server_config

Methods included from InternalHelper

arg_is_in_default_config?, check_file, create_log_file, custom_commands, default_config_keys, default_internal_config, default_internal_configuration_params, detect_root, enable_main_log_file, fail_capfile_not_found, fetch_default_internal_config, find_config_type, find_env_multi_cap_root, find_file_by_names, find_file_in_directory, get_current_gem_name, internal_config_directory, internal_config_file, log_directory, main_log_file, multi_level_prop, pathname_is_root?, pwd_directory, pwd_parent_dir, root, setup_default_configuration_types, sliced_default_config, try_detect_file, try_detect_file_in_dir

Constructor Details

#initializeApplication

Returns a new instance of Application.



25
26
27
28
29
30
31
32
33
34
35
# File 'lib/capistrano_multiconfig_parallel/application.rb', line 25

def initialize
  celluloid_running = Celluloid.running? rescue false
  Celluloid.boot unless celluloid_running
  CapistranoMulticonfigParallel.enable_logging
  @stage_apps = multi_apps? ? app_names_from_stages : []
  collect_command_line_tasks(CapistranoMulticonfigParallel.original_args)
  @bundler_workers_store = {}
  @jobs = []
  @checked_job_paths = []
  @patched_job_paths = [] # for deploy
end

Instance Attribute Details

#applicationObject (readonly)

Returns the value of attribute application.



7
8
9
# File 'lib/capistrano_multiconfig_parallel/application.rb', line 7

def application
  @application
end

#argsObject (readonly)

Returns the value of attribute args.



7
8
9
# File 'lib/capistrano_multiconfig_parallel/application.rb', line 7

def args
  @args
end

#argvObject (readonly)

Returns the value of attribute argv.



7
8
9
# File 'lib/capistrano_multiconfig_parallel/application.rb', line 7

def argv
  @argv
end

#bundler_workers_storeObject (readonly)

Returns the value of attribute bundler_workers_store.



7
8
9
# File 'lib/capistrano_multiconfig_parallel/application.rb', line 7

def bundler_workers_store
  @bundler_workers_store
end

#checked_job_pathsObject (readonly)

Returns the value of attribute checked_job_paths.



7
8
9
# File 'lib/capistrano_multiconfig_parallel/application.rb', line 7

def checked_job_paths
  @checked_job_paths
end

#conditionObject (readonly)

Returns the value of attribute condition.



7
8
9
# File 'lib/capistrano_multiconfig_parallel/application.rb', line 7

def condition
  @condition
end

#default_stageObject (readonly)

Returns the value of attribute default_stage.



7
8
9
# File 'lib/capistrano_multiconfig_parallel/application.rb', line 7

def default_stage
  @default_stage
end

#dependency_trackerObject (readonly)

Returns the value of attribute dependency_tracker.



7
8
9
# File 'lib/capistrano_multiconfig_parallel/application.rb', line 7

def dependency_tracker
  @dependency_tracker
end

#jobsObject (readonly)

Returns the value of attribute jobs.



7
8
9
# File 'lib/capistrano_multiconfig_parallel/application.rb', line 7

def jobs
  @jobs
end

#managerObject (readonly)

Returns the value of attribute manager.



7
8
9
# File 'lib/capistrano_multiconfig_parallel/application.rb', line 7

def manager
  @manager
end

#nameObject (readonly)

Returns the value of attribute name.



7
8
9
# File 'lib/capistrano_multiconfig_parallel/application.rb', line 7

def name
  @name
end

#patched_job_pathsObject

Returns the value of attribute patched_job_paths.



7
8
9
# File 'lib/capistrano_multiconfig_parallel/application.rb', line 7

def patched_job_paths
  @patched_job_paths
end

#stageObject (readonly)

Returns the value of attribute stage.



7
8
9
# File 'lib/capistrano_multiconfig_parallel/application.rb', line 7

def stage
  @stage
end

#stage_appsObject (readonly)

Returns the value of attribute stage_apps.



7
8
9
# File 'lib/capistrano_multiconfig_parallel/application.rb', line 7

def stage_apps
  @stage_apps
end

#top_level_tasksObject (readonly)

Returns the value of attribute top_level_tasks.



7
8
9
# File 'lib/capistrano_multiconfig_parallel/application.rb', line 7

def top_level_tasks
  @top_level_tasks
end

Instance Method Details

#action_keyObject



108
109
110
# File 'lib/capistrano_multiconfig_parallel/application.rb', line 108

def action_key
  'ACTION'
end

#add_job_to_list_of_jobs(job) ⇒ Object



240
241
242
# File 'lib/capistrano_multiconfig_parallel/application.rb', line 240

def add_job_to_list_of_jobs(job)
  @jobs << job unless job_can_tag_staging?(job)
end

#boxes_keyObject



201
202
203
# File 'lib/capistrano_multiconfig_parallel/application.rb', line 201

def boxes_key
  'BOX'
end

#call_task_deploy_app(options = {}) ⇒ Object



205
206
207
208
209
210
211
212
213
214
215
# File 'lib/capistrano_multiconfig_parallel/application.rb', line 205

def call_task_deploy_app(options = {})
  options = options.stringify_keys
  main_box_name = @argv[boxes_key].blank? ? '' : @argv[boxes_key]
  boxes = strip_characters_from_string(main_box_name).split(',').compact
  stage = options.fetch('stage', @default_stage)
  if configuration.development_stages.include?(stage) && boxes.present?
    execute_on_multiple_boxes(boxes, options)
  else
    prepare_job(options)
  end
end

#can_tag_staging?Boolean

Returns:

  • (Boolean)


161
162
163
# File 'lib/capistrano_multiconfig_parallel/application.rb', line 161

def can_tag_staging?
  wants_deploy_production? && fetch_multi_stages.include?('staging')
end

#check_before_startingObject



117
118
119
120
121
122
123
# File 'lib/capistrano_multiconfig_parallel/application.rb', line 117

def check_before_starting
  @dependency_tracker = CapistranoMulticonfigParallel::DependencyTracker.new(self)
  @default_stage = configuration.development_stages.present? ? configuration.development_stages.first : 'development'
  @condition = Celluloid::Condition.new
  @manager = CapistranoMulticonfigParallel::CelluloidManager.new(self)
  @manager.start_bundler_supervision_if_needed
end

#collect_command_line_tasks(args) ⇒ Object

:nodoc:



95
96
97
98
99
100
101
102
103
104
105
106
# File 'lib/capistrano_multiconfig_parallel/application.rb', line 95

def collect_command_line_tasks(args) # :nodoc:
  @argv = {}
  @top_level_tasks = []
  args.each do |arg|
    if arg =~ /^(\w+)=(.*)$/m
      @argv[Regexp.last_match(1)] = Regexp.last_match(2)
    else
      @top_level_tasks << arg unless arg =~ /^-/
    end
  end
  @top_level_tasks.push(Rake.application.default_task_name) if @top_level_tasks.blank?
end

#collect_jobs(options = {}, &_block) ⇒ Object



125
126
127
128
129
130
131
# File 'lib/capistrano_multiconfig_parallel/application.rb', line 125

def collect_jobs(options = {}, &_block)
  options = prepare_options(options)
  options = options.stringify_keys
  apps, app_options = @dependency_tracker.fetch_apps_needed_for_deployment(options['app'], options['action'])
  deploy_multiple_apps(apps, options)
  deploy_app(options.merge(app_options)) if !custom_command? || !multi_apps?
end

#custom_command?Boolean

Returns:

  • (Boolean)


68
69
70
# File 'lib/capistrano_multiconfig_parallel/application.rb', line 68

def custom_command?
  custom_commands.include?(@top_level_tasks.first)
end

#deploy_app(options = {}) ⇒ Object



165
166
167
168
169
170
171
# File 'lib/capistrano_multiconfig_parallel/application.rb', line 165

def deploy_app(options = {})
  options = options.stringify_keys
  call_task_deploy_app({
    app: options['app'],
    action: options['action']
  }.reverse_merge(options))
end

#deploy_multiple_apps(applications, options) ⇒ Object



60
61
62
63
64
65
66
# File 'lib/capistrano_multiconfig_parallel/application.rb', line 60

def deploy_multiple_apps(applications, options)
  options = options.stringify_keys
  return unless applications.present?
  applications.each do |app|
    deploy_app(options.merge('app' => app['app'], 'path' => app.fetch('path', nil)))
  end
end

#execute_on_multiple_boxes(boxes, options) ⇒ Object



316
317
318
319
320
321
# File 'lib/capistrano_multiconfig_parallel/application.rb', line 316

def execute_on_multiple_boxes(boxes, options)
  boxes.each do |box_name|
    options['env_options'][boxes_key] = box_name
    prepare_job(options)
  end
end

#fetch_app_additional_env_options(variable) ⇒ Object



306
307
308
309
310
311
312
313
314
# File 'lib/capistrano_multiconfig_parallel/application.rb', line 306

def fetch_app_additional_env_options(variable)
  options = {}
  return options if variable.blank?
  env_options = strip_characters_from_string(variable)
  env_options = env_options.split(' ')
  options = multi_fetch_argv(env_options)
  options.stringify_keys!
  options
end

#fetch_multi_stagesObject



150
151
152
153
154
155
# File 'lib/capistrano_multiconfig_parallel/application.rb', line 150

def fetch_multi_stages
  custom_stages = @argv[stages_key].blank? ? '' : @argv[stages_key]
  custom_stages = strip_characters_from_string(custom_stages).split(',').compact if custom_stages.present?
  custom_stages = custom_stages.present? ? custom_stages : [@default_stage]
  custom_stages
end

#get_app_additional_env_options(app, app_message) ⇒ Object



173
174
175
176
177
178
179
# File 'lib/capistrano_multiconfig_parallel/application.rb', line 173

def get_app_additional_env_options(app, app_message)
  app_name = (app.is_a?(Hash) && app[:app].present?) ? app[:app].camelcase : app
  app_name = app_name.present? ? app_name : 'current application'
  message = "Please write additional ENV options for #{app_name} for #{app_message}"
  app_additional_env_options = ask_confirm(message, nil)
  fetch_app_additional_env_options(app_additional_env_options)
end

#initialize_dataObject



88
89
90
91
92
93
# File 'lib/capistrano_multiconfig_parallel/application.rb', line 88

def initialize_data
  @application = custom_command? ? nil : @top_level_tasks.first.split(':').reverse[1]
  @stage = custom_command? ? nil : @top_level_tasks.first.split(':').reverse[0]
  @stage = @stage.present? ? @stage : @default_stage
  @name, @args = parse_task_string(@top_level_tasks[1])
end

#job_can_tag_staging?(job) ⇒ Boolean

Returns:

  • (Boolean)


278
279
280
# File 'lib/capistrano_multiconfig_parallel/application.rb', line 278

def job_can_tag_staging?(job)
  can_tag_staging? && job.stage == 'production' && job.gitflow.present?
end

#job_path(options) ⇒ Object



282
283
284
285
# File 'lib/capistrano_multiconfig_parallel/application.rb', line 282

def job_path(options)
  path = options["path"].present? ? options["path"] : nil
  path.present? && File.directory?(path) ? path : detect_root
end

#job_stage(options) ⇒ Object



291
292
293
# File 'lib/capistrano_multiconfig_parallel/application.rb', line 291

def job_stage(options)
  options['stage']
end

#job_stage_valid?(options) ⇒ Boolean

Returns:

  • (Boolean)


287
288
289
# File 'lib/capistrano_multiconfig_parallel/application.rb', line 287

def job_stage_valid?(options)
  stages(job_path(options)).include?(job_stage(options))
end

#jobs_restore_application_stateObject



322
323
324
325
326
# File 'lib/capistrano_multiconfig_parallel/application.rb', line 322

def jobs_restore_application_state
  @jobs.each do |job|
    job.rollback_changes_to_application
  end
end

#prepare_job(options) ⇒ Object



244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
# File 'lib/capistrano_multiconfig_parallel/application.rb', line 244

def prepare_job(options)
  options = options.stringify_keys
  app = options.fetch('app', '')
  path = job_path(options) rescue nil
#  raise [app, @stage_apps, job_stage(options) , stages(job_path(options))].inspect
  return raise_invalid_job_config(path) if (app.present? && !@stage_apps.include?(app)) || !job_stage_valid?(options)
  app = options.fetch('app', '')
  box = options['env_options'][boxes_key]
  message = box.present? ? "BOX #{box}:" : "stage #{options['stage']}:"
  env_opts = get_app_additional_env_options(app, message)

  options['env_options'] = options['env_options'].reverse_merge(env_opts)

  env_options = options['env_options']
  job_env_options = custom_command? ? env_options.except(action_key) : env_options

  job = CapistranoMulticonfigParallel::Job.new(self, options.merge(
  action: custom_command? && env_options[action_key].present? ? env_options[action_key] : options['action'],
  env_options: job_env_options,
  path:  job_path(options)

  ))

  if configuration.check_app_bundler_dependencies.to_s.downcase == 'true' && job.job_gemfile.present?
    if !@checked_job_paths.include?(job.job_path)
      @checked_job_paths << job.job_path
      @manager.bundler_workers.work(job) # make sure we have installed the dependencies first for this application
    end
  else
    add_job_to_list_of_jobs(job)
  end

end

#prepare_options(options) ⇒ Object



296
297
298
299
300
301
302
303
304
# File 'lib/capistrano_multiconfig_parallel/application.rb', line 296

def prepare_options(options)
  options = options.stringify_keys
  options['app'] = options.fetch('app', @application.to_s.clone)
  options['action'] = options.fetch('action', @name.to_s.clone)
  options['stage'] = options.fetch('stage', @stage.to_s.clone)
  options['env_options'] = options.fetch('env_options', @argv.clone)
  options['task_arguments'] = options.fetch('task_arguments', @args.clone)
  options
end

#process_jobsObject



133
134
135
136
137
138
139
140
# File 'lib/capistrano_multiconfig_parallel/application.rb', line 133

def process_jobs
  return unless @jobs.present?
  if configuration.multi_secvential.to_s.downcase == 'true'
    @jobs.each(&:execute_standard_deploy)
  else
    run_async_jobs
  end
end

#raise_invalid_job_config(path = nil) ⇒ Object



77
78
79
80
81
82
83
84
85
86
# File 'lib/capistrano_multiconfig_parallel/application.rb', line 77

def raise_invalid_job_config(path = nil)
  if independent_deploy?(path)
      puts 'Invalid execution, please call something such as `multi_cap app_name:production deploy`, where production is a stage you have defined and the "app_name" is a name that you defined in your multi_cap.yml file'.red
  elsif application_supports_multi_apps?
    puts 'Invalid execution, please call something such as `multi_cap app_name:production deploy`, where production is a stage you have defined and the "app_name" is name that you have defined in your application'.red
  else
    puts 'Invalid execution, please call something such as `multi_cap production deploy`, where production is a stage you have defined'.red
  end
  exit(false)
end

#runObject



181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
# File 'lib/capistrano_multiconfig_parallel/application.rb', line 181

def run
  FileUtils.rm Dir["#{log_directory}/worker_*.log"]
  FileUtils.rm Dir["#{log_directory}/multi_cap_worker_*.log"]
  options = {}
  if custom_command?
    options = verify_options_custom_command(options)
    run_custom_command(options)
  else
    collect_jobs(options)
  end
  if configuration.check_app_bundler_dependencies.to_s.downcase == 'true'
    loop do
      log_to_file("Trying to sleep until all budler workes are done (#{@jobs.size} and #{@bundler_workers_store.size})")
      break if @jobs.size == @bundler_workers_store.size
      sleep(0.1)
    end
  end
  process_jobs
end

#run_async_jobsObject



217
218
219
220
221
222
223
224
225
226
227
228
229
230
# File 'lib/capistrano_multiconfig_parallel/application.rb', line 217

def run_async_jobs
  return unless @jobs.present?
  @jobs.pmap do |job|
    @manager.async.delegate_job(job)
  end
  unless can_tag_staging? && tag_staging_exists?
    until @manager.registration_complete
      sleep(0.1) # keep current thread alive
    end
    return unless @manager.registration_complete
    @manager.async.process_jobs
  end
  wait_jobs_termination
end

#run_custom_command(options) ⇒ Object



52
53
54
55
56
57
58
# File 'lib/capistrano_multiconfig_parallel/application.rb', line 52

def run_custom_command(options)
  custom_stages = fetch_multi_stages
  return if custom_stages.blank?
  custom_stages.each do |stage|
    collect_jobs(options.merge('stage' => stage))
  end
end

#stages_keyObject



146
147
148
# File 'lib/capistrano_multiconfig_parallel/application.rb', line 146

def stages_key
  'STAGES'
end

#startObject



37
38
39
40
41
42
43
# File 'lib/capistrano_multiconfig_parallel/application.rb', line 37

def start
  verify_app_dependencies if multi_apps? && configuration.application_dependencies.present?
  initialize_data
  verify_valid_data
  check_before_starting
  run
end

#tag_staging_exists?Boolean

check exists task from capistrano-gitflow

Returns:

  • (Boolean)


142
143
144
# File 'lib/capistrano_multiconfig_parallel/application.rb', line 142

def tag_staging_exists? # check exists task from capistrano-gitflow
  @jobs.find(&:gitflow).present?
end

#verify_app_dependenciesObject

Raises:

  • (ArgumentError)


45
46
47
48
49
50
# File 'lib/capistrano_multiconfig_parallel/application.rb', line 45

def verify_app_dependencies
  wrong = configuration.application_dependencies.find do |hash|
    !@stage_apps.include?(hash[:app]) || (hash[:dependencies].present? && hash[:dependencies].find { |val| !@stage_apps.include?(val) })
  end
  raise ArgumentError, "Invalid configuration for #{wrong.inspect}".red if wrong.present?
end

#verify_options_custom_command(options) ⇒ Object



112
113
114
115
# File 'lib/capistrano_multiconfig_parallel/application.rb', line 112

def verify_options_custom_command(options)
  options[:action] = @argv[action_key].present? ? @argv[action_key] : 'deploy'
  options
end

#verify_valid_dataObject



72
73
74
75
# File 'lib/capistrano_multiconfig_parallel/application.rb', line 72

def verify_valid_data
  return if  @top_level_tasks != ['default']
  raise_invalid_job_config
end

#wait_jobs_terminationObject



232
233
234
235
236
237
238
# File 'lib/capistrano_multiconfig_parallel/application.rb', line 232

def wait_jobs_termination
  return if configuration.multi_secvential.to_s.downcase == 'true'
  result = @condition.wait
  return unless result.present?
  @manager.terminate
  #terminate
end

#wants_deploy_production?Boolean

Returns:

  • (Boolean)


157
158
159
# File 'lib/capistrano_multiconfig_parallel/application.rb', line 157

def wants_deploy_production?
  (!custom_command? && @stage == 'production') || (custom_command? && fetch_multi_stages.include?('production'))
end