pdk 0.3.0 → 0.4.0

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: 5501e7effa501cd2ba488735cc9abb8257ffef6a
4
- data.tar.gz: e4f7a88496280db89b0ee46a42a7b8feacc24594
3
+ metadata.gz: eaa8d8a0b82102681c9b61fe39ba2222cd252a3a
4
+ data.tar.gz: 586bc04ec9a42581020f88a03d5ed58f01b52d62
5
5
  SHA512:
6
- metadata.gz: 736c543069aa1b1d3eb79c8de901e91cf2491328fc3104f7bd660c37cafd88712583d471cbd2f4afd37b9c49890bfcb60c5b2857121f3cabb81841662fa53557
7
- data.tar.gz: 5b5b03c6ac83fca4d730cba0db0277441d9bb062ed5f13ebecf24836d237db11856e6811f276ec2dcfafe28fd5c86a009958ef9e894021d14ba8a1e4d0a15af8
6
+ metadata.gz: 33b02d12161649fd01c1b5e9c259646cfff45e0b0ffd4625ac28caea26aa995ce0346866eb81042c29f900d8dd99a5cb1f71d5347416da332a0376cf9f6ed166
7
+ data.tar.gz: 9f17d85f76f08d2feb31a9b6e5c2f8c7c39240be48f6a9c51f06cf3f441dddaf239b6da10d45ae075ce38b15d356c3a9572b9305691f3d069e4af5d83fb2576a
@@ -3,6 +3,19 @@
3
3
  All notable changes to this project will be documented in this file.
4
4
 
5
5
 
