pdk 0.0.1 → 0.1.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/LICENSE +201 -0
- data/README.md +270 -0
- data/exe/pdk +5 -0
- data/lib/pdk.rb +8 -0
- data/lib/pdk/cli.rb +80 -0
- data/lib/pdk/cli/errors.rb +12 -0
- data/lib/pdk/cli/exec.rb +51 -0
- data/lib/pdk/cli/input.rb +28 -0
- data/lib/pdk/cli/new.rb +30 -0
- data/lib/pdk/cli/new/class.rb +45 -0
- data/lib/pdk/cli/new/module.rb +55 -0
- data/lib/pdk/cli/test.rb +23 -0
- data/lib/pdk/cli/tests/unit.rb +52 -0
- data/lib/pdk/cli/util/option_normalizer.rb +44 -0
- data/lib/pdk/cli/util/option_validator.rb +74 -0
- data/lib/pdk/cli/validate.rb +81 -0
- data/lib/pdk/generate.rb +3 -0
- data/lib/pdk/generators/module.rb +139 -0
- data/lib/pdk/generators/puppet_class.rb +51 -0
- data/lib/pdk/generators/puppet_object.rb +213 -0
- data/lib/pdk/i18n.rb +4 -0
- data/lib/pdk/logger.rb +25 -0
- data/lib/pdk/module/metadata.rb +88 -0
- data/lib/pdk/module/templatedir.rb +231 -0
- data/lib/pdk/report.rb +38 -0
- data/lib/pdk/template_file.rb +87 -0
- data/lib/pdk/tests/unit.rb +21 -0
- data/lib/pdk/util.rb +16 -0
- data/lib/pdk/validate.rb +12 -0
- data/lib/pdk/validators/base_validator.rb +14 -0
- data/lib/pdk/validators/metadata.rb +17 -0
- data/lib/pdk/validators/puppet_lint.rb +17 -0
- data/lib/pdk/validators/puppet_parser.rb +17 -0
- data/lib/pdk/validators/ruby_lint.rb +17 -0
- data/lib/pdk/version.rb +3 -0
- data/locales/config.yaml +21 -0
- data/locales/de/pdk.po +250 -0
- data/locales/pdk.pot +215 -0
- metadata +92 -12
@@ -0,0 +1,74 @@
|
|
1
|
+
module PDK
|
2
|
+
module CLI
|
3
|
+
module Util
|
4
|
+
class OptionValidator
|
5
|
+
def self.is_comma_separated_list?(list, options = {})
|
6
|
+
list =~ /^[\w\-]+(?:,[\w\-]+)+$/ ? true : false
|
7
|
+
end
|
8
|
+
|
9
|
+
def self.enum(val, valid_entries, options = {})
|
10
|
+
vals = val.is_a?(Array) ? val : [val]
|
11
|
+
invalid_entries = vals.find_all { |e| !valid_entries.include?(e) }
|
12
|
+
|
13
|
+
unless invalid_entries.empty?
|
14
|
+
raise _("Error: the following values are invalid: %{invalid_entries}") % {invalid_entries: invalid_entries}
|
15
|
+
end
|
16
|
+
|
17
|
+
val
|
18
|
+
end
|
19
|
+
|
20
|
+
# Validate the module name against the regular expression in the
|
21
|
+
# documentation: https://docs.puppet.com/puppet/4.10/modules_fundamentals.html#allowed-module-names
|
22
|
+
def self.is_valid_module_name?(string)
|
23
|
+
!(string =~ /\A[a-z][a-z0-9_]*\Z/).nil?
|
24
|
+
end
|
25
|
+
|
26
|
+
# Validate a Puppet namespace against the regular expression in the
|
27
|
+
# documentation: https://docs.puppet.com/puppet/4.10/lang_reserved.html#classes-and-defined-resource-types
|
28
|
+
def self.is_valid_namespace?(string)
|
29
|
+
return false if (string || '').split('::').last == 'init'
|
30
|
+
|
31
|
+
!(string =~ /\A([a-z][a-z0-9_]*)(::[a-z][a-z0-9_]*)*\Z/).nil?
|
32
|
+
end
|
33
|
+
|
34
|
+
singleton_class.send(:alias_method, :is_valid_class_name?, :is_valid_namespace?)
|
35
|
+
singleton_class.send(:alias_method, :is_valid_defined_type_name?, :is_valid_namespace?)
|
36
|
+
|
37
|
+
# Validate that a class/defined type parameter matches the regular
|
38
|
+
# expression in the documentation: https://docs.puppet.com/puppet/4.10/lang_reserved.html#parameters
|
39
|
+
# The parameter should also not be a reserved word or overload
|
40
|
+
# a metaparameter.
|
41
|
+
def self.is_valid_param_name?(string)
|
42
|
+
reserved_words = %w{trusted facts server_facts title name}.freeze
|
43
|
+
metaparams = %w{alias audit before loglevel noop notify require schedule stage subscribe tag}.freeze
|
44
|
+
return false if reserved_words.include?(string) || metaparams.include?(string)
|
45
|
+
|
46
|
+
!(string =~ /\A[a-z][a-zA-Z0-9_]*\Z/).nil?
|
47
|
+
end
|
48
|
+
|
49
|
+
# Naive validation of a data type declaration. Extracts all the bare
|
50
|
+
# words and compares them against a list of known data types.
|
51
|
+
#
|
52
|
+
# @todo This prevents the use of dynamic data types like TypeReferences
|
53
|
+
# but that shouldn't be a problem for the current feature set. This
|
54
|
+
# should be replaced eventually by something better (or just call
|
55
|
+
# Puppet::Pops::Types::TypesParser)
|
56
|
+
def self.is_valid_data_type?(string)
|
57
|
+
valid_types = %w{
|
58
|
+
String Integer Float Numeric Boolean Array Hash Regexp Undef
|
59
|
+
Default Class Resource Scalar Collection Variant Data Pattern Enum
|
60
|
+
Tuple Struct Optional Catalogentry Type Any Callable NotUndef
|
61
|
+
}.freeze
|
62
|
+
|
63
|
+
string.scan(/\b(([a-zA-Z]+)(,|\[|\]|\Z))/) do |result|
|
64
|
+
type = result[1]
|
65
|
+
|
66
|
+
return false unless valid_types.include?(type)
|
67
|
+
end
|
68
|
+
|
69
|
+
true
|
70
|
+
end
|
71
|
+
end
|
72
|
+
end
|
73
|
+
end
|
74
|
+
end
|
@@ -0,0 +1,81 @@
|
|
1
|
+
require 'cri'
|
2
|
+
require 'pdk/cli/util/option_validator'
|
3
|
+
require 'pdk/report'
|
4
|
+
|
5
|
+
require 'pdk/validate'
|
6
|
+
|
7
|
+
module PDK
|
8
|
+
module CLI
|
9
|
+
class Validate
|
10
|
+
include PDK::CLI::Util
|
11
|
+
|
12
|
+
def self.command
|
13
|
+
@validate ||= Cri::Command.define do
|
14
|
+
name 'validate'
|
15
|
+
usage _("validate [options]")
|
16
|
+
summary _("Run static analysis tests.")
|
17
|
+
description _("Run metadata-json-lint, puppet parser validate, puppet-lint, or rubocop.")
|
18
|
+
|
19
|
+
flag nil, :list, _("list all available validators")
|
20
|
+
|
21
|
+
run do |opts, args, cmd|
|
22
|
+
validator_names = PDK::Validate.validators.map { |v| v.name }
|
23
|
+
validators = PDK::Validate.validators
|
24
|
+
targets = []
|
25
|
+
reports = nil
|
26
|
+
|
27
|
+
if opts[:list]
|
28
|
+
puts _("Available validators: %{validator_names}") % {validator_names: validator_names.join(', ')}
|
29
|
+
exit 0
|
30
|
+
end
|
31
|
+
|
32
|
+
if args[0]
|
33
|
+
# This may be a single validator, a list of validators, or a target.
|
34
|
+
if OptionValidator.is_comma_separated_list?(args[0])
|
35
|
+
# This is a comma separated list. Treat each item as a validator.
|
36
|
+
|
37
|
+
vals = OptionNormalizer.comma_separated_list_to_array(args[0])
|
38
|
+
validators = PDK::Validate.validators.find_all { |v| vals.include?(v.name) }
|
39
|
+
|
40
|
+
invalid = vals.find_all { |v| !validator_names.include?(v) }
|
41
|
+
invalid.each do |v|
|
42
|
+
PDK.logger.warn(_("Unknown validator '%{v}'. Available validators: %{validators}") % {v: v, validators: validator_names.join(', ')})
|
43
|
+
end
|
44
|
+
else
|
45
|
+
# This is a single item. Check if it's a known validator, or otherwise treat it as a target.
|
46
|
+
val = PDK::Validate.validators.find { |v| args[0] == v.name }
|
47
|
+
if val
|
48
|
+
validators = [val]
|
49
|
+
else
|
50
|
+
targets = [args[0]]
|
51
|
+
# We now know that no validators were passed, so let the user know we're using all of them by default.
|
52
|
+
PDK.logger.info(_("Running all available validators..."))
|
53
|
+
end
|
54
|
+
end
|
55
|
+
else
|
56
|
+
PDK.logger.info(_("Running all available validators..."))
|
57
|
+
end
|
58
|
+
|
59
|
+
# Subsequent arguments are targets.
|
60
|
+
targets.concat(args[1..-1]) if args.length > 1
|
61
|
+
|
62
|
+
# Note: Reporting may be delegated to the validation tool itself.
|
63
|
+
if opts[:format]
|
64
|
+
reports = OptionNormalizer.report_formats(opts.fetch(:format))
|
65
|
+
end
|
66
|
+
|
67
|
+
options = targets.empty? ? {} : { :targets => targets }
|
68
|
+
validators.each do |validator|
|
69
|
+
result = validator.invoke(options)
|
70
|
+
if reports
|
71
|
+
reports.each do |r|
|
72
|
+
r.write(result)
|
73
|
+
end
|
74
|
+
end
|
75
|
+
end
|
76
|
+
end
|
77
|
+
end
|
78
|
+
end
|
79
|
+
end
|
80
|
+
end
|
81
|
+
end
|
data/lib/pdk/generate.rb
ADDED
@@ -0,0 +1,139 @@
|
|
1
|
+
require 'etc'
|
2
|
+
require 'pathname'
|
3
|
+
require 'fileutils'
|
4
|
+
|
5
|
+
require 'pdk'
|
6
|
+
require 'pdk/logger'
|
7
|
+
require 'pdk/module/metadata'
|
8
|
+
require 'pdk/module/templatedir'
|
9
|
+
require 'pdk/cli/exec'
|
10
|
+
require 'pdk/cli/input'
|
11
|
+
require 'pdk/util'
|
12
|
+
|
13
|
+
module PDK
|
14
|
+
module Generate
|
15
|
+
class Module
|
16
|
+
DEFAULT_TEMPLATE = 'https://github.com/puppetlabs/pdk-module-template'
|
17
|
+
|
18
|
+
def self.invoke(opts={})
|
19
|
+
defaults = {
|
20
|
+
'version' => '0.1.0',
|
21
|
+
'dependencies' => [
|
22
|
+
{ 'name' => 'puppetlabs-stdlib', 'version_requirement' => '>= 1.0.0' }
|
23
|
+
]
|
24
|
+
}
|
25
|
+
|
26
|
+
defaults['license'] = opts[:license] if opts.has_key? :license
|
27
|
+
target_dir = File.expand_path(opts[:target_dir])
|
28
|
+
|
29
|
+
if File.exists?(target_dir)
|
30
|
+
raise PDK::CLI::FatalError, _("The destination directory '%{dir}' already exists") % {:dir => target_dir}
|
31
|
+
end
|
32
|
+
|
33
|
+
metadata = PDK::Module::Metadata.new(defaults)
|
34
|
+
|
35
|
+
module_interview(metadata, opts) unless opts[:'skip-interview'] # @todo Build way to get info by answers file
|
36
|
+
|
37
|
+
temp_target_dir = PDK::Util.make_tmpdir_name('pdk-module-target')
|
38
|
+
|
39
|
+
prepare_module_directory(temp_target_dir)
|
40
|
+
|
41
|
+
template_url = opts.fetch(:'template-url', DEFAULT_TEMPLATE)
|
42
|
+
|
43
|
+
PDK::Module::TemplateDir.new(template_url) do |templates|
|
44
|
+
templates.render do |file_path, file_content|
|
45
|
+
file = Pathname.new(temp_target_dir) + file_path
|
46
|
+
file.dirname.mkpath
|
47
|
+
file.write(file_content)
|
48
|
+
end
|
49
|
+
|
50
|
+
# Add information about the template used to generate the module to the
|
51
|
+
# metadata (for a future update command).
|
52
|
+
metadata.update!(templates.metadata)
|
53
|
+
|
54
|
+
File.open(File.join(temp_target_dir, 'metadata.json'), 'w') do |metadata_file|
|
55
|
+
metadata_file.puts metadata.to_json
|
56
|
+
end
|
57
|
+
end
|
58
|
+
|
59
|
+
FileUtils.mv(temp_target_dir, target_dir)
|
60
|
+
end
|
61
|
+
|
62
|
+
def self.prepare_module_directory(target_dir)
|
63
|
+
[
|
64
|
+
File.join(target_dir, 'manifests'),
|
65
|
+
File.join(target_dir, 'templates'),
|
66
|
+
].each do |dir|
|
67
|
+
begin
|
68
|
+
FileUtils.mkdir_p(dir)
|
69
|
+
rescue SystemCallError
|
70
|
+
raise PDK::CLI::FatalError, _("Unable to create directory '%{dir}'") % {:dir => dir}
|
71
|
+
end
|
72
|
+
end
|
73
|
+
end
|
74
|
+
|
75
|
+
def self.module_interview(metadata, opts={})
|
76
|
+
puts _(
|
77
|
+
"We need to create a metadata.json file for this module. Please answer the " +
|
78
|
+
"following questions; if the question is not applicable to this module, feel free " +
|
79
|
+
"to leave it blank."
|
80
|
+
)
|
81
|
+
|
82
|
+
begin
|
83
|
+
puts ""
|
84
|
+
forge_user = PDK::CLI::Input.get(_("What is your Puppet Forge username?"), Etc.getlogin)
|
85
|
+
metadata.update!('name' => "#{forge_user}-#{opts[:name]}")
|
86
|
+
rescue StandardError => e
|
87
|
+
PDK.logger.error(_("We're sorry, we could not parse your module name: %{message}") % {:message => e.message})
|
88
|
+
retry
|
89
|
+
end
|
90
|
+
|
91
|
+
begin
|
92
|
+
puts "\n" + _("Puppet uses Semantic Versioning (semver.org) to version modules.")
|
93
|
+
module_version = PDK::CLI::Input.get(_("What version is this module?"), metadata.data['version'])
|
94
|
+
metadata.update!('version' => module_version)
|
95
|
+
rescue StandardError => e
|
96
|
+
PDK.logger.error(_("We're sorry, we could not parse that as a Semantic Version: %{message}") % {message: e.message})
|
97
|
+
retry
|
98
|
+
end
|
99
|
+
|
100
|
+
puts ""
|
101
|
+
module_author = PDK::CLI::Input.get(_("Who wrote this module?"), metadata.data['author'])
|
102
|
+
metadata.update!('author' => module_author)
|
103
|
+
|
104
|
+
unless opts.has_key?(:license)
|
105
|
+
puts ""
|
106
|
+
module_license = PDK::CLI::Input.get(_("What license does this module code fall under?"), metadata.data['license'])
|
107
|
+
metadata.update!('license' => module_license)
|
108
|
+
end
|
109
|
+
|
110
|
+
puts ""
|
111
|
+
module_summary = PDK::CLI::Input.get(_("How would you describe this module in a single sentence?"))
|
112
|
+
metadata.update!('summary' => module_summary)
|
113
|
+
|
114
|
+
puts ""
|
115
|
+
module_source = PDK::CLI::Input.get(_("Where is this module's source code repository?"))
|
116
|
+
metadata.update!('source' => module_source)
|
117
|
+
|
118
|
+
puts ""
|
119
|
+
module_page = PDK::CLI::Input.get(_("Where can others go to learn more about this module?"), metadata.data['project_page'])
|
120
|
+
metadata.update!('project_page' => module_page)
|
121
|
+
|
122
|
+
puts ""
|
123
|
+
module_issues = PDK::CLI::Input.get(_("Where can others go to file issues about this module?"), metadata.data['issues_url'])
|
124
|
+
metadata.update!('issues_url' => module_issues)
|
125
|
+
|
126
|
+
puts
|
127
|
+
puts '-' * 40
|
128
|
+
puts metadata.to_json
|
129
|
+
puts '-' * 40
|
130
|
+
puts
|
131
|
+
|
132
|
+
if PDK::CLI::Input.get(_("About to generate this module; continue?"), 'Y') !~ /^y(es)?$/i
|
133
|
+
puts _("Aborting...")
|
134
|
+
exit 0
|
135
|
+
end
|
136
|
+
end
|
137
|
+
end
|
138
|
+
end
|
139
|
+
end
|
@@ -0,0 +1,51 @@
|
|
1
|
+
require 'pdk/generators/puppet_object'
|
2
|
+
|
3
|
+
module PDK
|
4
|
+
module Generate
|
5
|
+
class PuppetClass < PuppetObject
|
6
|
+
OBJECT_TYPE = :class
|
7
|
+
|
8
|
+
# Prepares the data needed to render the new Puppet class template.
|
9
|
+
#
|
10
|
+
# @return [Hash{Symbol => Object}] a hash of information that will be
|
11
|
+
# provided to the class and class spec templates during rendering.
|
12
|
+
def template_data
|
13
|
+
data = {name: object_name}
|
14
|
+
if @options.key?(:params)
|
15
|
+
data[:params] = @options[:params]
|
16
|
+
data[:max_type_length] = @options[:params].map { |r| r[:type].length }.max
|
17
|
+
end
|
18
|
+
data
|
19
|
+
end
|
20
|
+
|
21
|
+
# Calculates the path to the .pp file that the new class will be written
|
22
|
+
# to.
|
23
|
+
#
|
24
|
+
# @return [String] the path where the new class will be written.
|
25
|
+
def target_object_path
|
26
|
+
@target_pp_path ||= begin
|
27
|
+
class_name_parts = object_name.split('::')[1..-1]
|
28
|
+
class_name_parts << 'init' if class_name_parts.empty?
|
29
|
+
|
30
|
+
"#{File.join(module_dir, 'manifests', *class_name_parts)}.pp"
|
31
|
+
end
|
32
|
+
end
|
33
|
+
|
34
|
+
# Calculates the path to the file that the tests for the new class will
|
35
|
+
# be written to.
|
36
|
+
#
|
37
|
+
# @return [String] the path where the tests for the new class will be
|
38
|
+
# written.
|
39
|
+
def target_spec_path
|
40
|
+
@target_spec_path ||= begin
|
41
|
+
class_name_parts = object_name.split('::')
|
42
|
+
|
43
|
+
# drop the module name if the object name contains multiple parts
|
44
|
+
class_name_parts.delete_at(0) if class_name_parts.length > 1
|
45
|
+
|
46
|
+
"#{File.join(module_dir, 'spec', 'classes', *class_name_parts)}_spec.rb"
|
47
|
+
end
|
48
|
+
end
|
49
|
+
end
|
50
|
+
end
|
51
|
+
end
|
@@ -0,0 +1,213 @@
|
|
1
|
+
require 'fileutils'
|
2
|
+
|
3
|
+
require 'pdk'
|
4
|
+
require 'pdk/logger'
|
5
|
+
require 'pdk/module/metadata'
|
6
|
+
require 'pdk/module/templatedir'
|
7
|
+
require 'pdk/template_file'
|
8
|
+
|
9
|
+
module PDK
|
10
|
+
module Generate
|
11
|
+
class PuppetObject
|
12
|
+
attr_reader :module_dir
|
13
|
+
attr_reader :object_name
|
14
|
+
|
15
|
+
# Initialises the PDK::Generate::PuppetObject object.
|
16
|
+
#
|
17
|
+
# In general, this object should never be instantiated directly. Instead,
|
18
|
+
# one of the subclasses should be used e.g. PDK::Generate::Klass.
|
19
|
+
#
|
20
|
+
# New subclasses generally only need to inherit this class, set the
|
21
|
+
# OBJECT_TYPE constant and implement the {#template_data},
|
22
|
+
# {#target_object_path} and {#target_spec_path} methods.
|
23
|
+
#
|
24
|
+
# @param module_dir [String] The path to the module directory that the
|
25
|
+
# will contain the object.
|
26
|
+
# @param object_name [String] The name of the object.
|
27
|
+
# @param options [Hash{Symbol => Object}]
|
28
|
+
#
|
29
|
+
# @api public
|
30
|
+
def initialize(module_dir, object_name, options = {})
|
31
|
+
@module_dir = module_dir
|
32
|
+
@options = options
|
33
|
+
|
34
|
+
if [:class, :defined_type].include?(object_type)
|
35
|
+
object_name_parts = object_name.split('::')
|
36
|
+
|
37
|
+
if object_name_parts.first == module_name
|
38
|
+
@object_name = object_name
|
39
|
+
else
|
40
|
+
@object_name = [module_name, object_name].join('::')
|
41
|
+
end
|
42
|
+
end
|
43
|
+
end
|
44
|
+
|
45
|
+
# @abstract Subclass and implement {#template_data} to provide data to
|
46
|
+
# the templates during rendering. Implementations of this method should
|
47
|
+
# return a Hash[{Symbol => Object}].
|
48
|
+
def template_data
|
49
|
+
raise NotImplementedError
|
50
|
+
end
|
51
|
+
|
52
|
+
# @abstract Subclass and implement {#target_object_path}. Implementations
|
53
|
+
# of this method should return a String containing the destination path
|
54
|
+
# of the object being generated.
|
55
|
+
def target_object_path
|
56
|
+
raise NotImplementedError
|
57
|
+
end
|
58
|
+
|
59
|
+
# @abstract Subclass and implement {#target_spec_path}. Implementations
|
60
|
+
# of this method should return a String containing the destination path
|
61
|
+
# of the tests for the object being generated.
|
62
|
+
def target_spec_path
|
63
|
+
raise NotImplementedError
|
64
|
+
end
|
65
|
+
|
66
|
+
# Retrieves the type of the object being generated, e.g. :class,
|
67
|
+
# :defined_type, etc. This is specified in the subclass' OBJECT_TYPE
|
68
|
+
# constant.
|
69
|
+
#
|
70
|
+
# @return [Symbol] the type of the object being generated.
|
71
|
+
#
|
72
|
+
# @api private
|
73
|
+
def object_type
|
74
|
+
self.class::OBJECT_TYPE
|
75
|
+
end
|
76
|
+
|
77
|
+
# Check that the target files do not exist, find an appropriate template
|
78
|
+
# and create the target files from the template. This is the main entry
|
79
|
+
# point for the class.
|
80
|
+
#
|
81
|
+
# @raise [PDK::CLI::FatalError] if the target files already exist.
|
82
|
+
#
|
83
|
+
# @api public
|
84
|
+
def run
|
85
|
+
[target_object_path, target_spec_path].each do |target_file|
|
86
|
+
if File.exist?(target_file)
|
87
|
+
raise PDK::CLI::FatalError, _("Unable to generate class, '%{file}' already exists.") % {file: target_file}
|
88
|
+
end
|
89
|
+
end
|
90
|
+
|
91
|
+
with_templates do |template_path, config_hash|
|
92
|
+
data = template_data.merge(:configs => config_hash)
|
93
|
+
|
94
|
+
render_file(target_object_path, template_path[:object], data)
|
95
|
+
render_file(target_spec_path, template_path[:spec], data) if template_path[:spec]
|
96
|
+
end
|
97
|
+
end
|
98
|
+
|
99
|
+
# Render a file using the provided template and write it to disk.
|
100
|
+
#
|
101
|
+
# @param dest_path [String] The path that the rendered file should be
|
102
|
+
# written to. Any necessary directories will be automatically created.
|
103
|
+
# @param template_path [String] The path on disk to the file containing
|
104
|
+
# the template.
|
105
|
+
# @param data [Hash{Object => Object}] The data to be provided to the
|
106
|
+
# template when rendering.
|
107
|
+
#
|
108
|
+
# @return [void]
|
109
|
+
#
|
110
|
+
# @api private
|
111
|
+
def render_file(dest_path, template_path, data)
|
112
|
+
PDK.logger.info(_("Creating %{file} from template.") % {file: dest_path})
|
113
|
+
file_content = PDK::TemplateFile.new(template_path, data).render
|
114
|
+
FileUtils.mkdir_p(File.dirname(dest_path))
|
115
|
+
File.open(dest_path, 'w') { |f| f.write file_content }
|
116
|
+
end
|
117
|
+
|
118
|
+
# Search the possible template directories in order of preference to find
|
119
|
+
# a template that can be used to render a new object of the specified
|
120
|
+
# type.
|
121
|
+
#
|
122
|
+
# @yieldparam template_paths [Hash{Symbol => String}] :object contains
|
123
|
+
# the path on disk to the template file for the object, :spec contains
|
124
|
+
# the path on disk to the template file for the tests for the object
|
125
|
+
# (if it exists).
|
126
|
+
# @yieldparam config_hash [Hash{Object => Object}] the contents of the
|
127
|
+
# :global key in the config_defaults.yml file.
|
128
|
+
#
|
129
|
+
# @raise [PDK::CLI::FatalError] if no suitable template could be found.
|
130
|
+
#
|
131
|
+
# @api private
|
132
|
+
def with_templates
|
133
|
+
templates.each do |template|
|
134
|
+
if template[:url].nil?
|
135
|
+
PDK.logger.debug(_("No %{dir_type} template specified; trying next template directory.") % {dir_type: template[:type]})
|
136
|
+
next
|
137
|
+
end
|
138
|
+
|
139
|
+
PDK::Module::TemplateDir.new(template[:url]) do |template_dir|
|
140
|
+
template_paths = template_dir.object_template_for(object_type)
|
141
|
+
|
142
|
+
if template_paths
|
143
|
+
config_hash = template_dir.object_config
|
144
|
+
yield template_paths, config_hash
|
145
|
+
return
|
146
|
+
else
|
147
|
+
if template[:allow_fallback]
|
148
|
+
PDK.logger.debug(_("Unable to find a %{type} template in %{url}, trying next template directory") % {type: object_type, url: template[:url]})
|
149
|
+
else
|
150
|
+
raise PDK::CLI::FatalError, _("Unable to find the %{type} template in %{url}.") % {type: object_type, url: template[:url]}
|
151
|
+
end
|
152
|
+
end
|
153
|
+
end
|
154
|
+
end
|
155
|
+
end
|
156
|
+
|
157
|
+
# Provides the possible template directory locations in the order in
|
158
|
+
# which they should be searched for a valid template.
|
159
|
+
#
|
160
|
+
# If a template-url has been specified on in the options hash (e.g. from
|
161
|
+
# a CLI parameter), then this template directory will be checked first
|
162
|
+
# and we do not fall back to the next possible template directory.
|
163
|
+
#
|
164
|
+
# If we have not been provided a specific template directory to use, we
|
165
|
+
# try the template specified in the module metadata (as set during
|
166
|
+
# PDK::Generate::Module) and fall back to the default template if
|
167
|
+
# necessary.
|
168
|
+
#
|
169
|
+
# @return [Array<Hash{Symbol => Object}>] an array of hashes. Each hash
|
170
|
+
# contains 3 keys: :type contains a String that describes the template
|
171
|
+
# directory, :url contains a String with the URL to the template
|
172
|
+
# directory, and :allow_fallback contains a Boolean that specifies if
|
173
|
+
# the lookup process should proceed to the next template directory if
|
174
|
+
# the template file is not in this template directory.
|
175
|
+
#
|
176
|
+
# @api private
|
177
|
+
def templates
|
178
|
+
@templates ||= [
|
179
|
+
{type: 'CLI', url: @options[:'template-url'], allow_fallback: false},
|
180
|
+
{type: 'metadata', url: module_metadata.data['template-url'], allow_fallback: true},
|
181
|
+
{type: 'default', url: PDK::Generate::Module::DEFAULT_TEMPLATE, allow_fallback: false},
|
182
|
+
]
|
183
|
+
end
|
184
|
+
|
185
|
+
# Retrieves the name of the module (without the forge username) from the
|
186
|
+
# module metadata.
|
187
|
+
#
|
188
|
+
# @raise (see #module_metadata)
|
189
|
+
# @return [String] The name of the module.
|
190
|
+
#
|
191
|
+
# @api private
|
192
|
+
def module_name
|
193
|
+
@module_name ||= module_metadata.data['name'].rpartition('-').last
|
194
|
+
end
|
195
|
+
|
196
|
+
# Parses the metadata.json file for the module.
|
197
|
+
#
|
198
|
+
# @raise [PDK::CLI::FatalError] if the metadata.json file does not exist,
|
199
|
+
# can not be read, or contains invalid metadata.
|
200
|
+
#
|
201
|
+
# @return [PDK::Module::Metadata] the parsed module metadata.
|
202
|
+
#
|
203
|
+
# @api private
|
204
|
+
def module_metadata
|
205
|
+
@module_metadata ||= begin
|
206
|
+
PDK::Module::Metadata.from_file(File.join(module_dir, 'metadata.json'))
|
207
|
+
rescue ArgumentError => e
|
208
|
+
raise PDK::CLI::FatalError, _("'%{dir}' does not contain valid Puppet module metadata; %{msg}") % {dir: module_dir, msg: e.message}
|
209
|
+
end
|
210
|
+
end
|
211
|
+
end
|
212
|
+
end
|
213
|
+
end
|