capistrano_multiconfig_parallel 0.0.1

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 (40) hide show
  1. checksums.yaml +7 -0
  2. data/.coveralls.yml +2 -0
  3. data/.gitignore +21 -0
  4. data/.rspec +1 -0
  5. data/.rubocop.yml +68 -0
  6. data/.travis.yml +12 -0
  7. data/CONTRIBUTING.md +44 -0
  8. data/Gemfile +3 -0
  9. data/Guardfile +12 -0
  10. data/LICENSE +20 -0
  11. data/README.md +220 -0
  12. data/Rakefile +56 -0
  13. data/bin/multi_cap +7 -0
  14. data/capistrano_multiconfig_parallel.gemspec +51 -0
  15. data/img/parallel_demo.png +0 -0
  16. data/init.rb +1 -0
  17. data/lib/capistrano_multiconfig_parallel/application.rb +57 -0
  18. data/lib/capistrano_multiconfig_parallel/base.rb +92 -0
  19. data/lib/capistrano_multiconfig_parallel/celluloid/celluloid_manager.rb +178 -0
  20. data/lib/capistrano_multiconfig_parallel/celluloid/celluloid_worker.rb +238 -0
  21. data/lib/capistrano_multiconfig_parallel/celluloid/child_process.rb +104 -0
  22. data/lib/capistrano_multiconfig_parallel/celluloid/rake_worker.rb +83 -0
  23. data/lib/capistrano_multiconfig_parallel/celluloid/state_machine.rb +49 -0
  24. data/lib/capistrano_multiconfig_parallel/celluloid/terminal_table.rb +122 -0
  25. data/lib/capistrano_multiconfig_parallel/cli.rb +55 -0
  26. data/lib/capistrano_multiconfig_parallel/configuration.rb +70 -0
  27. data/lib/capistrano_multiconfig_parallel/helpers/base_manager.rb +217 -0
  28. data/lib/capistrano_multiconfig_parallel/helpers/multi_app_manager.rb +84 -0
  29. data/lib/capistrano_multiconfig_parallel/helpers/single_app_manager.rb +48 -0
  30. data/lib/capistrano_multiconfig_parallel/helpers/standard_deploy.rb +40 -0
  31. data/lib/capistrano_multiconfig_parallel/initializers/conf.rb +6 -0
  32. data/lib/capistrano_multiconfig_parallel/initializers/confirm_question.rb +25 -0
  33. data/lib/capistrano_multiconfig_parallel/initializers/i18n.rb +10 -0
  34. data/lib/capistrano_multiconfig_parallel/initializers/rake.rb +28 -0
  35. data/lib/capistrano_multiconfig_parallel/multi_app_helpers/dependency_tracker.rb +111 -0
  36. data/lib/capistrano_multiconfig_parallel/multi_app_helpers/interactive_menu.rb +61 -0
  37. data/lib/capistrano_multiconfig_parallel/version.rb +16 -0
  38. data/lib/capistrano_multiconfig_parallel.rb +2 -0
  39. data/spec/spec_helper.rb +48 -0
  40. metadata +648 -0
