minmb-capistrano 2.15.4

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 (119) hide show
  1. data/.gitignore +10 -0
  2. data/.travis.yml +7 -0
  3. data/CHANGELOG +1170 -0
  4. data/Gemfile +13 -0
  5. data/README.md +94 -0
  6. data/Rakefile +11 -0
  7. data/bin/cap +4 -0
  8. data/bin/capify +92 -0
  9. data/capistrano.gemspec +40 -0
  10. data/lib/capistrano.rb +5 -0
  11. data/lib/capistrano/callback.rb +45 -0
  12. data/lib/capistrano/cli.rb +47 -0
  13. data/lib/capistrano/cli/execute.rb +85 -0
  14. data/lib/capistrano/cli/help.rb +125 -0
  15. data/lib/capistrano/cli/help.txt +81 -0
  16. data/lib/capistrano/cli/options.rb +243 -0
  17. data/lib/capistrano/cli/ui.rb +40 -0
  18. data/lib/capistrano/command.rb +303 -0
  19. data/lib/capistrano/configuration.rb +57 -0
  20. data/lib/capistrano/configuration/actions/file_transfer.rb +50 -0
  21. data/lib/capistrano/configuration/actions/inspect.rb +46 -0
  22. data/lib/capistrano/configuration/actions/invocation.rb +329 -0
  23. data/lib/capistrano/configuration/alias_task.rb +26 -0
  24. data/lib/capistrano/configuration/callbacks.rb +147 -0
  25. data/lib/capistrano/configuration/connections.rb +237 -0
  26. data/lib/capistrano/configuration/execution.rb +142 -0
  27. data/lib/capistrano/configuration/loading.rb +205 -0
  28. data/lib/capistrano/configuration/log_formatters.rb +75 -0
  29. data/lib/capistrano/configuration/namespaces.rb +223 -0
  30. data/lib/capistrano/configuration/roles.rb +77 -0
  31. data/lib/capistrano/configuration/servers.rb +116 -0
  32. data/lib/capistrano/configuration/variables.rb +127 -0
  33. data/lib/capistrano/errors.rb +19 -0
  34. data/lib/capistrano/ext/multistage.rb +64 -0
  35. data/lib/capistrano/ext/string.rb +5 -0
  36. data/lib/capistrano/extensions.rb +57 -0
  37. data/lib/capistrano/fix_rake_deprecated_dsl.rb +8 -0
  38. data/lib/capistrano/logger.rb +166 -0
  39. data/lib/capistrano/processable.rb +57 -0
  40. data/lib/capistrano/recipes/compat.rb +32 -0
  41. data/lib/capistrano/recipes/deploy.rb +625 -0
  42. data/lib/capistrano/recipes/deploy/assets.rb +201 -0
  43. data/lib/capistrano/recipes/deploy/dependencies.rb +44 -0
  44. data/lib/capistrano/recipes/deploy/local_dependency.rb +54 -0
  45. data/lib/capistrano/recipes/deploy/remote_dependency.rb +117 -0
  46. data/lib/capistrano/recipes/deploy/scm.rb +19 -0
  47. data/lib/capistrano/recipes/deploy/scm/accurev.rb +169 -0
  48. data/lib/capistrano/recipes/deploy/scm/base.rb +200 -0
  49. data/lib/capistrano/recipes/deploy/scm/bzr.rb +86 -0
  50. data/lib/capistrano/recipes/deploy/scm/cvs.rb +153 -0
  51. data/lib/capistrano/recipes/deploy/scm/darcs.rb +96 -0
  52. data/lib/capistrano/recipes/deploy/scm/git.rb +293 -0
  53. data/lib/capistrano/recipes/deploy/scm/mercurial.rb +137 -0
  54. data/lib/capistrano/recipes/deploy/scm/none.rb +55 -0
  55. data/lib/capistrano/recipes/deploy/scm/perforce.rb +152 -0
  56. data/lib/capistrano/recipes/deploy/scm/subversion.rb +121 -0
  57. data/lib/capistrano/recipes/deploy/strategy.rb +19 -0
  58. data/lib/capistrano/recipes/deploy/strategy/base.rb +92 -0
  59. data/lib/capistrano/recipes/deploy/strategy/checkout.rb +20 -0
  60. data/lib/capistrano/recipes/deploy/strategy/copy.rb +338 -0
  61. data/lib/capistrano/recipes/deploy/strategy/export.rb +20 -0
  62. data/lib/capistrano/recipes/deploy/strategy/remote.rb +52 -0
  63. data/lib/capistrano/recipes/deploy/strategy/remote_cache.rb +57 -0
  64. data/lib/capistrano/recipes/deploy/strategy/unshared_remote_cache.rb +21 -0
  65. data/lib/capistrano/recipes/standard.rb +37 -0
  66. data/lib/capistrano/recipes/templates/maintenance.rhtml +53 -0
  67. data/lib/capistrano/role.rb +102 -0
  68. data/lib/capistrano/server_definition.rb +56 -0
  69. data/lib/capistrano/shell.rb +265 -0
  70. data/lib/capistrano/ssh.rb +95 -0
  71. data/lib/capistrano/task_definition.rb +77 -0
  72. data/lib/capistrano/transfer.rb +218 -0
  73. data/lib/capistrano/version.rb +11 -0
  74. data/test/cli/execute_test.rb +132 -0
  75. data/test/cli/help_test.rb +165 -0
  76. data/test/cli/options_test.rb +329 -0
  77. data/test/cli/ui_test.rb +28 -0
  78. data/test/cli_test.rb +17 -0
  79. data/test/command_test.rb +322 -0
  80. data/test/configuration/actions/file_transfer_test.rb +61 -0
  81. data/test/configuration/actions/inspect_test.rb +76 -0
  82. data/test/configuration/actions/invocation_test.rb +288 -0
  83. data/test/configuration/alias_task_test.rb +118 -0
  84. data/test/configuration/callbacks_test.rb +201 -0
  85. data/test/configuration/connections_test.rb +439 -0
  86. data/test/configuration/execution_test.rb +175 -0
  87. data/test/configuration/loading_test.rb +148 -0
  88. data/test/configuration/namespace_dsl_test.rb +332 -0
  89. data/test/configuration/roles_test.rb +157 -0
  90. data/test/configuration/servers_test.rb +183 -0
  91. data/test/configuration/variables_test.rb +190 -0
  92. data/test/configuration_test.rb +77 -0
  93. data/test/deploy/local_dependency_test.rb +76 -0
  94. data/test/deploy/remote_dependency_test.rb +146 -0
  95. data/test/deploy/scm/accurev_test.rb +23 -0
  96. data/test/deploy/scm/base_test.rb +55 -0
  97. data/test/deploy/scm/bzr_test.rb +51 -0
  98. data/test/deploy/scm/darcs_test.rb +37 -0
  99. data/test/deploy/scm/git_test.rb +221 -0
  100. data/test/deploy/scm/mercurial_test.rb +134 -0
  101. data/test/deploy/scm/none_test.rb +35 -0
  102. data/test/deploy/scm/perforce_test.rb +23 -0
  103. data/test/deploy/scm/subversion_test.rb +40 -0
  104. data/test/deploy/strategy/copy_test.rb +360 -0
  105. data/test/extensions_test.rb +69 -0
  106. data/test/fixtures/cli_integration.rb +5 -0
  107. data/test/fixtures/config.rb +5 -0
  108. data/test/fixtures/custom.rb +3 -0
  109. data/test/logger_formatting_test.rb +149 -0
  110. data/test/logger_test.rb +134 -0
  111. data/test/recipes_test.rb +25 -0
  112. data/test/role_test.rb +11 -0
  113. data/test/server_definition_test.rb +121 -0
  114. data/test/shell_test.rb +96 -0
  115. data/test/ssh_test.rb +113 -0
  116. data/test/task_definition_test.rb +117 -0
  117. data/test/transfer_test.rb +168 -0
  118. data/test/utils.rb +37 -0
  119. metadata +316 -0
