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
data/lib/r10k/git/cache.rb
CHANGED
@@ -111,6 +111,6 @@ class R10K::Git::Cache
|
|
111
111
|
|
112
112
|
# Reformat the remote name into something that can be used as a directory
|
113
113
|
def sanitized_dirname
|
114
|
-
@sanitized_dirname ||= @remote.gsub(/[^@\w\.-]/, '-')
|
114
|
+
@sanitized_dirname ||= @remote.gsub(/(\w+:\/\/)(.*)(@)/, '\1').gsub(/[^@\w\.-]/, '-')
|
115
115
|
end
|
116
116
|
end
|
data/lib/r10k/initializers.rb
CHANGED
@@ -63,6 +63,13 @@ module R10K
|
|
63
63
|
def call
|
64
64
|
with_setting(:baseurl) { |value| PuppetForge.host = value }
|
65
65
|
with_setting(:proxy) { |value| PuppetForge::Connection.proxy = value }
|
66
|
+
with_setting(:authorization_token) { |value|
|
67
|
+
if @settings[:baseurl]
|
68
|
+
PuppetForge::Connection.authorization = value
|
69
|
+
else
|
70
|
+
raise R10K::Error, "Cannot specify a Forge authorization token without configuring a custom baseurl."
|
71
|
+
end
|
72
|
+
}
|
66
73
|
end
|
67
74
|
end
|
68
75
|
end
|
data/lib/r10k/module.rb
CHANGED
@@ -17,7 +17,7 @@ module R10K::Module
|
|
17
17
|
#
|
18
18
|
# @param [String] name The unique name of the module
|
19
19
|
# @param [String] basedir The root to install the module in
|
20
|
-
# @param [
|
20
|
+
# @param [Hash] args An arbitary Hash that specifies the implementation
|
21
21
|
# @param [R10K::Environment] environment Optional environment that this module is a part of
|
22
22
|
#
|
23
23
|
# @return [Object < R10K::Module] A member of the implementing subclass
|
data/lib/r10k/module/base.rb
CHANGED
@@ -51,7 +51,11 @@ class R10K::Module::Base
|
|
51
51
|
@owner, @name = parse_title(@title)
|
52
52
|
@path = Pathname.new(File.join(@dirname, @name))
|
53
53
|
@environment = environment
|
54
|
+
@overrides = args.delete(:overrides) || {}
|
54
55
|
@origin = 'external' # Expect Puppetfile or R10k::Environment to set this to a specific value
|
56
|
+
|
57
|
+
@requested_modules = @overrides.dig(:modules, :requested_modules) || []
|
58
|
+
@should_sync = (@requested_modules.empty? || @requested_modules.include?(@name))
|
55
59
|
end
|
56
60
|
|
57
61
|
# @deprecated
|
@@ -61,11 +65,22 @@ class R10K::Module::Base
|
|
61
65
|
end
|
62
66
|
|
63
67
|
# Synchronize this module with the indicated state.
|
64
|
-
# @
|
68
|
+
# @param [Hash] opts Deprecated
|
65
69
|
def sync(opts={})
|
66
70
|
raise NotImplementedError
|
67
71
|
end
|
68
72
|
|
73
|
+
def should_sync?
|
74
|
+
if @should_sync
|
75
|
+
logger.info _("Deploying module to %{path}") % {path: path}
|
76
|
+
true
|
77
|
+
else
|
78
|
+
logger.debug1(_("Only updating modules %{modules}, skipping module %{name}") % {modules: @requested_modules.inspect, name: name})
|
79
|
+
false
|
80
|
+
end
|
81
|
+
end
|
82
|
+
|
83
|
+
|
69
84
|
# Return the desired version of this module
|
70
85
|
# @abstract
|
71
86
|
def version
|
@@ -87,6 +102,7 @@ class R10K::Module::Base
|
|
87
102
|
raise NotImplementedError
|
88
103
|
end
|
89
104
|
|
105
|
+
# Deprecated
|
90
106
|
def accept(visitor)
|
91
107
|
visitor.visit(:module, self)
|
92
108
|
end
|
data/lib/r10k/module/forge.rb
CHANGED
@@ -13,7 +13,12 @@ class R10K::Module::Forge < R10K::Module::Base
|
|
13
13
|
R10K::Module.register(self)
|
14
14
|
|
15
15
|
def self.implement?(name, args)
|
16
|
-
|
16
|
+
args[:type].to_s == 'forge' ||
|
17
|
+
(!!
|
18
|
+
((args.keys & %i{git svn type}).empty? &&
|
19
|
+
args.has_key?(:version) &&
|
20
|
+
name.match(%r[\w+[/-]\w+]) &&
|
21
|
+
valid_version?(args[:version])))
|
17
22
|
end
|
18
23
|
|
19
24
|
def self.valid_version?(expected_version)
|
@@ -40,28 +45,29 @@ class R10K::Module::Forge < R10K::Module::Base
|
|
40
45
|
@metadata_file = R10K::Module::MetadataFile.new(path + 'metadata.json')
|
41
46
|
@metadata = @metadata_file.read
|
42
47
|
|
43
|
-
|
44
|
-
|
45
|
-
|
46
|
-
|
47
|
-
|
48
|
-
|
49
|
-
|
50
|
-
|
51
|
-
@expected_version = opts || current_version || :latest
|
52
|
-
end
|
48
|
+
setopts(opts, {
|
49
|
+
# Standard option interface
|
50
|
+
:version => :expected_version,
|
51
|
+
:source => ::R10K::Util::Setopts::Ignore,
|
52
|
+
:type => ::R10K::Util::Setopts::Ignore,
|
53
|
+
}, :raise_on_unhandled => false)
|
54
|
+
|
55
|
+
@expected_version ||= current_version || :latest
|
53
56
|
|
54
57
|
@v3_module = PuppetForge::V3::Module.new(:slug => @title)
|
55
58
|
end
|
56
59
|
|
60
|
+
# @param [Hash] opts Deprecated
|
57
61
|
def sync(opts={})
|
58
|
-
|
59
|
-
|
60
|
-
|
61
|
-
|
62
|
-
|
63
|
-
|
64
|
-
|
62
|
+
if should_sync?
|
63
|
+
case status
|
64
|
+
when :absent
|
65
|
+
install
|
66
|
+
when :outdated
|
67
|
+
upgrade
|
68
|
+
when :mismatched
|
69
|
+
reinstall
|
70
|
+
end
|
65
71
|
end
|
66
72
|
end
|
67
73
|
|
@@ -77,7 +83,11 @@ class R10K::Module::Forge < R10K::Module::Base
|
|
77
83
|
def expected_version
|
78
84
|
if @expected_version == :latest
|
79
85
|
begin
|
80
|
-
|
86
|
+
if @v3_module.current_release
|
87
|
+
@expected_version = @v3_module.current_release.version
|
88
|
+
else
|
89
|
+
raise PuppetForge::ReleaseNotFound, _("The module %{title} does not appear to have any published releases, cannot determine latest version.") % { title: @title }
|
90
|
+
end
|
81
91
|
rescue Faraday::ResourceNotFound => e
|
82
92
|
raise PuppetForge::ReleaseNotFound, _("The module %{title} does not exist on %{url}.") % {title: @title, url: PuppetForge::V3::Release.conn.url_prefix}, e.backtrace
|
83
93
|
end
|
data/lib/r10k/module/git.rb
CHANGED
@@ -8,7 +8,7 @@ class R10K::Module::Git < R10K::Module::Base
|
|
8
8
|
R10K::Module.register(self)
|
9
9
|
|
10
10
|
def self.implement?(name, args)
|
11
|
-
args.
|
11
|
+
args.has_key?(:git) || args[:type].to_s == 'git'
|
12
12
|
rescue
|
13
13
|
false
|
14
14
|
end
|
@@ -36,27 +36,35 @@ class R10K::Module::Git < R10K::Module::Base
|
|
36
36
|
include R10K::Util::Setopts
|
37
37
|
|
38
38
|
def initialize(title, dirname, opts, environment=nil)
|
39
|
+
|
39
40
|
super
|
40
41
|
setopts(opts, {
|
41
42
|
# Standard option interface
|
42
|
-
:version
|
43
|
-
:source
|
44
|
-
:type
|
43
|
+
:version => :desired_ref,
|
44
|
+
:source => :remote,
|
45
|
+
:type => ::R10K::Util::Setopts::Ignore,
|
45
46
|
|
46
47
|
# Type-specific options
|
47
|
-
:branch
|
48
|
-
:tag
|
49
|
-
:commit
|
50
|
-
:ref
|
51
|
-
:git
|
48
|
+
:branch => :desired_ref,
|
49
|
+
:tag => :desired_ref,
|
50
|
+
:commit => :desired_ref,
|
51
|
+
:ref => :desired_ref,
|
52
|
+
:git => :remote,
|
52
53
|
:default_branch => :default_ref,
|
53
54
|
:default_branch_override => :default_override_ref,
|
54
|
-
})
|
55
|
+
}, :raise_on_unhandled => false)
|
56
|
+
|
57
|
+
force = @overrides.dig(:modules, :force)
|
58
|
+
@force = force == false ? false : true
|
55
59
|
|
56
60
|
@desired_ref ||= 'master'
|
57
61
|
|
58
|
-
if @desired_ref == :control_branch
|
59
|
-
@
|
62
|
+
if @desired_ref == :control_branch
|
63
|
+
if @environment && @environment.respond_to?(:ref)
|
64
|
+
@desired_ref = @environment.ref
|
65
|
+
else
|
66
|
+
logger.warn _("Cannot track control repo branch for content '%{name}' when not part of a git-backed environment, will use default if available." % {name: name})
|
67
|
+
end
|
60
68
|
end
|
61
69
|
|
62
70
|
@repo = R10K::Git::StatefulRepository.new(@remote, @dirname, @name)
|
@@ -74,9 +82,10 @@ class R10K::Module::Git < R10K::Module::Base
|
|
74
82
|
}
|
75
83
|
end
|
76
84
|
|
85
|
+
# @param [Hash] opts Deprecated
|
77
86
|
def sync(opts={})
|
78
|
-
force = opts
|
79
|
-
@repo.sync(version, force)
|
87
|
+
force = opts[:force] || @force
|
88
|
+
@repo.sync(version, force) if should_sync?
|
80
89
|
end
|
81
90
|
|
82
91
|
def status
|
data/lib/r10k/module/local.rb
CHANGED
data/lib/r10k/module/svn.rb
CHANGED
@@ -7,7 +7,7 @@ class R10K::Module::SVN < R10K::Module::Base
|
|
7
7
|
R10K::Module.register(self)
|
8
8
|
|
9
9
|
def self.implement?(name, args)
|
10
|
-
args.
|
10
|
+
args.has_key?(:svn) || args[:type].to_s == 'svn'
|
11
11
|
end
|
12
12
|
|
13
13
|
# @!attribute [r] expected_revision
|
@@ -50,7 +50,7 @@ class R10K::Module::SVN < R10K::Module::Base
|
|
50
50
|
:revision => :expected_revision,
|
51
51
|
:username => :self,
|
52
52
|
:password => :self
|
53
|
-
})
|
53
|
+
}, :raise_on_unhandled => false)
|
54
54
|
|
55
55
|
@working_dir = R10K::SVN::WorkingDir.new(@path, :username => @username, :password => @password)
|
56
56
|
end
|
@@ -69,14 +69,17 @@ class R10K::Module::SVN < R10K::Module::Base
|
|
69
69
|
end
|
70
70
|
end
|
71
71
|
|
72
|
+
# @param [Hash] opts Deprecated
|
72
73
|
def sync(opts={})
|
73
|
-
|
74
|
-
|
75
|
-
|
76
|
-
|
77
|
-
|
78
|
-
|
79
|
-
|
74
|
+
if should_sync?
|
75
|
+
case status
|
76
|
+
when :absent
|
77
|
+
install
|
78
|
+
when :mismatched
|
79
|
+
reinstall
|
80
|
+
when :outdated
|
81
|
+
update
|
82
|
+
end
|
80
83
|
end
|
81
84
|
end
|
82
85
|
|
@@ -0,0 +1,195 @@
|
|
1
|
+
module R10K
|
2
|
+
module ModuleLoader
|
3
|
+
class Puppetfile
|
4
|
+
|
5
|
+
DEFAULT_MODULEDIR = 'modules'
|
6
|
+
DEFAULT_PUPPETFILE_NAME = 'Puppetfile'
|
7
|
+
DEFAULT_FORGE_API = 'forgeapi.puppetlabs.com'
|
8
|
+
|
9
|
+
attr_accessor :default_branch_override, :environment
|
10
|
+
attr_reader :modules, :moduledir,
|
11
|
+
:managed_directories, :desired_contents, :purge_exclusions
|
12
|
+
|
13
|
+
# @param basedir [String] The path that contains the moduledir &
|
14
|
+
# Puppetfile by default. May be an environment, project, or
|
15
|
+
# simple directory.
|
16
|
+
# @param puppetfile [String] The path to the Puppetfile, either an
|
17
|
+
# absolute full path or a relative path with regards to the basedir.
|
18
|
+
# @param moduledir [String] The path to the moduledir, either an
|
19
|
+
# absolute full path or a relative path with regards to the basedir.
|
20
|
+
# @param forge [String] The url (without protocol) to the Forge
|
21
|
+
# @param overrides [Hash] Configuration for loaded modules' behavior
|
22
|
+
# @param environment [R10K::Environment] When provided, the environment
|
23
|
+
# in which loading takes place
|
24
|
+
def initialize(basedir:,
|
25
|
+
moduledir: DEFAULT_MODULEDIR,
|
26
|
+
puppetfile: DEFAULT_PUPPETFILE_NAME,
|
27
|
+
forge: DEFAULT_FORGE_API,
|
28
|
+
overrides: {},
|
29
|
+
environment: nil)
|
30
|
+
|
31
|
+
@basedir = cleanpath(basedir)
|
32
|
+
@moduledir = resolve_path(@basedir, moduledir)
|
33
|
+
@puppetfile = resolve_path(@basedir, puppetfile)
|
34
|
+
@forge = forge
|
35
|
+
@overrides = overrides
|
36
|
+
@environment = environment
|
37
|
+
@default_branch_override = @overrides.dig(:environments, :default_branch_override)
|
38
|
+
|
39
|
+
@modules = []
|
40
|
+
|
41
|
+
@managed_directories = []
|
42
|
+
@desired_contents = []
|
43
|
+
@purge_exclusions = []
|
44
|
+
end
|
45
|
+
|
46
|
+
def load
|
47
|
+
dsl = R10K::ModuleLoader::Puppetfile::DSL.new(self)
|
48
|
+
dsl.instance_eval(puppetfile_content(@puppetfile), @puppetfile)
|
49
|
+
|
50
|
+
validate_no_duplicate_names(@modules)
|
51
|
+
@modules
|
52
|
+
|
53
|
+
managed_content = @modules.group_by(&:dirname)
|
54
|
+
|
55
|
+
@managed_directories = determine_managed_directories(managed_content)
|
56
|
+
@desired_contents = determine_desired_contents(managed_content)
|
57
|
+
@purge_exclusions = determine_purge_exclusions(@managed_directories)
|
58
|
+
|
59
|
+
{
|
60
|
+
modules: @modules,
|
61
|
+
managed_directories: @managed_directories,
|
62
|
+
desired_contents: @desired_contents,
|
63
|
+
purge_exclusions: @purge_exclusions
|
64
|
+
}
|
65
|
+
|
66
|
+
rescue SyntaxError, LoadError, ArgumentError, NameError => e
|
67
|
+
raise R10K::Error.wrap(e, _("Failed to evaluate %{path}") % {path: @puppetfile})
|
68
|
+
end
|
69
|
+
|
70
|
+
|
71
|
+
##
|
72
|
+
## set_forge, set_moduledir, and add_module are used directly by the DSL class
|
73
|
+
##
|
74
|
+
|
75
|
+
# @param [String] forge
|
76
|
+
def set_forge(forge)
|
77
|
+
@forge = forge
|
78
|
+
end
|
79
|
+
|
80
|
+
# @param [String] moduledir
|
81
|
+
def set_moduledir(moduledir)
|
82
|
+
@moduledir = resolve_path(@basedir, moduledir)
|
83
|
+
end
|
84
|
+
|
85
|
+
# @param [String] name
|
86
|
+
# @param [Hash, String, Symbol, nil] module_info Calling with
|
87
|
+
# anything but a Hash is deprecated. The DSL will now convert
|
88
|
+
# String and Symbol versions to Hashes of the shape
|
89
|
+
# { version: <String or Symbol> }
|
90
|
+
#
|
91
|
+
# String inputs should be valid module versions, the Symbol
|
92
|
+
# `:latest` is allowed, as well as `nil`.
|
93
|
+
#
|
94
|
+
# Non-Hash inputs are only ever used by Forge modules. In
|
95
|
+
# future versions this method will require the caller (the
|
96
|
+
# DSL class, not the Puppetfile author) to do this conversion
|
97
|
+
# itself.
|
98
|
+
#
|
99
|
+
def add_module(name, module_info)
|
100
|
+
if !module_info.is_a?(Hash)
|
101
|
+
module_info = { version: module_info }
|
102
|
+
end
|
103
|
+
|
104
|
+
module_info[:overrides] = @overrides
|
105
|
+
|
106
|
+
if install_path = module_info.delete(:install_path)
|
107
|
+
install_path = resolve_path(@basedir, install_path)
|
108
|
+
validate_install_path(install_path, name)
|
109
|
+
else
|
110
|
+
install_path = @moduledir
|
111
|
+
end
|
112
|
+
|
113
|
+
if @default_branch_override
|
114
|
+
module_info[:default_branch_override] = @default_branch_override
|
115
|
+
end
|
116
|
+
|
117
|
+
mod = R10K::Module.new(name, install_path, module_info, @environment)
|
118
|
+
mod.origin = :puppetfile
|
119
|
+
|
120
|
+
# Do not save modules if they would conflict with the attached
|
121
|
+
# environment
|
122
|
+
if @environment && @environment.module_conflicts?(mod)
|
123
|
+
return @modules
|
124
|
+
end
|
125
|
+
|
126
|
+
@modules << mod
|
127
|
+
end
|
128
|
+
|
129
|
+
private
|
130
|
+
|
131
|
+
# @param [Array<R10K::Module>] modules
|
132
|
+
def validate_no_duplicate_names(modules)
|
133
|
+
dupes = modules
|
134
|
+
.group_by { |mod| mod.name }
|
135
|
+
.select { |_, mods| mods.size > 1 }
|
136
|
+
.map(&:first)
|
137
|
+
unless dupes.empty?
|
138
|
+
msg = _('Puppetfiles cannot contain duplicate module names.')
|
139
|
+
msg += ' '
|
140
|
+
msg += _("Remove the duplicates of the following modules: %{dupes}" % { dupes: dupes.join(' ') })
|
141
|
+
raise R10K::Error.new(msg)
|
142
|
+
end
|
143
|
+
end
|
144
|
+
|
145
|
+
def resolve_path(base, path)
|
146
|
+
if Pathname.new(path).absolute?
|
147
|
+
cleanpath(path)
|
148
|
+
else
|
149
|
+
cleanpath(File.join(base, path))
|
150
|
+
end
|
151
|
+
end
|
152
|
+
|
153
|
+
def validate_install_path(path, modname)
|
154
|
+
unless /^#{Regexp.escape(@basedir)}.*/ =~ path
|
155
|
+
raise R10K::Error.new("Puppetfile cannot manage content '#{modname}' outside of containing environment: #{path} is not within #{@basedir}")
|
156
|
+
end
|
157
|
+
|
158
|
+
true
|
159
|
+
end
|
160
|
+
|
161
|
+
def determine_managed_directories(managed_content)
|
162
|
+
managed_content.keys.reject { |dir| dir == @basedir }
|
163
|
+
end
|
164
|
+
|
165
|
+
# Returns an array of the full paths to all the content being managed.
|
166
|
+
# @return [Array<String>]
|
167
|
+
def determine_desired_contents(managed_content)
|
168
|
+
managed_content.flat_map do |install_path, mods|
|
169
|
+
mods.collect { |mod| File.join(install_path, mod.name) }
|
170
|
+
end
|
171
|
+
end
|
172
|
+
|
173
|
+
def determine_purge_exclusions(managed_dirs)
|
174
|
+
if environment && environment.respond_to?(:desired_contents)
|
175
|
+
managed_dirs + environment.desired_contents
|
176
|
+
else
|
177
|
+
managed_dirs
|
178
|
+
end
|
179
|
+
end
|
180
|
+
|
181
|
+
# .cleanpath is as close to a canonical path as we can do without touching
|
182
|
+
# the filesystem. The .realpath methods will choke if some of the
|
183
|
+
# intermediate paths are missing, even though in some cases we will create
|
184
|
+
# them later as needed.
|
185
|
+
def cleanpath(path)
|
186
|
+
Pathname.new(path).cleanpath.to_s
|
187
|
+
end
|
188
|
+
|
189
|
+
# For testing purposes only
|
190
|
+
def puppetfile_content(path)
|
191
|
+
File.read(path)
|
192
|
+
end
|
193
|
+
end
|
194
|
+
end
|
195
|
+
end
|