6
+ ## [v0.4.0](https://github.com/puppetlabs/pdk/tree/v0.4.0) (2017-07-14)
7
+ [Full Changelog](https://github.com/puppetlabs/pdk/compare/v0.3.0...v0.4.0)
8
+
9
+ **Implemented enhancements:**
10
+
11
+ - \(SDK-299\) Check metadata.json syntax before linting [\#133](https://github.com/puppetlabs/pdk/pull/133) ([rodjek](https://github.com/rodjek))
12
+ - \(SDK-305\) Answer file to cache module interview answers, template-url etc [\#132](https://github.com/puppetlabs/pdk/pull/132) ([rodjek](https://github.com/rodjek))
13
+ - \(SDK-296\) Allow target selection for the metadata validator [\#124](https://github.com/puppetlabs/pdk/pull/124) ([rodjek](https://github.com/rodjek))
14
+
15
+ **Fixed bugs:**
16
+
17
+ - \(SDK-298\) Handle exception raised when an invalid report format is specified on the CLI [\#125](https://github.com/puppetlabs/pdk/pull/125) ([rodjek](https://github.com/rodjek))
18
+
6
19
  ## [v0.3.0](https://github.com/puppetlabs/pdk/tree/v0.3.0) (2017-06-29)
7
20
  [Full Changelog](https://github.com/puppetlabs/pdk/compare/v0.2.0...v0.3.0)
8
21
 
data/README.md CHANGED
@@ -25,21 +25,7 @@ To get started, generate a new module from the default template.
25
25
  pdk new module my_module
26
26
  ```
27
27
 
28
- This generates the basic components of a new module and initializes Git for you. The `pdk new module` command sets some default values based on your environment. Check the `metadata.json` to make sure that these values are correct. The new module now contains all the infrastructure to use the other capabilities of `pdk`.
29
-
30
- ### Add a new resource provider
31
-
32
- If you need to manage a specific resource that is not covered by either Puppet's basic resource types or an existing module, create a new resource provider.
33
-
34
- 1. From within an existing module, run `pdk add provider`, specifying the new provider name as well as any attributes along with their data types.
35
-
36
- For example:
37
-
38
- ```
39
- pdk add provider new_provider String:content 'Enum[absent, present]:ensure'
40
- ```
41
-
42
- This creates all the files required to define a resource type, its provider, and the associated basic tests. In this example, the resource type has an `ensure` property with the expected values, and a `String` property named `content`. If your types use Bash special characters, such as 'Enum[absent, present]:ensure' above, you must quote to avoid issues with the shell.
28
+ This generates the basic components of a new module. The `pdk new module` command sets some default metadata values based on your environment. Check the `metadata.json` to make sure that these values are correct. The new module now contains all the infrastructure to use the other capabilities of `pdk`.
43
29
 
44
30
  ### Running validations
45
31
 
@@ -299,6 +285,31 @@ bundle binstubs pdk --path ~/bin
299
285
 
300
286
  Bug reports and pull requests are welcome on GitHub at https://github.com/puppetlabs/pdk.
301
287
 
288
+ ### Running tests
289
+
290
+ pdk has three testing rake tasks
291
+
292
+ #### spec
293
+
294
+ Run unit tests.
295
+
296
+ #### acceptance:local
297
+
298
+ Run acceptance tests on the current pdk code. These tests are executed on commits and pull requests to this repo using both travis and appveyor.
299
+
300
+ #### acceptance:package
301
+
302
+ Run acceptance tests against a package install. This task is for Puppet's packaging CI, and contributors outside of Puppet, Inc. don't need to worry about executing it. It uses [beaker](https://github.com/puppetlabs/beaker) to provision a VM, fetch and install a pdk installation package, and then run the acceptance tests on that VM.
303
+ It requires some environment variables to be set in order to specify what beaker will set up:
304
+
305
+ Environment Variable | Usage
306
+ ---------------------|------
307
+ **SHA** | The SHA or tag of a package build i.e. the folder name on the build server that packages will be found in.
308
+ **TEST_TARGET** | A beaker-hostgenerator string for the OS of the VM you want to test on e.g. _redhat7-64workstation._ or _windows2012r2-64workstation._ (The period character after workstation is required by beaker-hostgenerator).
309
+ **BUILD_SERVER** | (Only required if the tests will run on a Windows VM). The hostname of the build server that hosts packages. A Puppet JIRA ticket ([BKR-1109](https://tickets.puppetlabs.com/browse/BKR-1109)) has been filed to update beaker so this would never be required.
310
+
311
+ On completion of this testing task, the results from the VM will be available in a folder named _archive_.
312
+
302
313
  ### Release Process
303
314
 
304
315
  1. Bump the version in `lib/pdk/version.rb`.
@@ -307,4 +318,4 @@ Bug reports and pull requests are welcome on GitHub at https://github.com/puppet
307
318
  1. Commit and PR the changes.
308
319
  1. When the PR is merged, get a clean checkout of the merged commit, and run `bundle exec rake release[upstream]` (where "upstream" is your local name of the puppetlabs remote)
309
320
  1. Profit!
310
- 1. Update `lib/pdk/version.rb` with `x.y.z-pre`, commit, and PR to prepare for next release.
321
+ 1. Update `lib/pdk/version.rb` with `x.y.z.pre` version bump, commit, and PR to prepare for next release.
data/lib/pdk.rb CHANGED
@@ -1,3 +1,4 @@
1
+ require 'pdk/answer_file'
1
2
  require 'pdk/generate'
2
3
  require 'pdk/i18n'
3
4
  require 'pdk/logger'
@@ -0,0 +1,120 @@
1
+ require 'json'
2
+
3
+ module PDK
4
+ # Singleton accessor to the current answer file being used by the PDK.
5
+ #
6
+ # @return [PDK::AnswerFile] The AnswerFile instance currently being used by
7
+ # the PDK.
8
+ def self.answers
9
+ @answer_file ||= PDK::AnswerFile.new
10
+ end
11
+
12
+ # Specify the path to a custom answer file that the PDK should use.
13
+ #
14
+ # @param path [String] A path on disk to the file where the PDK should store
15
+ # answers to interactive questions.
16
+ def self.answer_file=(value)
17
+ @answer_file = PDK::AnswerFile.new(value)
18
+ end
19
+
20
+ class AnswerFile
21
+ attr_reader :answers
22
+ attr_reader :answer_file_path
23
+
24
+ # Initialises the AnswerFile object, which stores the responses to certain
25
+ # interactive questions.
26
+ #
27
+ # @param answer_file_path [String, nil] The path on disk to the file where
28
+ # the answers will be stored and read from. If not specified (or `nil`),
29
+ # the default path will be used (see #default_answer_file_path).
30
+ #
31
+ # @raise (see #read_from_disk)
32
+ def initialize(answer_file_path = nil)
33
+ @answer_file_path = answer_file_path || default_answer_file_path
34
+ @answers = read_from_disk
35
+ end
36
+
37
+ # Retrieve the stored answer to a question.
38
+ #
39
+ # @param question [String] The question name/identifying string.
40
+ #
41
+ # @return [Object] The answer to the question, or `nil` if no answer found.
42
+ def [](question)
43
+ answers[question]
44
+ end
45
+
46
+ # Update the stored answers in memory and then save them to disk.
47
+ #
48
+ # @param new_answers [Hash{String => Object}] The new questions and answers
49
+ # to be merged into the existing answers.
50
+ #
51
+ # @raise [PDK::CLI::FatalError] if the new answers are not provided as
52
+ # a Hash.
53
+ # @raise (see #save_to_disk)
54
+ def update!(new_answers = {})
55
+ unless new_answers.is_a?(Hash)
56
+ raise PDK::CLI::FatalError, _('Answer file can only be updated with a Hash')
57
+ end
58
+
59
+ answers.merge!(new_answers)
60
+
61
+ save_to_disk
62
+ end
63
+
64
+ private
65
+
66
+ # Determine the default path to the answer file.
67
+ #
68
+ # @return [String] The path on disk to the default answer file.
69
+ def default_answer_file_path
70
+ File.join(PDK::Util.cachedir, 'answers.json')
71
+ end
72
+
73
+ # Read existing answers into memory from the answer file on disk.
74
+ #
75
+ # @raise [PDK::CLI::FatalError] If the answer file exists but can not be
76
+ # read.
77
+ #
78
+ # @return [Hash{String => Object}] The existing questions and answers.
79
+ def read_from_disk
80
+ return {} if !File.file?(answer_file_path) || File.zero?(answer_file_path)
81
+
82
+ unless File.readable?(answer_file_path)
83
+ raise PDK::CLI::FatalError, _("Unable to open '%{file}' for reading") % {
84
+ file: answer_file_path,
85
+ }
86
+ end
87
+
88
+ answers = JSON.parse(File.read(answer_file_path))
89
+ if answers.is_a?(Hash)
90
+ answers
91
+ else
92
+ PDK.logger.warn _("Answer file '%{path}' did not contain a valid set of answers, recreating it") % {
93
+ path: answer_file_path,
94
+ }
95
+ {}
96
+ end
97
+ rescue JSON::JSONError
98
+ PDK.logger.warn _("Answer file '%{path}' did not contain valid JSON, recreating it") % {
99
+ path: answer_file_path,
100
+ }
101
+ {}
102
+ end
103
+
104
+ # Save the in memory answer set to the answer file on disk.
105
+ #
106
+ # @raise [PDK::CLI::FatalError] if the answer file can not be written to.
107
+ def save_to_disk
108
+ FileUtils.mkdir_p(File.dirname(answer_file_path))
109
+
110
+ File.open(answer_file_path, 'w') do |answer_file|
111
+ answer_file.puts JSON.pretty_generate(answers)
112
+ end
113
+ rescue SystemCallError, IOError => e
114
+ raise PDK::CLI::FatalError, _("Unable to write '%{file}': %{msg}") % {
115
+ file: answer_file_path,
116
+ msg: e.message,
117
+ }
118
+ end
119
+ end
120
+ end
@@ -60,20 +60,16 @@ module PDK::CLI
60
60
  ) % { available_formats: PDK::Report.formats.join("', '") }
61
61
 
62
62
  option :f, :format, format_desc, argument: :required, multiple: true do |values|
63
- values.compact.each do |v|
64
- if v.include?(':')
65
- format = v.split(':', 2).first
66
-
67
- Util::OptionValidator.enum(format, PDK::Report.formats)
68
- else
69
- Util::OptionValidator.enum(v, PDK::Report.formats)
70
- end
71
- end
63
+ PDK::CLI::Util::OptionNormalizer.report_formats(values.compact)
72
64
  end
73
65
 
74
66
  flag :d, :debug, _('Enable debug output.') do |_, _|
75
67
  PDK.logger.enable_debug_output
76
68
  end
69
+
70
+ option nil, 'answer-file', _('Path to an answer file'), argument: :required, hidden: true do |value|
71
+ PDK.answer_file = value
72
+ end
77
73
  end
78
74
 
79
75
  require 'pdk/cli/new'
@@ -3,6 +3,8 @@ require 'childprocess'
3
3
  require 'tempfile'
4
4
  require 'tty-spinner'
5
5
 
6
+ require 'pdk/util'
7
+
6
8
  module PDK
7
9
  module CLI
8
10
  module Exec
@@ -10,12 +12,8 @@ module PDK
10
12
  Command.new(*cmd).execute!
11
13
  end
12
14
 
13
- def self.pdk_basedir
14
- @pdk_basedir ||= Gem.win_platform? ? 'C:/Program Files/Puppet Labs/DevelopmentKit' : '/opt/puppetlabs/sdk'
15
- end
16
-
17
15
  def self.git_bindir
18
- @git_dir ||= File.join(pdk_basedir, 'private', 'git', Gem.win_platform? ? 'cmd' : 'bin')
16
+ @git_dir ||= File.join('private', 'git', Gem.win_platform? ? 'cmd' : 'bin')
19
17
  end
20
18
 
21
19
  def self.git(*args)
@@ -27,24 +25,29 @@ module PDK
27
25
 
28
26
  def self.bundle(*args)
29
27
  bundle_bin = Gem.win_platform? ? 'bundle.bat' : 'bundle'
30
- vendored_bin_path = File.join(pdk_basedir, 'private', 'ruby', '2.1.9', 'bin', bundle_bin)
28
+ vendored_bin_path = File.join('private', 'ruby', '2.1.9', 'bin', bundle_bin)
31
29
 
32
30
  execute(try_vendored_bin(vendored_bin_path, bundle_bin), *args)
33
31
  end
34
32
 
35
33
  def self.bundle_bin
36
34
  bundle_bin = Gem.win_platform? ? 'bundle.bat' : 'bundle'
37
- vendored_bin_path = File.join(pdk_basedir, 'private', 'ruby', '2.1.9', 'bin', bundle_bin)
35
+ vendored_bin_path = File.join('private', 'ruby', '2.1.9', 'bin', bundle_bin)
38
36
 
39
37
  try_vendored_bin(vendored_bin_path, bundle_bin)
40
38
  end
41
39
 
42
40
  def self.try_vendored_bin(vendored_bin_path, fallback)
43
- if File.exist?(vendored_bin_path)
44
- PDK.logger.debug(_("Using '%{vendored_bin_path}'") % { fallback: fallback, vendored_bin_path: vendored_bin_path })
45
- vendored_bin_path
41
+ unless PDK::Util.package_install?
42
+ PDK.logger.debug(_("PDK package installation not found, trying '%{fallback}' from the system PATH instead") % { fallback: fallback })
43
+ return fallback
44
+ end
45
+
46
+ if File.exist?(File.join(PDK::Util.pdk_package_basedir, vendored_bin_path))
47
+ PDK.logger.debug(_("Using '%{vendored_bin_path}' from PDK package") % { vendored_bin_path: vendored_bin_path })
48
+ File.join(PDK::Util.pdk_package_basedir, vendored_bin_path)
46
49
  else
47
- PDK.logger.debug(_("Trying '%{fallback}' from the system PATH, instead of '%{vendored_bin_path}'") % { fallback: fallback, vendored_bin_path: vendored_bin_path })
50
+ PDK.logger.debug(_("Could not find '%{vendored_bin_path}' in PDK package, trying '%{fallback}' from the system PATH instead") % { fallback: fallback, vendored_bin_path: vendored_bin_path })
48
51
  fallback
49
52
  end
50
53
  end
@@ -101,9 +104,17 @@ module PDK
101
104
  end
102
105
 
103
106
  if context == :module
107
+ # Subprocesses use their own set of gems which are managed by pdk or installed with the package.
108
+ @process.environment['GEM_HOME'] = File.join(PDK::Util.cachedir, 'ruby', RbConfig::CONFIG['ruby_version'])
109
+
110
+ if PDK::Util.gem_install?
111
+ # This allows the subprocess to find the 'bundler' gem, which isn't in the cachedir above for gem installs.
112
+ # bundler_gem_path = File.absolute_path(File.join(Gem.loaded_specs['bundler'].gem_dir, '..', '..', '..', '..', '..'))
113
+ bundler_gem_path = File.absolute_path(File.join(`bundle show bundler`, '..', '..'))
114
+ @process.environment['GEM_PATH'] = bundler_gem_path
115
+ end
116
+
104
117
  # TODO: we should probably more carefully manage PATH and maybe other things too
105
- @process.environment['GEM_HOME'] = File.join(PDK::Util.cachedir, 'bundler', 'ruby', RbConfig::CONFIG['ruby_version'])
106
- @process.environment['GEM_PATH'] = pdk_gem_path
107
118
 
108
119
  mod_root = PDK::Util.module_root
109
120
 
@@ -168,21 +179,6 @@ module PDK
168
179
  @process.wait
169
180
  end
170
181
  end
171
-
172
- def pdk_gem_path
173
- @pdk_gem_path ||= find_pdk_gem_path
174
- end
175
-
176
- def find_pdk_gem_path
177
- package_gem_path = File.join(PDK::CLI::Exec.pdk_basedir, 'private', 'ruby', RUBY_VERSION, 'lib', 'ruby', 'gems', RbConfig::CONFIG['ruby_version'])
178
-
179
- if File.directory?(package_gem_path)
180
- package_gem_path
181
- else
182
- # FIXME: calculate this more reliably
183
- File.absolute_path(File.join(`bundle show bundler`, '..', '..'))
184
- end
185
- end
186
182
  end
187
183
 
188
184
  # This is a placeholder until we integrate ansicon into Windows packaging
@@ -27,9 +27,9 @@ module PDK
27
27
  num_questions = @questions.count
28
28
  @questions.each do |question_name, question|
29
29
  @name = question_name
30
- puts pastel.bold(_('[Q %{current_number}/%{questions_total}]') % { current_number: i, questions_total: num_questions })
31
- puts pastel.bold(question[:question])
32
- puts question[:help]
30
+ @prompt.print pastel.bold(_('[Q %{current_number}/%{questions_total}]') % { current_number: i, questions_total: num_questions })
31
+ @prompt.print pastel.bold(question[:question])
32
+ @prompt.print question[:help]
33
33
  ask(_('-->')) do |q|
34
34
  q.required(question.fetch(:required, false))
35
35
 
@@ -40,7 +40,7 @@ module PDK
40
40
  q.default(question[:default]) if question.key?(:default)
41
41
  end
42
42
  i += 1
43
- puts ''
43
+ @prompt.print ''
44
44
  end
45
45
  @answers
46
46
  rescue TTY::Prompt::Reader::InputInterrupt
@@ -28,7 +28,7 @@ module PDK
28
28
 
29
29
  begin
30
30
  OptionValidator.enum(format, PDK::Report.formats)
31
- rescue
31
+ rescue ArgumentError
32
32
  raise PDK::CLI::FatalError, _("'%{name}' is not a valid report format (%{valid})") % {
33
33
  name: format,
34
34
  valid: PDK::Report.formats.join(', '),
@@ -40,6 +40,8 @@ module PDK
40
40
  target = $stdout
41
41
  when 'stderr'
42
42
  target = $stderr
43
+ when nil
44
+ target = PDK::Report.default_target
43
45
  end
44
46
 
45
47
  { method: "write_#{format}".to_sym, target: target }
@@ -11,7 +11,7 @@ module PDK
11
11
  invalid_entries = vals.reject { |e| valid_entries.include?(e) }
12
12
 
13
13
  unless invalid_entries.empty?
14
- raise _('Error: the following values are invalid: %{invalid_entries}') % { invalid_entries: invalid_entries }
14
+ raise ArgumentError, _('Error: the following values are invalid: %{invalid_entries}') % { invalid_entries: invalid_entries }
15
15
  end
16
16
 
17
17
  val
@@ -30,7 +30,7 @@ module PDK
30
30
 
31
31
  prepare_module_directory(temp_target_dir)
32
32
 
33
- template_url = opts.fetch(:'template-url', DEFAULT_TEMPLATE)
33
+ template_url = opts.fetch(:'template-url', PDK.answers['template-url'] || DEFAULT_TEMPLATE)
34
34
 
35
35
  PDK::Module::TemplateDir.new(template_url) do |templates|
36
36
  templates.render do |file_path, file_content|
@@ -48,16 +48,28 @@ module PDK
48
48
  end
49
49
  end
50
50
 
51
+ PDK.answers.update!('template-url' => template_url)
52
+
51
53
  FileUtils.mv(temp_target_dir, target_dir)
52
54
  end
53
55
 
54
- def self.prepare_metadata(opts)
55
- username = Etc.getlogin.gsub(%r{[^0-9a-z]}i, '')
56
- username = 'username' if username == ''
57
- if Etc.getlogin != username
58
- PDK.logger.warn(_('Your username is not a valid Forge username, proceeding with the username %{username}' % { username: username }))
56
+ def self.username_from_login
57
+ login = Etc.getlogin
58
+ login_clean = login.gsub(%r{[^0-9a-z]}i, '')
59
+ login_clean = 'username' if login_clean.empty?
60
+
61
+ if login_clean != login
62
+ PDK.logger.warn _('You username is not a valid Forge username, proceeding with the username %{username}') % {
63
+ username: login_clean,
64
+ }
59
65
  end
60
66
 
67
+ login_clean
68
+ end
69
+
70
+ def self.prepare_metadata(opts)
71
+ username = PDK.answers['forge-username'] || username_from_login
72
+
61
73
  defaults = {
62
74
  'name' => "#{username}-#{opts[:name]}",
63
75
  'version' => '0.1.0',
@@ -65,11 +77,13 @@ module PDK
65
77
  { 'name' => 'puppetlabs-stdlib', 'version_requirement' => '>= 4.13.1 < 5.0.0' },
66
78
  ],
67
79
  }
80
+ defaults['author'] = PDK.answers['author'] unless PDK.answers['author'].nil?
81
+ defaults['license'] = PDK.answers['license'] unless PDK.answers['license'].nil?
68
82
  defaults['license'] = opts[:license] if opts.key? :license
69
83
 
70
84
  metadata = PDK::Module::Metadata.new(defaults)
71
85
 
72
- module_interview(metadata, opts) unless opts[:'skip-interview'] # @todo Build way to get info by answers file
86
+ module_interview(metadata, opts) unless opts[:'skip-interview']
73
87
 
74
88
  metadata.update!('pdk-version' => PDK::Util::Version.version_string)
75
89
 
@@ -98,7 +112,7 @@ module PDK
98
112
  required: true,
99
113
  validate_pattern: %r{\A[a-z0-9]+\Z}i,
100
114
  validate_message: _('Forge usernames can only contain lowercase letters and numbers'),
101
- default: metadata.data['author'],
115
+ default: PDK.answers['forge-username'] || metadata.data['author'],
102
116
  },
103
117
  {
104
118
  name: 'version',
@@ -167,11 +181,13 @@ module PDK
167
181
  answers = interview.run
168
182
 
169
183
  if answers.nil?
170
- puts _('Interview cancelled, aborting...')
184
+ PDK.logger.info _('Interview cancelled, not generating the module.')
171
185
  exit 0
172
186
  end
173
187
 
188
+ forge_username = answers['name']
174
189
  answers['name'] = "#{answers['name']}-#{opts[:name]}"
190
+ answers['license'] = opts[:license] if opts.key?(:license)
175
191
  metadata.update!(answers)
176
192
 
177
193
  puts '-' * 40
@@ -181,10 +197,16 @@ module PDK
181
197
  puts '-' * 40
182
198
  puts
183
199
 
184
- unless prompt.yes?(_('About to generate this module; continue?')) # rubocop:disable Style/GuardClause
185
- puts _('Aborting...')
200
+ unless prompt.yes?(_('About to generate this module; continue?'))
201
+ PDK.logger.info _('Module not generated.')
186
202
  exit 0
187
203
  end
204
+
205
+ PDK.answers.update!(
206
+ 'forge-username' => forge_username,
207
+ 'author' => answers['author'],
208
+ 'license' => answers['license'],
209
+ )
188
210
  end
189
211
  end
190
212
  end
@@ -67,7 +67,7 @@ module PDK
67
67
  end
68
68
 
69
69
  def to_json
70
- JSON.pretty_generate(@data)
70
+ JSON.pretty_generate(@data.dup.delete_if { |_key, value| value.nil? })
71
71
  end
72
72
 
73
73
  private
@@ -48,9 +48,6 @@ module PDK
48
48
  # @param target [#write] an IO object that the report will be written to.
49
49
  # Defaults to PDK::Report.default_target.
50
50
  def write_junit(target = self.class.default_target)
51
- # Extra defaulting here, b/c the Class.send method will pass in nil
52
- target ||= self.class.default_target
53
-
54
51
  # Open a File Object for IO if target is a string containing a filename or path
55
52
  target = File.open(target, 'w') if target.is_a? String
56
53
 
@@ -94,9 +91,6 @@ module PDK
94
91
  # @param target [#write] an IO object that the report will be written to.
95
92
  # Defaults to PDK::Report.default_target.
96
93
  def write_text(target = self.class.default_target)
97
- # Extra defaulting here, b/c the Class.send method will pass in nil
98
- target ||= self.class.default_target
99
-
100
94
  # Open a File Object for IO if target is a string containing a filename or path
101
95
  target = File.open(target, 'w') if target.is_a? String
102
96
 
@@ -2,17 +2,20 @@ require 'tmpdir'
2
2
  require 'tempfile'
3
3
  require 'puppet/util/windows'
4
4
 
5
+ require 'pdk/util/version'
6
+
5
7
  module PDK
6
8
  module Util
7
9
  # Searches upwards from current working directory for the given target file.
8
10
  #
9
11
  # @param target [String] Name of file to search for.
12
+ # @param start_dir [String] Directory to start searching from, defaults to Dir.pwd
10
13
  #
11
14
  # @return [String, nil] Fully qualified path to the given target file if found,
12
15
  # nil if the target file could not be found.
13
- def find_upwards(target)
16
+ def find_upwards(target, start_dir = nil)
14
17
  previous = nil
15
- current = File.expand_path(Dir.pwd)
18
+ current = File.expand_path(start_dir || Dir.pwd)
16
19
 
17
20
  until !File.directory?(current) || current == previous
18
21
  filename = File.join(current, target)
@@ -50,17 +53,34 @@ module PDK
50
53
  end
51
54
  module_function :canonical_path
52
55
 
56
+ def package_install?
57
+ !PDK::Util::Version.version_file.nil?
58
+ end
59
+ module_function :package_install?
60
+
61
+ def gem_install?
62
+ !package_install?
63
+ end
64
+ module_function :gem_install?
65
+
66
+ def pdk_package_basedir
67
+ raise PDK::CLI::FatalError, _('Package basedir requested for non-package install') unless package_install?
68
+
69
+ File.dirname(PDK::Util::Version.version_file)
70
+ end
71
+ module_function :pdk_package_basedir
72
+
53
73
  # Returns the fully qualified path to a per-user PDK cachedir.
54
74
  #
55
75
  # @return [String] Fully qualified path to per-user PDK cachedir.
56
76
  def cachedir
57
- basedir = if Gem.win_platform?
58
- ENV['LOCALAPPDATA']
59
- else
60
- Dir.home
61
- end
62
-
63
- File.join(basedir, '.pdk', 'cache')
77
+ if package_install?
78
+ File.join(pdk_package_basedir, 'share', 'cache')
79
+ elsif Gem.win_platform?
80
+ File.join(ENV['LOCALAPPDATA'], 'PDK', 'cache')
81
+ else
82
+ File.join(Dir.home, '.pdk', 'cache')
83
+ end
64
84
  end
65
85
  module_function :cachedir
66
86
 
@@ -62,7 +62,10 @@ module PDK
62
62
  end
63
63
 
64
64
  def installed?
65
- command = bundle_command('check', "--gemfile=#{gemfile}", "--path=#{bundle_cachedir}").tap do |c|
65
+ argv = ['check', "--gemfile=#{gemfile}"]
66
+ argv << "--path=#{bundle_cachedir}" if PDK::Util.gem_install?
67
+
68
+ command = bundle_command(*argv).tap do |c|
66
69
  c.add_spinner(_('Checking for missing Gemfile dependencies'))
67
70
  end
68
71
 
@@ -92,7 +95,10 @@ module PDK
92
95
  end
93
96
 
94
97
  def install!
95
- command = bundle_command('install', "--gemfile=#{gemfile}", "--path=#{bundle_cachedir}").tap do |c|
98
+ argv = ['install', "--gemfile=#{gemfile}"]
99
+ argv << "--path=#{bundle_cachedir}" if PDK::Util.gem_install?
100
+
101
+ command = bundle_command(*argv).tap do |c|
96
102
  c.add_spinner(_('Installing missing Gemfile dependencies'))
97
103
  end
98
104
 
@@ -107,7 +113,10 @@ module PDK
107
113
  end
108
114
 
109
115
  def binstubs!(gems)
110
- command = bundle_command('binstubs', gems.join(' '), '--force')
116
+ binstub_dir = File.join(File.dirname(gemfile), 'bin')
117
+ return true if gems.all? { |gem| File.file?(File.join(binstub_dir, gem)) }
118
+
119
+ command = bundle_command('binstubs', *gems, '--force')
111
120
 
112
121
  result = command.execute!
113
122
 
@@ -137,7 +146,7 @@ module PDK
137
146
  end
138
147
 
139
148
  def bundle_cachedir
140
- @bundle_cachedir ||= File.join(PDK::Util.cachedir, 'bundler')
149
+ @bundle_cachedir ||= File.join(PDK::Util.cachedir)
141
150
  end
142
151
  end
143
152
  end
@@ -14,11 +14,9 @@ module PDK
14
14
  end
15
15
 
16
16
  def self.pkg_sha
17
- version_file = File.join(File.expand_path('../../..', File.dirname(__FILE__)), 'VERSION')
18
-
19
- if File.exist? version_file
17
+ if version_file && File.exist?(version_file)
20
18
  ver = File.read(version_file)
21
- sha = ver.strip.split('.')[-1] unless ver.nil?
19
+ sha = ver.strip.split('.')[5] unless ver.nil?
22
20
  end
23
21
 
24
22
  sha
@@ -29,6 +27,10 @@ module PDK
29
27
 
30
28
  ref_result[:stdout].strip if ref_result[:exit_code].zero?
31
29
  end
30
+
31
+ def self.version_file
32
+ PDK::Util.find_upwards('PDK_VERSION', File.dirname(__FILE__))
33
+ end
32
34
  end
33
35
  end
34
36
  end
@@ -1,11 +1,11 @@
1
- require 'pdk/validators/metadata'
1
+ require 'pdk/validators/metadata_validator'
2
2
  require 'pdk/validators/puppet_validator'
3
3
  require 'pdk/validators/ruby_validator'
4
4
 
5
5
  module PDK
6
6
  module Validate
7
7
  def self.validators
8
- @validators ||= [Metadata, PuppetValidator, RubyValidator].freeze
8
+ @validators ||= [MetadataValidator, PuppetValidator, RubyValidator].freeze
9
9
  end
10
10
  end
11
11
  end
@@ -4,6 +4,14 @@ require 'pdk/cli/exec'
4
4
  module PDK
5
5
  module Validate
6
6
  class BaseValidator
7
+ # Controls how many times the validator is invoked.
8
+ #
9
+ # :once - The validator will be invoked once and passed all the
10
+ # targets.
11
+ # :per_target - The validator will be invoked for each target
12
+ # separately.
13
+ INVOKE_STYLE = :once
14
+
7
15
  def self.cmd_path
8
16
  File.join(PDK::Util.module_root, 'bin', cmd)
9
17
  end
@@ -34,7 +42,7 @@ module PDK
34
42
  targets
35
43
  end
36
44
 
37
- def self.spinner_text
45
+ def self.spinner_text(_targets = nil)
38
46
  _('Invoking %{cmd}') % { cmd: cmd }
39
47
  end
40
48
 
@@ -44,19 +52,31 @@ module PDK
44
52
  return 0 if targets.empty?
45
53
 
46
54
  PDK::Util::Bundler.ensure_binstubs!(cmd)
47
- cmd_argv = parse_options(options, targets).unshift(cmd_path)
48
- cmd_argv.unshift('ruby', '-W0') if Gem.win_platform?
49
55
 
50
- command = PDK::CLI::Exec::Command.new(*cmd_argv).tap do |c|
51
- c.context = :module
52
- c.add_spinner(spinner_text)
53
- end
56
+ # If invoking :per_target, split the targets array into an array of
57
+ # single element arrays (one per target). If invoking :once, wrap the
58
+ # targets array in another array. This is so we can loop through the
59
+ # invokes with the same logic, regardless of which invoke style is
60
+ # needed.
61
+ targets = (self::INVOKE_STYLE == :per_target) ? targets.combination(1).to_a : Array[targets]
62
+ exit_codes = []
63
+
64
+ targets.each do |invokation_targets|
65
+ cmd_argv = parse_options(options, invokation_targets).unshift(cmd_path)
66
+ cmd_argv.unshift('ruby', '-W0') if Gem.win_platform?
54
67
 
55
- result = command.execute!
68
+ command = PDK::CLI::Exec::Command.new(*cmd_argv).tap do |c|
69
+ c.context = :module
70
+ c.add_spinner(spinner_text(invokation_targets))
71
+ end
72
+
73
+ result = command.execute!
74
+ exit_codes << result[:exit_code]
56
75
 
57
- parse_output(report, result, targets)
76
+ parse_output(report, result, invokation_targets)
77
+ end
58
78
 
59
- result[:exit_code]
79
+ exit_codes.sort.last
60
80
  end
61
81
  end
62
82
  end
@@ -2,24 +2,31 @@ require 'pdk'
2
2
  require 'pdk/cli/exec'
3
3
  require 'pdk/validators/base_validator'
4
4
  require 'pdk/util/bundler'
5
+ require 'pathname'
5
6
 
6
7
  module PDK
7
8
  module Validate
8
- class Metadata < BaseValidator
9
+ class MetadataJSONLint < BaseValidator
10
+ # Validate each metadata file separately, as metadata-json-lint does not
11
+ # support multiple targets.
12
+ INVOKE_STYLE = :per_target
13
+
9
14
  def self.name
10
- 'metadata'
15
+ 'metadata-json-lint'
11
16
  end
12
17
 
13
18
  def self.cmd
14
19
  'metadata-json-lint'
15
20
  end
16
21
 
17
- def self.spinner_text
18
- _('Checking metadata.json')
22
+ def self.spinner_text(targets = nil)
23
+ _('Checking metadata (%{targets})') % {
24
+ targets: targets.map { |t| Pathname.new(t).absolute? ? Pathname.new(t).relative_path_from(Pathname.pwd) : t }.join(' '),
25
+ }
19
26
  end
20
27
 
21
- def self.parse_targets(_options)
22
- [File.join(PDK::Util.module_root, 'metadata.json')]
28
+ def self.pattern
29
+ 'metadata.json'
23
30
  end
24
31
 
25
32
  def self.parse_options(_options, targets)
@@ -28,16 +35,18 @@ module PDK
28
35
  cmd_options.concat(targets)
29
36
  end
30
37
 
31
- def self.parse_output(report, result, _targets)
38
+ def self.parse_output(report, result, targets)
32
39
  begin
33
40
  json_data = JSON.parse(result[:stdout])
34
41
  rescue JSON::ParserError
35
42
  json_data = []
36
43
  end
37
44
 
45
+ raise ArgumentError, 'More that 1 target provided to PDK::Validate::MetadataJSONLint' if targets.count > 1
46
+
38
47
  if json_data.empty?
39
48
  report.add_event(
40
- file: 'metadata.json',
49
+ file: targets.first,
41
50
  source: cmd,
42
51
  state: :passed,
43
52
  severity: :ok,
@@ -52,7 +61,7 @@ module PDK
52
61
  event_type = type[%r{\A(.+?)s?\Z}, 1]
53
62
 
54
63
  report.add_event(
55
- file: 'metadata.json',
64
+ file: targets.first,
56
65
  source: cmd,
57
66
  message: offense['msg'],
58
67
  test: offense['check'],
@@ -0,0 +1,89 @@
1
+ require 'pdk'
2
+ require 'pdk/cli/exec'
3
+ require 'pdk/validators/base_validator'
4
+ require 'pathname'
5
+
6
+ module PDK
7
+ module Validate
8
+ class MetadataSyntax < BaseValidator
9
+ def self.name
10
+ 'metadata-syntax'
11
+ end
12
+
13
+ def self.pattern
14
+ 'metadata.json'
15
+ end
16
+
17
+ def self.invoke(report, options = {})
18
+ targets = parse_targets(options)
19
+
20
+ return 0 if targets.empty?
21
+
22
+ return_val = 0
23
+
24
+ # The pure ruby JSON parser gives much nicer parse error messages than
25
+ # the C extension at the cost of slightly slower parsing. We require it
26
+ # here and restore the C extension at the end of the method (if it was
27
+ # being used).
28
+ require 'json/pure'
29
+ JSON.parser = JSON::Pure::Parser
30
+
31
+ targets.each do |target|
32
+ unless File.file?(target)
33
+ report.add_event(
34
+ file: target,
35
+ source: name,
36
+ state: :failure,
37
+ severity: 'error',
38
+ message: _('not a file'),
39
+ )
40
+ return_val = 1
41
+ next
42
+ end
43
+
44
+ unless File.readable?(target)
45
+ report.add_event(
46
+ file: target,
47
+ source: name,
48
+ state: :failure,
49
+ severity: 'error',
50
+ message: _('could not be read'),
51
+ )
52
+ return_val = 1
53
+ next
54
+ end
55
+
56
+ begin
57
+ JSON.parse(File.read(target))
58
+
59
+ report.add_event(
60
+ file: target,
61
+ source: name,
62
+ state: :passed,
63
+ severity: 'ok',
64
+ )
65
+ rescue JSON::ParserError => e
66
+ # Because the message contains a raw segment of the file, we use
67
+ # String#dump here to unescape any escape characters like newlines.
68
+ # We then strip out the surrounding quotes and the exclaimation
69
+ # point that json_pure likes to put in exception messages.
70
+ sane_message = e.message.dump[%r{\A"(.+?)!?"\Z}, 1]
71
+
72
+ report.add_event(
73
+ file: target,
74
+ source: name,
75
+ state: :failure,
76
+ severity: 'error',
77
+ message: sane_message,
78
+ )
79
+ return_val = 1
80
+ end
81
+ end
82
+
83
+ return_val
84
+ ensure
85
+ JSON.parser = JSON::Ext::Parser if defined?(JSON::Ext::Parser)
86
+ end
87
+ end
88
+ end
89
+ end
@@ -0,0 +1,30 @@
1
+ require 'pdk'
2
+ require 'pdk/cli/exec'
3
+ require 'pdk/validators/base_validator'
4
+ require 'pdk/validators/metadata/metadata_json_lint'
5
+ require 'pdk/validators/metadata/metadata_syntax'
6
+
7
+ module PDK
8
+ module Validate
9
+ class MetadataValidator < BaseValidator
10
+ def self.name
11
+ 'metadata'
12
+ end
13
+
14
+ def self.metadata_validators
15
+ [MetadataSyntax, MetadataJSONLint]
16
+ end
17
+
18
+ def self.invoke(report, options = {})
19
+ exit_code = 0
20
+
21
+ metadata_validators.each do |validator|
22
+ exit_code = validator.invoke(report, options)
23
+ break if exit_code != 0
24
+ end
25
+
26
+ exit_code
27
+ end
28
+ end
29
+ end
30
+ end
@@ -18,7 +18,7 @@ module PDK
18
18
  '**/*.pp'
19
19
  end
20
20
 
21
- def self.spinner_text
21
+ def self.spinner_text(_targets = nil)
22
22
  _('Checking Puppet manifest style')
23
23
  end
24
24
 
@@ -32,7 +32,7 @@ module PDK
32
32
 
33
33
  def self.parse_output(report, result, targets)
34
34
  begin
35
- json_data = JSON.parse(result[:stdout])
35
+ json_data = JSON.parse(result[:stdout]).flatten
36
36
  rescue JSON::ParserError
37
37
  json_data = []
38
38
  end
@@ -17,7 +17,7 @@ module PDK
17
17
  '**/**.pp'
18
18
  end
19
19
 
20
- def self.spinner_text
20
+ def self.spinner_text(_targets = nil)
21
21
  _('Checking Puppet manifest syntax')
22
22
  end
23
23
 
@@ -16,7 +16,7 @@ module PDK
16
16
  'rubocop'
17
17
  end
18
18
 
19
- def self.spinner_text
19
+ def self.spinner_text(_targets = nil)
20
20
  _('Checking Ruby code style')
21
21
  end
22
22
 
@@ -31,10 +31,12 @@ module PDK
31
31
  end
32
32
 
33
33
  def self.parse_output(report, result, _targets)
34
+ return if result[:stdout].empty?
35
+
34
36
  begin
35
37
  json_data = JSON.parse(result[:stdout])
36
38
  rescue JSON::ParserError
37
- json_data = []
39
+ json_data = {}
38
40
  end
39
41
 
40
42
  return unless json_data.key?('files')
@@ -1,3 +1,3 @@
1
1
  module PDK
2
- VERSION = '0.3.0'.freeze
2
+ VERSION = '0.4.0'.freeze
3
3
  end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: pdk
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.3.0
4
+ version: 0.4.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Puppet, Inc.
8
8
  autorequire:
9
9
  bindir: exe
10
10
  cert_chain: []
11
- date: 2017-06-29 00:00:00.000000000 Z
11
+ date: 2017-07-14 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: bundler
@@ -94,6 +94,20 @@ dependencies:
94
94
  - - "~>"
95
95
  - !ruby/object:Gem::Version
96
96
  version: '0.12'
97
+ - !ruby/object:Gem::Dependency
98
+ name: json_pure
99
+ requirement: !ruby/object:Gem::Requirement
100
+ requirements:
101
+ - - "~>"
102
+ - !ruby/object:Gem::Version
103
+ version: 2.1.0
104
+ type: :runtime
105
+ prerelease: false
106
+ version_requirements: !ruby/object:Gem::Requirement
107
+ requirements:
108
+ - - "~>"
109
+ - !ruby/object:Gem::Version
110
+ version: 2.1.0
97
111
  - !ruby/object:Gem::Dependency
98
112
  name: deep_merge
99
113
  requirement: !ruby/object:Gem::Requirement
@@ -121,6 +135,7 @@ files:
121
135
  - README.md
122
136
  - exe/pdk
123
137
  - lib/pdk.rb
138
+ - lib/pdk/answer_file.rb
124
139
  - lib/pdk/cli.rb
125
140
  - lib/pdk/cli/errors.rb
126
141
  - lib/pdk/cli/exec.rb
@@ -151,7 +166,9 @@ files:
151
166
  - lib/pdk/util/version.rb
152
167
  - lib/pdk/validate.rb
153
168
  - lib/pdk/validators/base_validator.rb
154
- - lib/pdk/validators/metadata.rb
169
+ - lib/pdk/validators/metadata/metadata_json_lint.rb
170
+ - lib/pdk/validators/metadata/metadata_syntax.rb
171
+ - lib/pdk/validators/metadata_validator.rb
155
172
  - lib/pdk/validators/puppet/puppet_lint.rb
156
173
  - lib/pdk/validators/puppet/puppet_syntax.rb
157
174
  - lib/pdk/validators/puppet_validator.rb
@@ -184,7 +201,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
184
201
  version: '0'
185
202
  requirements: []
186
203
  rubyforge_project:
187
- rubygems_version: 2.4.8
204
+ rubygems_version: 2.6.12
188
205
  signing_key:
189
206
  specification_version: 4
190
207
  summary: A key part of the Puppet Development Kit, the shortest path to better modules