pdk 1.4.1 → 1.5.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.
@@ -13,6 +13,7 @@ module PDK::CLI
13
13
  'If not specified, validators are run against all applicable files in the module.',
14
14
  )
15
15
 
16
+ PDK::CLI.puppet_version_options(self)
16
17
  flag nil, :list, _('List all available validators.')
17
18
  flag :a, 'auto-correct', _('Automatically correct problems where possible.')
18
19
  flag nil, :parallel, _('Run validations in parallel.')
@@ -32,11 +33,15 @@ module PDK::CLI
32
33
  exit 0
33
34
  end
34
35
 
36
+ PDK::CLI::Util.validate_puppet_version_opts(opts)
37
+
35
38
  PDK::CLI::Util.ensure_in_module!(
36
39
  message: _('Code validation can only be run from inside a valid module directory'),
37
40
  log_level: :info,
38
41
  )
39
42
 
43
+ PDK::CLI::Util.module_version_check
44
+
40
45
  if args[0]
41
46
  # This may be a single validator, a list of validators, or a target.
42
47
  if Util::OptionValidator.comma_separated_list?(args[0])
@@ -80,8 +85,10 @@ module PDK::CLI
80
85
  options = targets.empty? ? {} : { targets: targets }
81
86
  options[:auto_correct] = true if opts.key?(:'auto-correct')
82
87
 
83
- # Ensure that the bundle is installed and tools are available before running any validations.
84
- PDK::Util::Bundler.ensure_bundle!
88
+ # Ensure that the bundled gems are up to date and correct Ruby is activated before running any validations.
89
+ puppet_env = PDK::CLI::Util.puppet_from_opts_or_env(opts)
90
+ PDK::Util::RubyVersion.use(puppet_env[:ruby_version])
91
+ PDK::Util::Bundler.ensure_bundle!(puppet_env[:gemset])
85
92
 
86
93
  exit_code = 0
87
94
  if opts[:parallel]
@@ -286,7 +286,7 @@ module PDK
286
286
  if opts[:only_ask]
287
287
  questions.reject! do |question|
288
288
  if %w[module_name forge_username].include?(question[:name])
289
- metadata.data['name'] && metadata.data['name'] =~ %r{\A[a-z0-9]+-[a-z0-9]+\Z}i
289
+ metadata.data['name'] && metadata.data['name'] =~ %r{\A[a-z0-9]+-[a-z][a-z0-9_]*\Z}i
290
290
  else
291
291
  !opts[:only_ask].include?(question[:name])
292
292
  end
@@ -52,25 +52,27 @@ module PDK
52
52
  end
53
53
  end
54
54
 
55
- # @return [String] the path where the new type will be written.
55
+ # @return [String] the path where the new provider will be written.
56
56
  def target_object_path
57
- @target_object_path ||= File.join(module_dir, 'lib', 'puppet', 'type', object_name) + '.rb'
57
+ @target_object_path ||= File.join(module_dir, 'lib', 'puppet', 'provider', object_name, object_name) + '.rb'
58
58
  end
59
59
 
60
- # @return [String] the path where the new provider will be written.
61
- def target_addon_path
62
- @target_addon_path ||= File.join(module_dir, 'lib', 'puppet', 'provider', object_name, object_name) + '.rb'
60
+ # @return [String] the path where the new type will be written.
61
+ def target_type_path
62
+ @target_type_path ||= File.join(module_dir, 'lib', 'puppet', 'type', object_name) + '.rb'
63
63
  end
64
64
 
65
- # Calculates the path to the file where the tests for the new defined
66
- # type will be written.
67
- #
68
- # @return [String] the path where the tests for the new defined type
65
+ # @return [String] the path where the tests for the new provider
69
66
  # will be written.
70
67
  def target_spec_path
71
68
  @target_spec_path ||= File.join(module_dir, 'spec', 'unit', 'puppet', 'provider', object_name, object_name) + '_spec.rb'
72
69
  end
