r10k 3.9.0 → 3.10.0
Sign up to get free protection for your applications and to get access to all the features.
- 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
|