@@ -0,0 +1,25 @@
1
+
2
+ module Capistrano
3
+ # class used for configuration
4
+ class Configuration
5
+ # class used for confirming customized questions
6
+ class ConfirmQuestion < Capistrano::Configuration::Question
7
+ def question
8
+ I18n.t(:confirm_question, key: key, default_value: default, scope: :capistrano)
9
+ end
10
+ end
11
+ end
12
+ end
13
+
14
+ Capistrano::DSL::Env.class_eval do
15
+ def ask_confirm(key, value, options = {})
16
+ env.ask_confirm(key, value, options)
17
+ end
18
+ end
19
+
20
+ Capistrano::Configuration.class_eval do
21
+ def ask_confirm(key, default = nil, options = {})
22
+ question = Capistrano::Configuration::ConfirmQuestion.new(key, default, options)
23
+ set(key, question)
24
+ end
25
+ end
@@ -0,0 +1,10 @@
1
+ require 'i18n'
2
+ en = {
3
+ confirm_question: '%{key} (%{default_value}): '
4
+ }
5
+
6
+ I18n.backend.store_translations(:en, capistrano: en)
7
+
8
+ if I18n.respond_to?(:enforce_available_locales=)
9
+ I18n.enforce_available_locales = true
10
+ end
@@ -0,0 +1,28 @@
1
+ Rake::Task.class_eval do
2
+ alias_method :original_execute, :execute
3
+
4
+ def execute(args = nil)
5
+ job_id = ENV[CapistranoMulticonfigParallel::ENV_KEY_JOB_ID]
6
+ if job_id.present?
7
+ run_the_actor(job_id) do
8
+ original_execute(*args)
9
+ end
10
+ else
11
+ original_execute(*args)
12
+ end
13
+ end
14
+
15
+ def run_the_actor(job_id, &block)
16
+ rake_actor_id = ENV['count_rake'].present? ? "rake_worker_#{job_id}_count" : "rake_worker_#{job_id}"
17
+ if Celluloid::Actor[rake_actor_id].blank?
18
+ CapistranoMulticonfigParallel::RakeWorker.supervise_as rake_actor_id
19
+ Celluloid::Actor[rake_actor_id].work(ENV, self, rake_actor_id: rake_actor_id)
20
+ else
21
+ Celluloid::Actor[rake_actor_id].publish_new_work(ENV, self)
22
+ end
23
+ until Celluloid::Actor[rake_actor_id].task_approved
24
+ sleep(0.1) # keep current thread alive
25
+ end
26
+ block.call if Celluloid::Actor[rake_actor_id].task_approved
27
+ end
28
+ end
@@ -0,0 +1,111 @@
1
+ require_relative './interactive_menu'
2
+
3
+ module CapistranoMulticonfigParallel
4
+ # class used to find application dependencies
5
+ class DependencyTracker
6
+ attr_accessor :job_manager
7
+
8
+ def initialize(job_manager)
9
+ @job_manager = job_manager
10
+ end
11
+
12
+ def application_dependencies
13
+ deps = CapistranoMulticonfigParallel.configuration.track_dependencies ? CapistranoMulticonfigParallel.configuration.application_dependencies : []
14
+ deps.present? && deps.is_a?(Array) ? deps.map(&:stringify_keys) : []
15
+ end
16
+
17
+ def all_websites_return_applications_selected
18
+ applications = application_dependencies.map { |hash| hash['app'].camelcase }
19
+ applications << 'all_frameworks'
20
+ interactive_menu = CapistranoMulticonfigParallel::InteractiveMenu.new
21
+ applications_selected = interactive_menu.show_all_websites_interactive_menu(applications)
22
+
23
+ applications_selected = applications_selected.gsub("\r\n", '') if applications_selected.present?
24
+ applications_selected = applications_selected.gsub("\n", '') if applications_selected.present?
25
+ applications_selected = applications_selected.split(',') if applications_selected.present?
26
+ applications_selected.present? ? applications_selected : []
27
+ end
28
+
29
+ def add_dependency_app(app_to_deploy, apps_dependencies, applications_to_deploy)
30
+ return unless app_to_deploy.present?
31
+ applications_to_deploy << app_to_deploy
32
+ return unless app_to_deploy['dependencies'].present?
33
+ app_to_deploy['dependencies'].each do |dependency|
34
+ dependency_app = application_dependencies.find { |hash| hash['app'] == dependency }
35
+ apps_dependencies << dependency_app if dependency_app.present?
36
+ end
37
+ end
38
+
39
+ def find_apps_and_deps(applications_selected)
40
+ applications_to_deploy = []
41
+ apps_dependencies = []
42
+ applications_selected.each do |app|
43
+ app_to_deploy = application_dependencies.find { |hash| hash['app'].camelcase == app }
44
+ add_dependency_app(app_to_deploy, apps_dependencies, applications_to_deploy)
45
+ end
46
+ [applications_to_deploy, apps_dependencies]
47
+ end
48
+
49
+ def check_app_dependency_unique(applications_selected, apps_dependencies, applications_to_deploy, action)
50
+ return applications_to_deploy if applications_selected.blank? || apps_dependencies.blank? || (apps_dependencies.map { |app| app['app'] } - applications_to_deploy.map { |app| app['app'] }).blank?
51
+ set :apps_dependency_confirmation, ask_confirm("Do you want to #{action} all dependencies also ? (Y/N):", 'N')
52
+ applications_to_deploy = applications_to_deploy.concat(apps_dependencies) if fetch(:apps_dependency_confirmation).present? && fetch(:apps_dependency_confirmation).downcase == 'y'
53
+ applications_to_deploy
54
+ end
55
+
56
+ def get_applications_to_deploy(action, applications_selected)
57
+ all_frameworks = applications_selected.find { |app| app == 'all_frameworks' }
58
+ if all_frameworks.present?
59
+ applications_to_deploy = application_dependencies.map { |hash| hash }
60
+ else
61
+ applications_to_deploy, apps_dependencies = find_apps_and_deps(applications_selected)
62
+ applications_to_deploy = check_app_dependency_unique(applications_selected, apps_dependencies, applications_to_deploy, action)
63
+ end
64
+ if applications_to_deploy.present?
65
+ applications_to_deploy = applications_to_deploy.uniq
66
+ applications_to_deploy = applications_to_deploy.sort_by { |hash| hash['priority'] }
67
+ end
68
+ show_frameworks_used(applications_to_deploy, all_frameworks, action)
69
+ end
70
+
71
+ def show_frameworks_used(applications_to_deploy, all_frameworks, action)
72
+ return [] if applications_to_deploy.blank? || applications_to_deploy.size <= 1
73
+ puts 'The following frameworks will be used:'
74
+ app_names = []
75
+ if all_frameworks.present?
76
+ app_names = applications_to_deploy.map { |app| app['app'].camelcase }
77
+ else
78
+ app_names = applications_to_deploy.map { |app| application_dependencies.find { |hash| hash['app'] == app['app'] }['app'].camelcase }
79
+ end
80
+ print_frameworks_used(app_names, applications_to_deploy, action)
81
+ end
82
+
83
+ def print_frameworks_used(app_names, applications_to_deploy, action)
84
+ app_names.each { |app| puts "#{app}" }
85
+ set :apps_deploy_confirmation, ask_confirm("Are you sure you want to #{action} these apps? (Y/N):", 'N')
86
+ if fetch(:apps_deploy_confirmation).blank? || (fetch(:apps_deploy_confirmation).present? && fetch(:apps_deploy_confirmation).downcase == 'n')
87
+ return []
88
+ elsif fetch(:apps_deploy_confirmation).present? && fetch(:apps_deploy_confirmation).downcase == 'y'
89
+ return applications_to_deploy
90
+ end
91
+ end
92
+
93
+ def fetch_apps_needed_for_deployment(application, action)
94
+ applications = []
95
+ if @job_manager.custom_command? && @job_manager.multi_apps?
96
+ apps_selected = all_websites_return_applications_selected
97
+ applications = get_applications_to_deploy(action, apps_selected)
98
+ elsif CapistranoMulticonfigParallel.configuration.track_dependencies
99
+ if application.present?
100
+ applications = get_applications_to_deploy(action, [application.camelcase])
101
+ applications = applications.delete_if { |hash| hash['app'] == application }
102
+ else
103
+ applications = []
104
+ end
105
+ else
106
+ applications = []
107
+ end
108
+ applications
109
+ end
110
+ end
111
+ end
@@ -0,0 +1,61 @@
1
+ module CapistranoMulticonfigParallel
2
+ # methods used for the interactive menu where are listed all aplications
3
+ class InteractiveMenu
4
+ def show_all_websites_interactive_menu(applications)
5
+ msg = ''
6
+ choices = []
7
+ print_menu_choices(msg, choices, applications)
8
+ print "\nYou selected"
9
+ msg = ' nothing'
10
+ result = ''
11
+ applications.each_with_index do |option_name, index|
12
+ next unless choices[index].present?
13
+ print(" #{option_name}")
14
+ msg = ''
15
+ result += "#{option_name},"
16
+ end
17
+ print "#{msg}\n"
18
+ result
19
+ end
20
+
21
+ def confirm_option_selected
22
+ print 'Enter a comma-separated list of option numbers or one single option number (again to uncheck, ENTER when done): '
23
+ $stdin.gets.squeeze(' ').strip
24
+ end
25
+
26
+ def print_menu_choices(msg, choices, applications)
27
+ while print_all_websites_available_options(applications, msg, choices) && (option = confirm_option_selected).present?
28
+ if /^[0-9,]+/.match(option)
29
+ handle_menu_option(msg, option, choices, applications)
30
+ else
31
+ msg = "Invalid option: #{option}\n "
32
+ next
33
+ end
34
+ end
35
+ end
36
+
37
+ def print_all_websites_available_options(applications, msg, choices)
38
+ puts 'Available options:'
39
+ applications.each_with_index do |option, index|
40
+ puts "#{(index + 1)} #{choices[index].present? ? "#{choices[index]}" : ''}) #{option} "
41
+ end
42
+ puts "\n#{msg}" if msg.present?
43
+ true
44
+ end
45
+
46
+ def handle_menu_option(msg, option, choices, applications)
47
+ arr_in = option.split(',')
48
+ arr_in.each_with_index do |number_option, _index|
49
+ num = number_option.to_i
50
+ if /^[0-9]+/.match(num.to_s) && ((num.to_i > 0 && num.to_i <= applications.size))
51
+ num -= 1
52
+ msg += "#{applications[num]} was #{choices[num].present? ? 'un' : '' }checked\n"
53
+ choices[num] = choices[num].blank? ? '+' : ' '
54
+ else
55
+ msg = "Invalid option: #{num}\n"
56
+ next
57
+ end
58
+ end
59
+ end
60
+ end
61
+ end
@@ -0,0 +1,16 @@
1
+ # Returns the version of the currently loaded gem as a <tt>Gem::Version</tt>
2
+ module CapistranoMulticonfigParallel
3
+ def self.gem_version
4
+ Gem::Version.new VERSION::STRING
5
+ end
6
+
7
+ # module used for generating the version
8
+ module VERSION
9
+ MAJOR = 0
10
+ MINOR = 0
11
+ TINY = 1
12
+ PRE = nil
13
+
14
+ STRING = [MAJOR, MINOR, TINY, PRE].compact.join('.')
15
+ end
16
+ end
@@ -0,0 +1,2 @@
1
+ require 'capistrano_multiconfig_parallel/version'
2
+ require 'capistrano_multiconfig_parallel/base'
@@ -0,0 +1,48 @@
1
+ # Configure Rails Envinronment
2
+ ENV['RAILS_ENV'] = 'test'
3
+
4
+ require 'simplecov'
5
+ require 'simplecov-summary'
6
+ require 'coveralls'
7
+
8
+ # require "codeclimate-test-reporter"
9
+ formatters = [SimpleCov::Formatter::HTMLFormatter]
10
+
11
+ formatters << Coveralls::SimpleCov::Formatter # if ENV['TRAVIS']
12
+ # formatters << CodeClimate::TestReporter::Formatter # if ENV['CODECLIMATE_REPO_TOKEN'] && ENV['TRAVIS']
13
+
14
+ SimpleCov.formatter = SimpleCov::Formatter::MultiFormatter[*formatters]
15
+
16
+ Coveralls.wear!
17
+ SimpleCov.start 'rails' do
18
+ add_filter 'spec'
19
+
20
+ at_exit {}
21
+ end
22
+
23
+ # CodeClimate::TestReporter.configure do |config|
24
+ # config.logger.level = Logger::WARN
25
+ # end
26
+ # CodeClimate::TestReporter.start
27
+
28
+ require 'bundler/setup'
29
+ require 'celluloid_pubsub'
30
+
31
+ require 'rspec/autorun'
32
+
33
+ RSpec.configure do |config|
34
+ require 'rspec/expectations'
35
+ config.include RSpec::Matchers
36
+
37
+ config.mock_with :mocha
38
+
39
+ config.after(:suite) do
40
+ if SimpleCov.running
41
+ silence_stream(STDOUT) do
42
+ SimpleCov::Formatter::HTMLFormatter.new.format(SimpleCov.result)
43
+ end
44
+
45
+ SimpleCov::Formatter::SummaryFormatter.new.format(SimpleCov.result)
46
+ end
47
+ end
48
+ end