capistrano_multiconfig_parallel 0.0.1
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +7 -0
- data/.coveralls.yml +2 -0
- data/.gitignore +21 -0
- data/.rspec +1 -0
- data/.rubocop.yml +68 -0
- data/.travis.yml +12 -0
- data/CONTRIBUTING.md +44 -0
- data/Gemfile +3 -0
- data/Guardfile +12 -0
- data/LICENSE +20 -0
- data/README.md +220 -0
- data/Rakefile +56 -0
- data/bin/multi_cap +7 -0
- data/capistrano_multiconfig_parallel.gemspec +51 -0
- data/img/parallel_demo.png +0 -0
- data/init.rb +1 -0
- data/lib/capistrano_multiconfig_parallel/application.rb +57 -0
- data/lib/capistrano_multiconfig_parallel/base.rb +92 -0
- data/lib/capistrano_multiconfig_parallel/celluloid/celluloid_manager.rb +178 -0
- data/lib/capistrano_multiconfig_parallel/celluloid/celluloid_worker.rb +238 -0
- data/lib/capistrano_multiconfig_parallel/celluloid/child_process.rb +104 -0
- data/lib/capistrano_multiconfig_parallel/celluloid/rake_worker.rb +83 -0
- data/lib/capistrano_multiconfig_parallel/celluloid/state_machine.rb +49 -0
- data/lib/capistrano_multiconfig_parallel/celluloid/terminal_table.rb +122 -0
- data/lib/capistrano_multiconfig_parallel/cli.rb +55 -0
- data/lib/capistrano_multiconfig_parallel/configuration.rb +70 -0
- data/lib/capistrano_multiconfig_parallel/helpers/base_manager.rb +217 -0
- data/lib/capistrano_multiconfig_parallel/helpers/multi_app_manager.rb +84 -0
- data/lib/capistrano_multiconfig_parallel/helpers/single_app_manager.rb +48 -0
- data/lib/capistrano_multiconfig_parallel/helpers/standard_deploy.rb +40 -0
- data/lib/capistrano_multiconfig_parallel/initializers/conf.rb +6 -0
- data/lib/capistrano_multiconfig_parallel/initializers/confirm_question.rb +25 -0
- data/lib/capistrano_multiconfig_parallel/initializers/i18n.rb +10 -0
- data/lib/capistrano_multiconfig_parallel/initializers/rake.rb +28 -0
- data/lib/capistrano_multiconfig_parallel/multi_app_helpers/dependency_tracker.rb +111 -0
- data/lib/capistrano_multiconfig_parallel/multi_app_helpers/interactive_menu.rb +61 -0
- data/lib/capistrano_multiconfig_parallel/version.rb +16 -0
- data/lib/capistrano_multiconfig_parallel.rb +2 -0
- data/spec/spec_helper.rb +48 -0
- 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,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
|
data/spec/spec_helper.rb
ADDED
@@ -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
|