@@ -0,0 +1,75 @@
1
+ # Add custom log formatters
2
+ #
3
+ # Passing a hash or a array of hashes with custom log formatters.
4
+ #
5
+ # Add the following to your deploy.rb or in your ~/.caprc
6
+ #
7
+ # == Example:
8
+ #
9
+ # capistrano_log_formatters = [
10
+ # { :match => /command finished/, :color => :hide, :priority => 10, :prepend => "$$$" },
11
+ # { :match => /executing command/, :color => :blue, :priority => 10, :style => :underscore, :timestamp => true },
12
+ # { :match => /^transaction: commit$/, :color => :magenta, :priority => 10, :style => :blink },
13
+ # { :match => /git/, :color => :white, :priority => 20, :style => :reverse }
14
+ # ]
15
+ #
16
+ # format_logs capistrano_log_formatters
17
+ #
18
+ # You can call format_logs multiple times, with either a hash or an array of hashes.
19
+ #
20
+ # == Colors:
21
+ #
22
+ # :color can have the following values:
23
+ #
24
+ # * :hide (hides the row completely)
25
+ # * :none
26
+ # * :black
27
+ # * :red
28
+ # * :green
29
+ # * :yellow
30
+ # * :blue
31
+ # * :magenta
32
+ # * :cyan
33
+ # * :white
34
+ #
35
+ # == Styles:
36
+ #
37
+ # :style can have the following values:
38
+ #
39
+ # * :bright
40
+ # * :dim
41
+ # * :underscore
42
+ # * :blink
43
+ # * :reverse
44
+ # * :hidden
45
+ #
46
+ #
47
+ # == Text alterations
48
+ #
49
+ # :prepend gives static text to be prepended to the output
50
+ # :replace replaces the matched text in the output
51
+ # :timestamp adds the current time before the output
52
+
53
+ module Capistrano
54
+ class Configuration
55
+ module LogFormatters
56
+ def log_formatter(options)
57
+ if options.class == Array
58
+ options.each do |option|
59
+ Capistrano::Logger.add_formatter(option)
60
+ end
61
+ else
62
+ Capistrano::Logger.add_formatter(options)
63
+ end
64
+ end
65
+
66
+ def default_log_formatters(formatters)
67
+ default_formatters = [*formatters]
68
+ end
69
+
70
+ def disable_log_formatters
71
+ @logger.disable_formatters = true
72
+ end
73
+ end
74
+ end
75
+ end
@@ -0,0 +1,223 @@
1
+ require 'capistrano/task_definition'
2
+
3
+ module Capistrano
4
+ class Configuration
5
+ module Namespaces
6
+ DEFAULT_TASK = :default
7
+
8
+ def self.included(base) #:nodoc:
9
+ base.send :alias_method, :initialize_without_namespaces, :initialize
10
+ base.send :alias_method, :initialize, :initialize_with_namespaces
11
+ end
12
+
13
+ # The name of this namespace. Defaults to +nil+ for the top-level
14
+ # namespace.
15
+ attr_reader :name
16
+
17
+ # The parent namespace of this namespace. Returns +nil+ for the top-level
18
+ # namespace.
19
+ attr_reader :parent
20
+
21
+ # The hash of tasks defined for this namespace.
22
+ attr_reader :tasks
23
+
24
+ # The hash of namespaces defined for this namespace.
25
+ attr_reader :namespaces
26
+
27
+ def initialize_with_namespaces(*args) #:nodoc:
28
+ @name = @parent = nil
29
+ initialize_without_namespaces(*args)
30
+ @tasks = {}
31
+ @namespaces = {}
32
+ end
33
+ private :initialize_with_namespaces
34
+
35
+ # Returns the top-level namespace (the one with no parent).
36
+ def top
37
+ return parent.top if parent
38
+ return self
39
+ end
40
+
41
+ # Returns the fully-qualified name of this namespace, or nil if the
42
+ # namespace is at the top-level.
43
+ def fully_qualified_name
44
+ return nil if name.nil?
45
+ [parent.fully_qualified_name, name].compact.join(":")
46
+ end
47
+
48
+ # Describe the next task to be defined. The given text will be attached to
49
+ # the next task that is defined and used as its description.
50
+ def desc(text)
51
+ @next_description = text
52
+ end
53
+
54
+ # Returns the value set by the last, pending "desc" call. If +reset+ is
55
+ # not false, the value will be reset immediately afterwards.
56
+ def next_description(reset=false)
57
+ @next_description
58
+ ensure
59
+ @next_description = nil if reset
60
+ end
61
+
62
+ # Open a namespace in which to define new tasks. If the namespace was
63
+ # defined previously, it will be reopened, otherwise a new namespace
64
+ # will be created for the given name.
65
+ def namespace(name, &block)
66
+ name = name.to_sym
67
+ raise ArgumentError, "expected a block" unless block_given?
68
+
69
+ namespace_already_defined = namespaces.key?(name)
70
+ if all_methods.any? { |m| m.to_sym == name } && !namespace_already_defined
71
+ thing = tasks.key?(name) ? "task" : "method"
72
+ raise ArgumentError, "defining a namespace named `#{name}' would shadow an existing #{thing} with that name"
73
+ end
74
+
75
+ namespaces[name] ||= Namespace.new(name, self)
76
+ namespaces[name].instance_eval(&block)
77
+
78
+ # make sure any open description gets terminated
79
+ namespaces[name].desc(nil)
80
+
81
+ if !namespace_already_defined
82
+ metaclass = class << self; self; end
83
+ metaclass.send(:define_method, name) { namespaces[name] }
84
+ end
85
+ end
86
+
87
+ # Describe a new task. If a description is active (see #desc), it is added
88
+ # to the options under the <tt>:desc</tt> key. The new task is added to
89
+ # the namespace.
90
+ def task(name, options={}, &block)
91
+ name = name.to_sym
92
+ raise ArgumentError, "expected a block" unless block_given?
93
+
94
+ task_already_defined = tasks.key?(name)
95
+ if all_methods.any? { |m| m.to_sym == name } && !task_already_defined
96
+ thing = namespaces.key?(name) ? "namespace" : "method"
97
+ raise ArgumentError, "defining a task named `#{name}' would shadow an existing #{thing} with that name"
98
+ end
99
+
100
+
101
+ task = TaskDefinition.new(name, self, {:desc => next_description(:reset)}.merge(options), &block)
102
+
103
+ define_task(task)
104
+ end
105
+
106
+ def define_task(task)
107
+ tasks[task.name] = task
108
+
109
+ metaclass = class << self; self; end
110
+ metaclass.send(:define_method, task.name) { execute_task(tasks[task.name]) }
111
+ end
112
+
113
+ # Find the task with the given name, where name is the fully-qualified
114
+ # name of the task. This will search into the namespaces and return
115
+ # the referenced task, or nil if no such task can be found. If the name
116
+ # refers to a namespace, the task in that namespace named "default"
117
+ # will be returned instead, if one exists.
118
+ def find_task(name)
119
+ parts = name.to_s.split(/:/)
120
+ tail = parts.pop.to_sym
121
+
122
+ ns = self
123
+ until parts.empty?
124
+ next_part = parts.shift
125
+ ns = next_part.empty? ? nil : ns.namespaces[next_part.to_sym]
126
+ return nil if ns.nil?
127
+ end
128
+
129
+ if ns.namespaces.key?(tail)
130
+ ns = ns.namespaces[tail]
131
+ tail = DEFAULT_TASK
132
+ end
133
+
134
+ ns.tasks[tail]
135
+ end
136
+
137
+ # Given a task name, this will search the current namespace, and all
138
+ # parent namespaces, looking for a task that matches the name, exactly.
139
+ # It returns the task, if found, or nil, if not.
140
+ def search_task(name)
141
+ name = name.to_sym
142
+ ns = self
143
+
144
+ until ns.nil?
145
+ return ns.tasks[name] if ns.tasks.key?(name)
146
+ ns = ns.parent
147
+ end
148
+
149
+ return nil
150
+ end
151
+
152
+ # Returns the default task for this namespace. This will be +nil+ if
153
+ # the namespace is at the top-level, and will otherwise return the
154
+ # task named "default". If no such task exists, +nil+ will be returned.
155
+ def default_task
156
+ return nil if parent.nil?
157
+ return tasks[DEFAULT_TASK]
158
+ end
159
+
160
+ # Returns the tasks in this namespace as an array of TaskDefinition
161
+ # objects. If a non-false parameter is given, all tasks in all
162
+ # namespaces under this namespace will be returned as well.
163
+ def task_list(all=false)
164
+ list = tasks.values
165
+ namespaces.each { |name,space| list.concat(space.task_list(:all)) } if all
166
+ list
167
+ end
168
+
169
+ private
170
+
171
+ def all_methods
172
+ public_methods.concat(protected_methods).concat(private_methods)
173
+ end
174
+
175
+ class Namespace
176
+ def initialize(name, parent)
177
+ @parent = parent
178
+ @name = name
179
+ end
180
+
181
+ def role(*args)
182
+ raise NotImplementedError, "roles cannot be defined in a namespace"
183
+ end
184
+
185
+ def respond_to?(sym, include_priv=false)
186
+ super || parent.respond_to?(sym, include_priv)
187
+ end
188
+
189
+ def method_missing(sym, *args, &block)
190
+ if parent.respond_to?(sym)
191
+ parent.send(sym, *args, &block)
192
+ else
193
+ super
194
+ end
195
+ end
196
+
197
+ include Capistrano::Configuration::AliasTask
198
+ include Capistrano::Configuration::Namespaces
199
+ undef :desc, :next_description
200
+ end
201
+ end
202
+ end
203
+ end
204
+
205
+ module Kernel
206
+ class << self
207
+ alias_method :method_added_without_capistrano, :method_added
208
+
209
+ # Detect method additions to Kernel and remove them in the Namespace class
210
+ def method_added(name)
211
+ result = method_added_without_capistrano(name)
212
+ return result if self != Kernel
213
+
214
+ namespace = Capistrano::Configuration::Namespaces::Namespace
215
+
216
+ if namespace.method_defined?(name) && namespace.instance_method(name).owner == Kernel
217
+ namespace.send :undef_method, name
218
+ end
219
+
220
+ result
221
+ end
222
+ end
223
+ end
@@ -0,0 +1,77 @@
1
+ require 'capistrano/server_definition'
2
+ require 'capistrano/role'
3
+
4
+ module Capistrano
5
+ class Configuration
6
+ module Roles
7
+ def self.included(base) #:nodoc:
8
+ base.send :alias_method, :initialize_without_roles, :initialize
9
+ base.send :alias_method, :initialize, :initialize_with_roles
10
+ end
11
+
12
+ # The hash of roles defined for this configuration. Each entry in the
13
+ # hash points to an array of server definitions that belong in that
14
+ # role.
15
+ attr_reader :roles
16
+
17
+ def initialize_with_roles(*args) #:nodoc:
18
+ initialize_without_roles(*args)
19
+ @roles = Hash.new { |h,k| h[k] = Role.new }
20
+ end
21
+
22
+ # Define a new role and its associated servers. You must specify at least
23
+ # one host for each role. Also, you can specify additional information
24
+ # (in the form of a Hash) which can be used to more uniquely specify the
25
+ # subset of servers specified by this specific role definition.
26
+ #
27
+ # Usage:
28
+ #
29
+ # role :db, "db1.example.com", "db2.example.com"
30
+ # role :db, "master.example.com", :primary => true
31
+ # role :app, "app1.example.com", "app2.example.com"
32
+ #
33
+ # You can also encode the username and port number for each host in the
34
+ # server string, if needed:
35
+ #
36
+ # role :web, "www@web1.example.com"
37
+ # role :file, "files.example.com:4144"
38
+ # role :db, "admin@db3.example.com:1234"
39
+ #
40
+ # Lastly, username and port number may be passed as options, if that is
41
+ # preferred; note that the options apply to all servers defined in
42
+ # that call to "role":
43
+ #
44
+ # role :web, "web2", "web3", :user => "www", :port => 2345
45
+ def role(which, *args, &block)
46
+ options = args.last.is_a?(Hash) ? args.pop : {}
47
+ which = which.to_sym
48
+
49
+ # The roles Hash is defined so that unrecognized keys always auto-initialize
50
+ # to a new Role instance (see the assignment in the initialize_with_roles method,
51
+ # above). However, we explicitly assign here so that role declarations will
52
+ # vivify the role object even if there are no server arguments. (Otherwise,
53
+ # role(:app) won't actually instantiate a Role object for :app.)
54
+ roles[which] ||= Role.new
55
+
56
+ roles[which].push(block, options) if block_given?
57
+ args.each { |host| roles[which] << ServerDefinition.new(host, options) }
58
+ end
59
+
60
+ # An alternative way to associate servers with roles. If you have a server
61
+ # that participates in multiple roles, this can be a DRYer way to describe
62
+ # the relationships. Pass the host definition as the first parameter, and
63
+ # the roles as the remaining parameters:
64
+ #
65
+ # server "master.example.com", :web, :app
66
+ def server(host, *roles)
67
+ options = roles.last.is_a?(Hash) ? roles.pop : {}
68
+ raise ArgumentError, "you must associate a server with at least one role" if roles.empty?
69
+ roles.each { |name| role(name, host, options) }
70
+ end
71
+
72
+ def role_names_for_host(host)
73
+ roles.map {|role_name, role| role_name if role.include?(host) }.compact || []
74
+ end
75
+ end
76
+ end
77
+ end
@@ -0,0 +1,116 @@
1
+ module Capistrano
2
+ class Configuration
3
+ module Servers
4
+ # Identifies all servers that the given task should be executed on.
5
+ # The options hash accepts the same arguments as #find_servers, and any
6
+ # preexisting options there will take precedence over the options in
7
+ # the task.
8
+ def find_servers_for_task(task, options={})
9
+ find_servers(task.options.merge(options))
10
+ end
11
+
12
+ # Attempts to find all defined servers that match the given criteria.
13
+ # The options hash may include a :hosts option (which should specify
14
+ # an array of host names or ServerDefinition instances), a :roles
15
+ # option (specifying an array of roles), an :only option (specifying
16
+ # a hash of key/value pairs that any matching server must match),
17
+ # an :exception option (like :only, but the inverse), and a
18
+ # :skip_hostfilter option to ignore the HOSTFILTER environment variable
19
+ # described below.
20
+ #
21
+ # Additionally, if the HOSTS environment variable is set, it will take
22
+ # precedence over any other options. Similarly, the ROLES environment
23
+ # variable will take precedence over other options. If both HOSTS and
24
+ # ROLES are given, HOSTS wins.
25
+ #
26
+ # Yet additionally, if the HOSTFILTER environment variable is set, it
27
+ # will limit the result to hosts found in that (comma-separated) list.
28
+ #
29
+ # If the HOSTROLEFILTER environment variable is set, it will limit the
30
+ # result to hosts found in that (comma-separated) list of roles
31
+ #
32
+ # Usage:
33
+ #
34
+ # # return all known servers
35
+ # servers = find_servers
36
+ #
37
+ # # find all servers in the app role that are not exempted from
38
+ # # deployment
39
+ # servers = find_servers :roles => :app,
40
+ # :except => { :no_release => true }
41
+ #
42
+ # # returns the given hosts, translated to ServerDefinition objects
43
+ # servers = find_servers :hosts => "jamis@example.host.com"
44
+ def find_servers(options={})
45
+ return [] if options.key?(:hosts) && (options[:hosts].nil? || [] == options[:hosts])
46
+ return [] if options.key?(:roles) && (options[:roles].nil? || [] == options[:roles])
47
+
48
+ hosts = server_list_from(ENV['HOSTS'] || options[:hosts])
49
+
50
+ if hosts.any?
51
+ if options[:skip_hostfilter]
52
+ hosts.uniq
53
+ else
54
+ filter_server_list(hosts.uniq)
55
+ end
56
+ else
57
+ roles = role_list_from(ENV['ROLES'] || options[:roles] || self.roles.keys)
58
+ roles = roles & Array(options[:roles]) if preserve_roles && !options[:roles].nil?
59
+
60
+ only = options[:only] || {}
61
+ except = options[:except] || {}
62
+
63
+ # If we don't have a def for a role it means its bogus, skip it so higher level can handle
64
+ servers = roles.inject([]) { |list, role| list.concat(self.roles[role] || []) }
65
+ servers = servers.select { |server| only.all? { |key,value| server.options[key] == value } }
66
+ servers = servers.reject { |server| except.any? { |key,value| server.options[key] == value } }
67
+
68
+ if options[:skip_hostfilter]
69
+ servers.uniq
70
+ else
71
+ filter_server_list(servers.uniq)
72
+ end
73
+ end
74
+ end
75
+
76
+ protected
77
+
78
+ def filter_server_list(servers)
79
+ return servers unless ENV['HOSTFILTER'] or ENV['HOSTROLEFILTER']
80
+ if ENV['HOSTFILTER']
81
+ filters = ENV['HOSTFILTER'].split(/,/)
82
+ servers.select { |server| filters.include?(server.host) }
83
+ elsif ENV['HOSTROLEFILTER']
84
+ filters = ENV['HOSTROLEFILTER'].split(/,/).map do |role|
85
+ local_roles = roles[role.to_sym]
86
+ if local_roles.is_a? Array
87
+ roles[role.to_sym]
88
+ else
89
+ roles[role.to_sym].servers
90
+ end
91
+ end.flatten
92
+ servers.select { |server| filters.include?(server) }
93
+ end
94
+ end
95
+
96
+ def server_list_from(hosts)
97
+ hosts = hosts.split(/,/) if String === hosts
98
+ hosts = build_list(hosts)
99
+ hosts.map { |s| String === s ? ServerDefinition.new(s.strip) : s }
100
+ end
101
+
102
+ def role_list_from(roles)
103
+ roles = roles.split(/,/) if String === roles
104
+ roles = build_list(roles)
105
+ roles.map do |role|
106
+ role = String === role ? role.strip.to_sym : role
107
+ role
108
+ end
109
+ end
110
+
111
+ def build_list(list)
112
+ Array(list).map { |item| item.respond_to?(:call) ? item.call : item }.flatten
113
+ end
114
+ end
115
+ end
116
+ end