r10k 3.9.0 → 3.10.0
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.
- checksums.yaml +4 -4
- data/.github/pull_request_template.md +1 -1
- data/.github/workflows/rspec_tests.yml +1 -1
- data/.github/workflows/stale.yml +19 -0
- data/CHANGELOG.mkd +24 -0
- data/doc/dynamic-environments/configuration.mkd +13 -6
- data/integration/Rakefile +1 -1
- data/integration/tests/user_scenario/basic_workflow/negative/neg_specify_deleted_forge_module.rb +3 -9
- data/integration/tests/user_scenario/basic_workflow/single_env_purge_unmanaged_modules.rb +8 -14
- data/lib/r10k/action/base.rb +10 -0
- data/lib/r10k/action/deploy/display.rb +42 -9
- data/lib/r10k/action/deploy/environment.rb +70 -41
- data/lib/r10k/action/deploy/module.rb +51 -29
- data/lib/r10k/action/puppetfile/check.rb +3 -1
- data/lib/r10k/action/puppetfile/install.rb +20 -23
- data/lib/r10k/action/puppetfile/purge.rb +8 -2
- data/lib/r10k/action/runner.rb +11 -6
- data/lib/r10k/content_synchronizer.rb +83 -0
- data/lib/r10k/deployment.rb +1 -1
- data/lib/r10k/environment/base.rb +21 -1
- data/lib/r10k/environment/git.rb +0 -3
- data/lib/r10k/environment/svn.rb +4 -6
- data/lib/r10k/environment/with_modules.rb +18 -10
- data/lib/r10k/git/cache.rb +1 -1
- data/lib/r10k/initializers.rb +7 -0
- data/lib/r10k/module.rb +1 -1
- data/lib/r10k/module/base.rb +17 -1
- data/lib/r10k/module/forge.rb +29 -19
- data/lib/r10k/module/git.rb +23 -14
- data/lib/r10k/module/local.rb +1 -0
- data/lib/r10k/module/svn.rb +12 -9
- data/lib/r10k/module_loader/puppetfile.rb +195 -0
- data/lib/r10k/module_loader/puppetfile/dsl.rb +37 -0
- data/lib/r10k/puppetfile.rb +111 -202
- data/lib/r10k/settings.rb +3 -0
- data/lib/r10k/source/base.rb +14 -0
- data/lib/r10k/source/git.rb +19 -6
- data/lib/r10k/source/hash.rb +1 -3
- data/lib/r10k/source/svn.rb +4 -2
- data/lib/r10k/util/cleaner.rb +21 -0
- data/lib/r10k/util/purgeable.rb +70 -8
- data/lib/r10k/version.rb +1 -1
- data/locales/r10k.pot +67 -71
- data/spec/fixtures/unit/action/r10k_forge_auth.yaml +4 -0
- data/spec/fixtures/unit/action/r10k_forge_auth_no_url.yaml +3 -0
- data/spec/fixtures/unit/util/purgeable/managed_one/managed_subdir_1/managed_subdir_2/ignored_1 +0 -0
- data/spec/fixtures/unit/util/purgeable/managed_two/.hidden/unmanaged_3 +0 -0
- data/spec/r10k-mocks/mock_source.rb +1 -1
- data/spec/shared-examples/puppetfile-action.rb +7 -7
- data/spec/unit/action/deploy/display_spec.rb +32 -6
- data/spec/unit/action/deploy/environment_spec.rb +85 -48
- data/spec/unit/action/deploy/module_spec.rb +163 -31
- data/spec/unit/action/puppetfile/check_spec.rb +2 -2
- data/spec/unit/action/puppetfile/install_spec.rb +31 -10
- data/spec/unit/action/puppetfile/purge_spec.rb +25 -5
- data/spec/unit/action/runner_spec.rb +49 -25
- data/spec/unit/git/cache_spec.rb +14 -0
- data/spec/unit/module/forge_spec.rb +23 -14
- data/spec/unit/module/git_spec.rb +8 -8
- data/spec/unit/module_loader/puppetfile_spec.rb +330 -0
- data/spec/unit/module_spec.rb +22 -5
- data/spec/unit/puppetfile_spec.rb +123 -203
- data/spec/unit/settings_spec.rb +6 -2
- data/spec/unit/util/purgeable_spec.rb +40 -14
- metadata +12 -2
@@ -10,25 +10,56 @@ module R10K
|
|
10
10
|
|
11
11
|
include R10K::Action::Deploy::DeployHelpers
|
12
12
|
|
13
|
+
# Deprecated
|
13
14
|
attr_reader :force
|
14
15
|
|
15
|
-
|
16
|
-
|
17
|
-
|
16
|
+
attr_reader :settings
|
17
|
+
|
18
|
+
# @param opts [Hash] A hash of options defined in #allowed_initialized_opts
|
19
|
+
# and managed by the SetOps mixin within the Action::Base class.
|
20
|
+
# Corresponds to the CLI flags and options.
|
21
|
+
# @param argv [Enumerable] Typically CRI::ArgumentList or Array. A list-like
|
22
|
+
# collection of the remaining arguments to the CLI invocation (after
|
23
|
+
# removing flags and options).
|
24
|
+
# @param settings [Hash] A hash of configuration loaded from the relevant
|
25
|
+
# config (r10k.yaml).
|
26
|
+
#
|
27
|
+
# @note All arguments will be required in the next major version
|
28
|
+
def initialize(opts, argv, settings = {})
|
18
29
|
super
|
19
30
|
|
20
|
-
|
21
|
-
|
31
|
+
requested_env = @opts[:environment] ? [@opts[:environment].gsub(/\W/, '_')] : []
|
32
|
+
|
33
|
+
@settings = @settings.merge({
|
34
|
+
overrides: {
|
35
|
+
environments: {
|
36
|
+
requested_environments: requested_env,
|
37
|
+
generate_types: @generate_types
|
38
|
+
},
|
39
|
+
modules: {
|
40
|
+
requested_modules: @argv.map.to_a,
|
41
|
+
# force here is used to make it easier to reason about
|
42
|
+
force: !@no_force
|
43
|
+
},
|
44
|
+
purging: {},
|
45
|
+
output: {}
|
46
|
+
}
|
47
|
+
})
|
22
48
|
end
|
23
49
|
|
24
50
|
def call
|
25
51
|
@visit_ok = true
|
52
|
+
begin
|
53
|
+
expect_config!
|
54
|
+
deployment = R10K::Deployment.new(@settings)
|
55
|
+
check_write_lock!(@settings)
|
56
|
+
|
57
|
+
deployment.accept(self)
|
58
|
+
rescue => e
|
59
|
+
@visit_ok = false
|
60
|
+
logger.error R10K::Errors::Formatting.format_exception(e, @trace)
|
61
|
+
end
|
26
62
|
|
27
|
-
expect_config!
|
28
|
-
deployment = R10K::Deployment.new(@settings)
|
29
|
-
check_write_lock!(@settings)
|
30
|
-
|
31
|
-
deployment.accept(self)
|
32
63
|
@visit_ok
|
33
64
|
end
|
34
65
|
|
@@ -45,29 +76,20 @@ module R10K
|
|
45
76
|
end
|
46
77
|
|
47
78
|
def visit_environment(environment)
|
48
|
-
|
49
|
-
|
79
|
+
requested_envs = @settings.dig(:overrides, :environments, :requested_environments)
|
80
|
+
if !requested_envs.empty? && !requested_envs.include?(environment.dirname)
|
81
|
+
logger.debug1(_("Only updating modules in environment(s) %{opt_env} skipping environment %{env_path}") % {opt_env: requested_envs.inspect, env_path: environment.path})
|
50
82
|
else
|
51
|
-
logger.debug1(_("Updating modules %{modules} in environment %{env_path}") % {modules: @
|
52
|
-
yield
|
53
|
-
end
|
54
|
-
end
|
83
|
+
logger.debug1(_("Updating modules %{modules} in environment %{env_path}") % {modules: @settings.dig(:overrides, :modules, :requested_modules).inspect, env_path: environment.path})
|
55
84
|
|
56
|
-
|
57
|
-
puppetfile.load
|
58
|
-
yield
|
59
|
-
end
|
85
|
+
environment.deploy
|
60
86
|
|
61
|
-
|
62
|
-
|
63
|
-
|
64
|
-
|
65
|
-
|
66
|
-
logger.debug("Generating puppet types for environment '#{mod.environment.dirname}'...")
|
67
|
-
mod.environment.generate_types!
|
87
|
+
requested_mods = @settings.dig(:overrides, :modules, :requested_modules) || []
|
88
|
+
generate_types = @settings.dig(:overrides, :environments, :generate_types)
|
89
|
+
if generate_types && !((environment.modules.map(&:name) & requested_mods).empty?)
|
90
|
+
logger.debug("Generating puppet types for environment '#{environment.dirname}'...")
|
91
|
+
environment.generate_types!
|
68
92
|
end
|
69
|
-
else
|
70
|
-
logger.debug1(_("Only updating modules %{modules}, skipping module %{mod_name}") % {modules: @argv.inspect, mod_name: mod.name})
|
71
93
|
end
|
72
94
|
end
|
73
95
|
|
@@ -8,7 +8,9 @@ module R10K
|
|
8
8
|
class Check < R10K::Action::Base
|
9
9
|
|
10
10
|
def call
|
11
|
-
pf = R10K::Puppetfile.new(@root,
|
11
|
+
pf = R10K::Puppetfile.new(@root,
|
12
|
+
{moduledir: @moduledir,
|
13
|
+
puppetfile_path: @puppetfile})
|
12
14
|
begin
|
13
15
|
pf.load!
|
14
16
|
$stderr.puts _("Syntax OK")
|
@@ -2,6 +2,7 @@ require 'r10k/puppetfile'
|
|
2
2
|
require 'r10k/errors/formatting'
|
3
3
|
require 'r10k/action/visitor'
|
4
4
|
require 'r10k/action/base'
|
5
|
+
require 'r10k/util/cleaner'
|
5
6
|
|
6
7
|
module R10K
|
7
8
|
module Action
|
@@ -9,33 +10,29 @@ module R10K
|
|
9
10
|
class Install < R10K::Action::Base
|
10
11
|
|
11
12
|
def call
|
12
|
-
@
|
13
|
-
|
14
|
-
|
15
|
-
|
16
|
-
|
17
|
-
|
18
|
-
|
19
|
-
|
20
|
-
|
21
|
-
|
22
|
-
|
23
|
-
|
24
|
-
|
25
|
-
|
26
|
-
|
27
|
-
|
28
|
-
def visit_module(mod)
|
29
|
-
@force ||= false
|
30
|
-
logger.info _("Updating module %{mod_path}") % {mod_path: mod.path}
|
31
|
-
|
32
|
-
if mod.respond_to?(:desired_ref) && mod.desired_ref == :control_branch
|
33
|
-
logger.warn _("Cannot track control repo branch for content '%{name}' when not part of a 'deploy' action, will use default if available." % {name: mod.name})
|
13
|
+
@ok = true
|
14
|
+
begin
|
15
|
+
pf = R10K::Puppetfile.new(@root,
|
16
|
+
{moduledir: @moduledir,
|
17
|
+
puppetfile_path: @puppetfile,
|
18
|
+
force: @force || false})
|
19
|
+
pf.load!
|
20
|
+
pf.sync
|
21
|
+
|
22
|
+
R10K::Util::Cleaner.new(pf.managed_directories,
|
23
|
+
pf.desired_contents,
|
24
|
+
pf.purge_exclusions).purge!
|
25
|
+
|
26
|
+
rescue => e
|
27
|
+
@ok = false
|
28
|
+
logger.error R10K::Errors::Formatting.format_exception(e, @trace)
|
34
29
|
end
|
35
30
|
|
36
|
-
|
31
|
+
@ok
|
37
32
|
end
|
38
33
|
|
34
|
+
private
|
35
|
+
|
39
36
|
def allowed_initialize_opts
|
40
37
|
super.merge(root: :self, puppetfile: :self, moduledir: :self, force: :self )
|
41
38
|
end
|
@@ -1,4 +1,5 @@
|
|
1
1
|
require 'r10k/puppetfile'
|
2
|
+
require 'r10k/util/cleaner'
|
2
3
|
require 'r10k/action/base'
|
3
4
|
require 'r10k/errors/formatting'
|
4
5
|
|
@@ -8,9 +9,14 @@ module R10K
|
|
8
9
|
class Purge < R10K::Action::Base
|
9
10
|
|
10
11
|
def call
|
11
|
-
pf = R10K::Puppetfile.new(@root,
|
12
|
+
pf = R10K::Puppetfile.new(@root,
|
13
|
+
{moduledir: @moduledir,
|
14
|
+
puppetfile_path: @puppetfile})
|
12
15
|
pf.load!
|
13
|
-
pf.
|
16
|
+
R10K::Util::Cleaner.new(pf.managed_directories,
|
17
|
+
pf.desired_contents,
|
18
|
+
pf.purge_exclusions).purge!
|
19
|
+
|
14
20
|
true
|
15
21
|
rescue => e
|
16
22
|
logger.error R10K::Errors::Formatting.format_exception(e, @trace)
|
data/lib/r10k/action/runner.rb
CHANGED
@@ -67,15 +67,20 @@ module R10K
|
|
67
67
|
exit(8)
|
68
68
|
end
|
69
69
|
|
70
|
+
# Set up authorization from license file if it wasn't
|
71
|
+
# already set via the config
|
70
72
|
def setup_authorization
|
71
|
-
|
72
|
-
|
73
|
+
if PuppetForge::Connection.authorization.nil?
|
74
|
+
begin
|
75
|
+
license = R10K::Util::License.load
|
73
76
|
|
74
|
-
|
75
|
-
|
77
|
+
if license.respond_to?(:authorization_token)
|
78
|
+
logger.debug "Using token from license to connect to the Forge."
|
79
|
+
PuppetForge::Connection.authorization = license.authorization_token
|
80
|
+
end
|
81
|
+
rescue R10K::Error => e
|
82
|
+
logger.warn e.message
|
76
83
|
end
|
77
|
-
rescue R10K::Error => e
|
78
|
-
logger.warn e.message
|
79
84
|
end
|
80
85
|
end
|
81
86
|
|
@@ -0,0 +1,83 @@
|
|
1
|
+
module R10K
|
2
|
+
module ContentSynchronizer
|
3
|
+
|
4
|
+
def self.serial_accept(modules, visitor, loader)
|
5
|
+
visitor.visit(:puppetfile, loader) do
|
6
|
+
serial_sync(modules)
|
7
|
+
end
|
8
|
+
end
|
9
|
+
|
10
|
+
def self.serial_sync(modules)
|
11
|
+
modules.each do |mod|
|
12
|
+
mod.sync
|
13
|
+
end
|
14
|
+
end
|
15
|
+
|
16
|
+
def self.concurrent_accept(modules, visitor, loader, pool_size, logger)
|
17
|
+
mods_queue = modules_visit_queue(modules, visitor, loader)
|
18
|
+
sync_queue(mods_queue, pool_size, logger)
|
19
|
+
end
|
20
|
+
|
21
|
+
def self.concurrent_sync(modules, pool_size, logger)
|
22
|
+
mods_queue = modules_sync_queue(modules)
|
23
|
+
sync_queue(mods_queue, pool_size, logger)
|
24
|
+
end
|
25
|
+
|
26
|
+
def self.sync_queue(mods_queue, pool_size, logger)
|
27
|
+
logger.debug _("Updating modules with %{pool_size} threads") % {pool_size: pool_size}
|
28
|
+
thread_pool = pool_size.times.map { sync_thread(mods_queue, logger) }
|
29
|
+
thread_exception = nil
|
30
|
+
|
31
|
+
# If any threads raise an exception the deployment is considered a failure.
|
32
|
+
# In that event clear the queue, wait for other threads to finish their
|
33
|
+
# current work, then re-raise the first exception caught.
|
34
|
+
begin
|
35
|
+
thread_pool.each(&:join)
|
36
|
+
rescue => e
|
37
|
+
logger.error _("Error during concurrent deploy of a module: %{message}") % {message: e.message}
|
38
|
+
mods_queue.clear
|
39
|
+
thread_exception ||= e
|
40
|
+
retry
|
41
|
+
ensure
|
42
|
+
raise thread_exception unless thread_exception.nil?
|
43
|
+
end
|
44
|
+
end
|
45
|
+
|
46
|
+
def self.modules_visit_queue(modules, visitor, loader)
|
47
|
+
Queue.new.tap do |queue|
|
48
|
+
visitor.visit(:puppetfile, loader) do
|
49
|
+
enqueue_modules(queue, modules)
|
50
|
+
end
|
51
|
+
end
|
52
|
+
end
|
53
|
+
|
54
|
+
def self.modules_sync_queue(modules)
|
55
|
+
Queue.new.tap do |queue|
|
56
|
+
enqueue_modules(queue, modules)
|
57
|
+
end
|
58
|
+
end
|
59
|
+
|
60
|
+
def self.enqueue_modules(queue, modules)
|
61
|
+
modules_by_cachedir = modules.group_by { |mod| mod.cachedir }
|
62
|
+
modules_without_vcs_cachedir = modules_by_cachedir.delete(:none) || []
|
63
|
+
|
64
|
+
modules_without_vcs_cachedir.each {|mod| queue << Array(mod) }
|
65
|
+
modules_by_cachedir.values.each {|mods| queue << mods }
|
66
|
+
end
|
67
|
+
|
68
|
+
def self.sync_thread(mods_queue, logger)
|
69
|
+
Thread.new do
|
70
|
+
begin
|
71
|
+
while mods = mods_queue.pop(true) do
|
72
|
+
mods.each { |mod| mod.sync }
|
73
|
+
end
|
74
|
+
rescue ThreadError => e
|
75
|
+
logger.debug _("Module thread %{id} exiting: %{message}") % {message: e.message, id: Thread.current.object_id}
|
76
|
+
Thread.exit
|
77
|
+
rescue => e
|
78
|
+
Thread.main.raise(e)
|
79
|
+
end
|
80
|
+
end
|
81
|
+
end
|
82
|
+
end
|
83
|
+
end
|
data/lib/r10k/deployment.rb
CHANGED
@@ -118,7 +118,7 @@ module R10K
|
|
118
118
|
raise R10K::Error, _("Unable to load sources; the supplied configuration does not define the 'sources' key")
|
119
119
|
end
|
120
120
|
@_sources = sources.map do |(name, hash)|
|
121
|
-
R10K::Source.from_hash(name, hash)
|
121
|
+
R10K::Source.from_hash(name, hash.merge({overrides: @config[:overrides]}))
|
122
122
|
end
|
123
123
|
end
|
124
124
|
|
@@ -1,10 +1,13 @@
|
|
1
1
|
require 'r10k/util/subprocess'
|
2
|
+
require 'r10k/logging'
|
2
3
|
|
3
4
|
# This class defines a common interface for environment implementations.
|
4
5
|
#
|
5
6
|
# @since 1.3.0
|
6
7
|
class R10K::Environment::Base
|
7
8
|
|
9
|
+
include R10K::Logging
|
10
|
+
|
8
11
|
# @!attribute [r] name
|
9
12
|
# @return [String] A name for this environment that is unique to the given source
|
10
13
|
attr_reader :name
|
@@ -44,11 +47,15 @@ class R10K::Environment::Base
|
|
44
47
|
@dirname = dirname
|
45
48
|
@options = options
|
46
49
|
@puppetfile_name = options.delete(:puppetfile_name)
|
50
|
+
@overrides = options.delete(:overrides) || {}
|
47
51
|
|
48
52
|
@full_path = File.join(@basedir, @dirname)
|
49
53
|
@path = Pathname.new(File.join(@basedir, @dirname))
|
50
54
|
|
51
|
-
@puppetfile = R10K::Puppetfile.new(@full_path,
|
55
|
+
@puppetfile = R10K::Puppetfile.new(@full_path,
|
56
|
+
{overrides: @overrides,
|
57
|
+
force: @overrides.dig(:modules, :force),
|
58
|
+
puppetfile_name: @puppetfile_name})
|
52
59
|
@puppetfile.environment = self
|
53
60
|
end
|
54
61
|
|
@@ -116,6 +123,19 @@ class R10K::Environment::Base
|
|
116
123
|
end
|
117
124
|
end
|
118
125
|
|
126
|
+
def deploy
|
127
|
+
puppetfile.load(@overrides.dig(:environments, :default_branch_override))
|
128
|
+
|
129
|
+
puppetfile.sync
|
130
|
+
|
131
|
+
if (@overrides.dig(:purging, :purge_levels) || []).include?(:puppetfile)
|
132
|
+
logger.debug("Purging unmanaged Puppetfile content for environment '#{dirname}'...")
|
133
|
+
R10K::Util::Cleaner.new(puppetfile.managed_directories,
|
134
|
+
puppetfile.desired_contents,
|
135
|
+
puppetfile.purge_exclusions).purge!
|
136
|
+
end
|
137
|
+
end
|
138
|
+
|
119
139
|
def whitelist(user_whitelist=[])
|
120
140
|
user_whitelist.collect { |pattern| File.join(@full_path, pattern) }
|
121
141
|
end
|
data/lib/r10k/environment/git.rb
CHANGED
@@ -1,4 +1,3 @@
|
|
1
|
-
require 'r10k/logging'
|
2
1
|
require 'r10k/puppetfile'
|
3
2
|
require 'r10k/git/stateful_repository'
|
4
3
|
require 'forwardable'
|
@@ -8,8 +7,6 @@ require 'forwardable'
|
|
8
7
|
# @since 1.3.0
|
9
8
|
class R10K::Environment::Git < R10K::Environment::WithModules
|
10
9
|
|
11
|
-
include R10K::Logging
|
12
|
-
|
13
10
|
R10K::Environment.register(:git, self)
|
14
11
|
# Register git as the default environment type
|
15
12
|
R10K::Environment.register(nil, self)
|
data/lib/r10k/environment/svn.rb
CHANGED
@@ -7,8 +7,6 @@ require 'r10k/util/setopts'
|
|
7
7
|
# @since 1.3.0
|
8
8
|
class R10K::Environment::SVN < R10K::Environment::Base
|
9
9
|
|
10
|
-
include R10K::Logging
|
11
|
-
|
12
10
|
R10K::Environment.register(:svn, self)
|
13
11
|
|
14
12
|
# @!attribute [r] remote
|
@@ -46,12 +44,12 @@ class R10K::Environment::SVN < R10K::Environment::Base
|
|
46
44
|
super
|
47
45
|
setopts(options, {
|
48
46
|
# Standard option interface
|
49
|
-
:source
|
50
|
-
:version
|
51
|
-
:type
|
47
|
+
:source => :remote,
|
48
|
+
:version => :expected_revision,
|
49
|
+
:type => ::R10K::Util::Setopts::Ignore,
|
52
50
|
|
53
51
|
# Type-specific options
|
54
|
-
:remote
|
52
|
+
:remote => :self,
|
55
53
|
:username => :self,
|
56
54
|
:password => :self,
|
57
55
|
})
|
@@ -1,4 +1,3 @@
|
|
1
|
-
require 'r10k/logging'
|
2
1
|
require 'r10k/util/purgeable'
|
3
2
|
|
4
3
|
# This abstract base class implements an environment that can include module
|
@@ -7,8 +6,6 @@ require 'r10k/util/purgeable'
|
|
7
6
|
# @since 3.4.0
|
8
7
|
class R10K::Environment::WithModules < R10K::Environment::Base
|
9
8
|
|
10
|
-
include R10K::Logging
|
11
|
-
|
12
9
|
# @!attribute [r] moduledir
|
13
10
|
# @return [String] The directory to install environment-defined modules
|
14
11
|
# into (default: #{basedir}/modules)
|
@@ -78,28 +75,39 @@ class R10K::Environment::WithModules < R10K::Environment::Base
|
|
78
75
|
def accept(visitor)
|
79
76
|
visitor.visit(:environment, self) do
|
80
77
|
@modules.each do |mod|
|
81
|
-
mod.
|
78
|
+
mod.sync
|
82
79
|
end
|
83
80
|
|
84
81
|
puppetfile.accept(visitor)
|
85
82
|
end
|
86
83
|
end
|
87
84
|
|
85
|
+
def deploy
|
86
|
+
@modules.each do |mod|
|
87
|
+
mod.sync
|
88
|
+
end
|
89
|
+
|
90
|
+
super
|
91
|
+
end
|
92
|
+
|
88
93
|
def load_modules(module_hash)
|
89
94
|
module_hash.each do |name, args|
|
95
|
+
if !args.is_a?(Hash)
|
96
|
+
args = { version: args }
|
97
|
+
end
|
98
|
+
|
90
99
|
add_module(name, args)
|
91
100
|
end
|
92
101
|
end
|
93
102
|
|
94
103
|
# @param [String] name
|
95
|
-
# @param [
|
104
|
+
# @param [Hash] args
|
96
105
|
def add_module(name, args)
|
97
|
-
|
98
|
-
|
99
|
-
|
100
|
-
end
|
106
|
+
# symbolize keys in the args hash
|
107
|
+
args = args.inject({}) { |memo,(k,v)| memo[k.to_sym] = v; memo }
|
108
|
+
args[:overrides] = @overrides
|
101
109
|
|
102
|
-
if
|
110
|
+
if install_path = args.delete(:install_path)
|
103
111
|
install_path = resolve_install_path(install_path)
|
104
112
|
validate_install_path(install_path, name)
|
105
113
|
else
|