73
70
 
71
+ # @return [String] the path where the tests for the new type will be written.
72
+ def target_type_spec_path
73
+ @target_type_spec_path ||= File.join(module_dir, 'spec', 'unit', 'puppet', 'type', object_name) + '_spec.rb'
74
+ end
75
+
74
76
  # transform a object name into a ruby class name
75
77
  def self.class_name_from_object_name(object_name)
76
78
  object_name.to_s.split('_').map(&:capitalize).join
@@ -58,11 +58,11 @@ module PDK
58
58
  raise NotImplementedError
59
59
  end
60
60
 
61
- # @abstract Subclass and implement {#target_addon_path}. Implementations
61
+ # @abstract Subclass and implement {#target_type_path}. Implementations
62
62
  # of this method should return a String containing the destination path
63
63
  # of the additional object file being generated.
64
64
  # @return [String] returns nil if there is no additional object file
65
- def target_addon_path
65
+ def target_type_path
66
66
  nil
67
67
  end
68
68
 
@@ -73,6 +73,13 @@ module PDK
73
73
  raise NotImplementedError
74
74
  end
75
75
 
76
+ # @abstract Subclass and implement {#target_type_spec_path}. Implementations
77
+ # of this method should return a String containing the destination path
78
+ # of the tests for the object being generated.
79
+ def target_type_spec_path
80
+ nil
81
+ end
82
+
76
83
  # Retrieves the type of the object being generated, e.g. :class,
77
84
  # :defined_type, etc. This is specified in the subclass' OBJECT_TYPE
78
85
  # constant.
@@ -91,7 +98,7 @@ module PDK
91
98
  #
92
99
  # @api public
93
100
  def check_preconditions
94
- [target_object_path, target_addon_path, target_spec_path].compact.each do |target_file|
101
+ [target_object_path, target_type_path, target_spec_path, target_type_spec_path].compact.each do |target_file|
95
102
  next unless File.exist?(target_file)
96
103
 
