pdk 1.4.1 → 1.5.0

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