97
104
  raise PDK::CLI::ExitWithError, _("Unable to generate %{object_type}; '%{file}' already exists.") % {
@@ -116,8 +123,9 @@ module PDK
116
123
  data = template_data.merge(configs: config_hash)
117
124
 
118
125
  render_file(target_object_path, template_path[:object], data)
119
- render_file(target_addon_path, template_path[:addon], data) if template_path[:addon]
126
+ render_file(target_type_path, template_path[:type], data) if template_path[:type]
120
127
  render_file(target_spec_path, template_path[:spec], data) if template_path[:spec]
128
+ render_file(target_type_spec_path, template_path[:type_spec], data) if template_path[:type_spec]
121
129
  end
122
130
  end
123
131
 
@@ -66,22 +66,31 @@ module PDK
66
66
  end
67
67
 
68
68
  def stage_changes!
69
+ metadata_path = 'metadata.json'
70
+
69
71
  PDK::Module::TemplateDir.new(template_url, nil, false) do |templates|
70
- new_metadata = update_metadata('metadata.json', templates.metadata)
72
+ new_metadata = update_metadata(metadata_path, templates.metadata)
73
+ templates.module_metadata = new_metadata.data unless new_metadata.nil?
71
74
 
72
75
  if options[:noop] && new_metadata.nil?
73
- update_manager.add_file('metadata.json', '')
74
- elsif File.file?('metadata.json')
75
- update_manager.modify_file('metadata.json', new_metadata)
76
+ update_manager.add_file(metadata_path, '')
77
+ elsif File.file?(metadata_path)
78
+ update_manager.modify_file(metadata_path, new_metadata.to_json)
76
79
  else
77
- update_manager.add_file('metadata.json', new_metadata)
80
+ update_manager.add_file(metadata_path, new_metadata.to_json)
78
81
  end
79
82
 
80
- templates.render do |file_path, file_content|
81
- if File.exist? file_path
82
- update_manager.modify_file(file_path, file_content)
83
- else
84
- update_manager.add_file(file_path, file_content)
83
+ templates.render do |file_path, file_content, file_status|
84
+ if file_status == :unmanage
85
+ PDK.logger.debug(_("skipping '%{path}'") % { path: file_path })
86
+ elsif file_status == :delete
87
+ update_manager.remove_file(file_path)
88
+ elsif file_status == :manage
89
+ if File.exist? file_path
90
+ update_manager.modify_file(file_path, file_content)
91
+ else
92
+ update_manager.add_file(file_path, file_content)
93
+ end
85
94
  end
86
95
  end
87
96
  end
@@ -126,7 +135,7 @@ module PDK
126
135
  end
127
136
 
128
137
  metadata.update!(template_metadata)
129
- metadata.to_json
138
+ metadata
130
139
  end
131
140
 
132
141
  def summary
@@ -87,6 +87,25 @@ module PDK
87
87
  PDK::Generate::Module.module_interview(self, only_ask: missing_fields)
88
88
  end
89
89
 
90
+ def validate_puppet_version_requirement!
91
+ msgs = {
92
+ no_reqs: _('Module metadata does not contain any requirements.'),
93
+ no_puppet_req: _('Module metadata does not contain a "puppet" requirement.'),
94
+ no_puppet_ver: _('The "puppet" requirement in module metadata does not specify a "version_requirement".'),
95
+ }
96
+
97
+ raise ArgumentError, msgs[:no_reqs] unless @data.key?('requirements')
98
+ raise ArgumentError, msgs[:no_puppet_req] if puppet_requirement.nil?
99
+ raise ArgumentError, msgs[:no_puppet_ver] unless puppet_requirement.key?('version_requirement')
100
+ raise ArgumentError, msgs[:no_puppet_ver] if puppet_requirement['version_requirement'].empty?
101
+ end
102
+
103
+ def puppet_requirement
104
+ @data['requirements'].find do |r|
105
+ r.key?('name') && r['name'] == 'puppet'
106
+ end
107
+ end
108
+
90
109
  private
91
110
 
92
111
  def missing_fields
@@ -8,6 +8,8 @@ require 'pdk/template_file'
8
8
  module PDK
9
9
  module Module
10
10
  class TemplateDir
11
+ attr_accessor :module_metadata
12
+
11
13
  # Initialises the TemplateDir object with the path or URL to the template
12
14
  # and the block of code to run to be run while the template is available.
13
15
  #
@@ -30,43 +32,21 @@ module PDK
30
32
  # end
31
33
  # end
32
34
  #
33
- # @raise [PDK::CLI::FatalError] If the template is a git repository and
34
- # the git binary is unavailable.
35
- # @raise [PDK::CLI::FatalError] If the template is a git repository and
36
- # the git clone operation fails.
35
+ # @raise [ArgumentError] If no block is given to this method.
36
+ # @raise [PDK::CLI::FatalError] (see #clone_repo)
37
37
  # @raise [ArgumentError] (see #validate_module_template!)
38
38
  #
39
39
  # @api public
40
40
  def initialize(path_or_url, module_metadata = {}, init = false)
41
- if File.directory?(path_or_url)
42
- @path = path_or_url
43
- else
44
- # If path_or_url isn't a directory on disk, we assume that it is
45
- # a remote git repository.
46
-
47
- # @todo When switching this over to using rugged, cache the cloned
48
- # template repo in `%AppData%` or `$XDG_CACHE_DIR` and update before
49
- # use.
50
- temp_dir = PDK::Util.make_tmpdir_name('pdk-templates')
51
- git_ref = (path_or_url == PDK::Util.default_template_url) ? PDK::Util.default_template_ref : 'origin/master'
52
-
53
- clone_result = PDK::Util::Git.git('clone', path_or_url, temp_dir)
54
-
55
- if clone_result[:exit_code].zero?
56
- reset_result = PDK::Util::Git.git('-C', temp_dir, 'reset', '--hard', git_ref)
57
- unless reset_result[:exit_code].zero?
58
- PDK.logger.error reset_result[:stdout]
59
- PDK.logger.error reset_result[:stderr]
60
- raise PDK::CLI::FatalError, _("Unable to set git repository '%{repo}' to ref:'%{ref}'.") % { repo: temp_dir, ref: git_ref }
61
- end
62
- else
63
- PDK.logger.error clone_result[:stdout]
64
- PDK.logger.error clone_result[:stderr]
65
- raise PDK::CLI::FatalError, _("Unable to clone git repository '%{repo}' to '%{dest}'.") % { repo: path_or_url, dest: temp_dir }
66
- end
41
+ unless block_given?
42
+ raise ArgumentError, _('%{class_name} must be initialized with a block.') % { class_name: self.class.name }
43
+ end
67
44
 
68
- @path = PDK::Util.canonical_path(temp_dir)
45
+ if PDK::Util::Git.repo?(path_or_url)
46
+ @path = self.class.clone_template_repo(path_or_url)
69
47
  @repo = path_or_url
48
+ else
49
+ @path = path_or_url
70
50
  end
71
51
 
72
52
  @init = init
@@ -75,6 +55,7 @@ module PDK
75
55
  @dirs = [@moduleroot_dir]
76
56
  @dirs << @moduleroot_init if @init
77
57
  @object_dir = File.join(@path, 'object_templates')
58
+
78
59
  validate_module_template!
79
60
 
80
61
  @module_metadata = module_metadata
@@ -127,16 +108,26 @@ module PDK
127
108
  template_file = template_file.to_s
128
109
  PDK.logger.debug(_("Rendering '%{template}'...") % { template: template_file })
129
110
  dest_path = template_file.sub(%r{\.erb\Z}, '')
130
- begin
131
- dest_content = PDK::TemplateFile.new(File.join(template_loc, template_file), configs: config_for(dest_path)).render
132
- rescue => e
133
- error_msg = _(
134
- "Failed to render template '%{template}'\n" \
135
- '%{exception}: %{message}',
136
- ) % { template: template_file, exception: e.class, message: e.message }
137
- raise PDK::CLI::FatalError, error_msg
111
+ config = config_for(dest_path)
112
+ dest_status = :manage
113
+
114
+ if config['unmanaged']
115
+ dest_status = :unmanage
116
+ elsif config['delete']
117
+ dest_status = :delete
118
+ else
119
+ begin
120
+ dest_content = PDK::TemplateFile.new(File.join(template_loc, template_file), configs: config).render
121
+ rescue => e
122
+ error_msg = _(
123
+ "Failed to render template '%{template}'\n" \
124
+ '%{exception}: %{message}',
125
+ ) % { template: template_file, exception: e.class, message: e.message }
126
+ raise PDK::CLI::FatalError, error_msg
127
+ end
138
128
  end
139
- yield dest_path, dest_content
129
+
130
+ yield dest_path, dest_content, dest_status
140
131
  end
141
132
  end
142
133
 
@@ -155,13 +146,15 @@ module PDK
155
146
  # @api public
156
147
  def object_template_for(object_type)
157
148
  object_path = File.join(@object_dir, "#{object_type}.erb")
158
- addon_path = File.join(@object_dir, "#{object_type}_addon.erb")
149
+ type_path = File.join(@object_dir, "#{object_type}_type.erb")
159
150
  spec_path = File.join(@object_dir, "#{object_type}_spec.erb")
151
+ type_spec_path = File.join(@object_dir, "#{object_type}_type_spec.erb")
160
152
 
161
153
  if File.file?(object_path) && File.readable?(object_path)
162
154
  result = { object: object_path }
163
- result[:addon] = addon_path if File.file?(addon_path) && File.readable?(addon_path)
155
+ result[:type] = type_path if File.file?(type_path) && File.readable?(type_path)
164
156
  result[:spec] = spec_path if File.file?(spec_path) && File.readable?(spec_path)
157
+ result[:type_spec] = type_spec_path if File.file?(type_spec_path) && File.readable?(type_spec_path)
165
158
  result
166
159
  else
167
160
  nil
@@ -282,6 +275,37 @@ module PDK
282
275
  {}
283
276
  end
284
277
  end
278
+
279
+ # @return [String] Path to working directory into which template repo has been cloned and reset
280
+ #
281
+ # @raise [PDK::CLI::FatalError] If unable to clone the given origin_repo into a tempdir.
282
+ # @raise [PDK::CLI::FatalError] If reset HEAD of the cloned repo to desired ref.
283
+ #
284
+ # @api private
285
+ def self.clone_template_repo(origin_repo)
286
+ # @todo When switching this over to using rugged, cache the cloned
287
+ # template repo in `%AppData%` or `$XDG_CACHE_DIR` and update before
288
+ # use.
289
+ temp_dir = PDK::Util.make_tmpdir_name('pdk-templates')
290
+ git_ref = (origin_repo == PDK::Util.default_template_url) ? PDK::Util.default_template_ref : 'origin/master'
291
+
292
+ clone_result = PDK::Util::Git.git('clone', origin_repo, temp_dir)
293
+
294
+ if clone_result[:exit_code].zero?
295
+ reset_result = PDK::Util::Git.git('-C', temp_dir, 'reset', '--hard', git_ref)
296
+ unless reset_result[:exit_code].zero?
297
+ PDK.logger.error reset_result[:stdout]
298
+ PDK.logger.error reset_result[:stderr]
299
+ raise PDK::CLI::FatalError, _("Unable to set HEAD of git repository at '%{repo}' to ref:'%{ref}'.") % { repo: temp_dir, ref: git_ref }
300
+ end
301
+ else
302
+ PDK.logger.error clone_result[:stdout]
303
+ PDK.logger.error clone_result[:stderr]
304
+ raise PDK::CLI::FatalError, _("Unable to clone git repository at '%{repo}' into '%{dest}'.") % { repo: origin_repo, dest: temp_dir }
305
+ end
306
+
307
+ PDK::Util.canonical_path(temp_dir)
308
+ end
285
309
  end
286
310
  end
287
311
  end
@@ -8,14 +8,14 @@ module PDK
8
8
  def run
9
9
  stage_changes!
10
10
 
11
+ if current_version == new_version
12
+ PDK.logger.debug _('This module is already up to date with version %{version} of the template.') % {
13
+ version: new_version,
14
+ }
15
+ end
16
+
11
17
  unless update_manager.changes?
12
- if current_version == new_version
13
- PDK.logger.info _('This module is already up to date with version %{version} of the template.') % {
14
- version: new_version,
15
- }
16
- else
17
- PDK::Report.default_target.puts(_('No changes required.'))
18
- end
18
+ PDK::Report.default_target.puts(_('No changes required.'))
19
19
  return
20
20
  end
21
21
 
@@ -18,7 +18,7 @@ module PDK
18
18
 
19
19
  def self.rake(task, spinner_text, environment = {})
20
20
  argv = [rake_bin, task]
21
- argv.unshift('ruby') if Gem.win_platform?
21
+ argv.unshift(File.join(PDK::Util::RubyVersion.bin_path, 'ruby.exe')) if Gem.win_platform?
22
22
 
23
23
  command = PDK::CLI::Exec::Command.new(*argv).tap do |c|
24
24
  c.context = :module
@@ -62,7 +62,6 @@ module PDK
62
62
  end
63
63
 
64
64
  def self.invoke(report, options = {})
65
- PDK::Util::Bundler.ensure_bundle!
66
65
  PDK::Util::Bundler.ensure_binstubs!('rake')
67
66
 
68
67
  setup
@@ -181,11 +180,11 @@ module PDK
181
180
 
182
181
  # @return array of { :id, :full_description }
183
182
  def self.list
184
- PDK::Util::Bundler.ensure_bundle!
185
183
  PDK::Util::Bundler.ensure_binstubs!('rake')
186
184
 
187
185
  command_argv = [File.join(PDK::Util.module_root, 'bin', 'rake'), 'spec_list_json']
188
- command_argv.unshift('ruby') if Gem.win_platform?
186
+ command_argv.unshift(File.join(PDK::Util::RubyVersion.bin_path, 'ruby.exe')) if Gem.win_platform?
187
+
189
188
  list_command = PDK::CLI::Exec::Command.new(*command_argv)
190
189
  list_command.context = :module
191
190
  output = list_command.execute!
@@ -3,9 +3,19 @@ require 'tempfile'
3
3
 
4
4
  require 'pdk/util/version'
5
5
  require 'pdk/util/windows'
6
+ require 'pdk/util/vendored_file'
6
7
 
7
8
  module PDK
8
9
  module Util
10
+ MODULE_FOLDERS = %w[
11
+ manifests
12
+ lib
13
+ tasks
14
+ facts.d
15
+ functions
16
+ types
17
+ ].freeze
18
+
9
19
  # Searches upwards from current working directory for the given target file.
10
20
  #
11
21
  # @param target [String] Name of file to search for.
@@ -32,7 +42,9 @@ module PDK
32
42
  #
33
43
  # @return [String] The temporary directory path.
34
44
  def make_tmpdir_name(base)
35
- Dir::Tmpname.make_tmpname(File.join(Dir.tmpdir, base), nil)
45
+ t = Time.now.strftime('%Y%m%d')
46
+ name = "#{base}#{t}-#{Process.pid}-#{rand(0x100000000).to_s(36)}"
47
+ File.join(Dir.tmpdir, name)
36
48
  end
37
49
  module_function :make_tmpdir_name
38
50
 
@@ -100,12 +112,24 @@ module PDK
100
112
  metadata_path = find_upwards('metadata.json')
101
113
  if metadata_path
102
114
  File.dirname(metadata_path)
115
+ elsif in_module_root?
116
+ Dir.pwd
103
117
  else
104
118
  nil
105
119
  end
106
120
  end
107
121
  module_function :module_root
108
122
 
123
+ # Returns true or false depending on if any of the common directories in a module
124
+ # are found in the current directory
125
+ #
126
+ # @return [boolean] True if any folders from MODULE_FOLDERS are found in the current dir,
127
+ # false otherwise.
128
+ def in_module_root?
129
+ PDK::Util::MODULE_FOLDERS.any? { |dir| File.directory?(dir) }
130
+ end
131
+ module_function :in_module_root?
132
+
109
133
  # Iterate through possible JSON documents until we find one that is valid.
110
134
  #
111
135
  # @param [String] text the text in which to find a JSON document
@@ -181,7 +205,7 @@ module PDK
181
205
  return puppetlabs_template_url if answer_file_url == 'https://github.com/puppetlabs/pdk-module-template'
182
206
  return puppetlabs_template_url if answer_file_url == puppetlabs_template_url
183
207
 
184
- unless PDK::Util::Git.repo_exists?(answer_file_url)
208
+ unless PDK::Util::Git.repo?(answer_file_url)
185
209
  PDK.logger.warn(_("Unable to access the previously used template '%{template}', using the default template instead.") % { template: answer_file_url })
186
210
  PDK.answers.update!('template-url' => nil)
187
211
  return puppetlabs_template_url
@@ -228,5 +252,16 @@ module PDK
228
252
  ['pdk-version', 'template-url'].any? { |key| module_metadata.key?(key) }
229
253
  end
230
254
  module_function :module_pdk_compatible?
255
+
256
+ def module_pdk_version
257
+ metadata = module_metadata
258
+
259
+ if !metadata.nil? && metadata.include?('pdk-version')
260
+ metadata['pdk-version'].split.first
261
+ else
262
+ nil
263
+ end
264
+ end
265
+ module_function :module_pdk_version
231
266
  end
232
267
  end