pdk 1.16.0 → 1.17.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.
Files changed (68) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +24 -0
  3. data/lib/pdk.rb +25 -18
  4. data/lib/pdk/answer_file.rb +2 -93
  5. data/lib/pdk/cli.rb +1 -5
  6. data/lib/pdk/cli/config.rb +3 -1
  7. data/lib/pdk/cli/config/get.rb +3 -1
  8. data/lib/pdk/cli/convert.rb +1 -1
  9. data/lib/pdk/cli/exec/command.rb +13 -0
  10. data/lib/pdk/cli/exec_group.rb +78 -43
  11. data/lib/pdk/cli/get.rb +20 -0
  12. data/lib/pdk/cli/get/config.rb +24 -0
  13. data/lib/pdk/cli/util.rb +6 -3
  14. data/lib/pdk/cli/validate.rb +26 -44
  15. data/lib/pdk/config.rb +178 -4
  16. data/lib/pdk/config/ini_file.rb +183 -0
  17. data/lib/pdk/config/ini_file_setting.rb +39 -0
  18. data/lib/pdk/config/namespace.rb +25 -5
  19. data/lib/pdk/config/setting.rb +3 -2
  20. data/lib/pdk/context.rb +96 -0
  21. data/lib/pdk/context/control_repo.rb +60 -0
  22. data/lib/pdk/context/module.rb +28 -0
  23. data/lib/pdk/context/none.rb +22 -0
  24. data/lib/pdk/control_repo.rb +40 -0
  25. data/lib/pdk/generate/module.rb +8 -12
  26. data/lib/pdk/module/release.rb +2 -8
  27. data/lib/pdk/util.rb +35 -5
  28. data/lib/pdk/util/bundler.rb +1 -0
  29. data/lib/pdk/util/changelog_generator.rb +6 -1
  30. data/lib/pdk/util/template_uri.rb +4 -3
  31. data/lib/pdk/validate.rb +72 -25
  32. data/lib/pdk/validate/external_command_validator.rb +208 -0
  33. data/lib/pdk/validate/internal_ruby_validator.rb +100 -0
  34. data/lib/pdk/validate/invokable_validator.rb +216 -0
  35. data/lib/pdk/validate/metadata/metadata_json_lint_validator.rb +86 -0
  36. data/lib/pdk/validate/metadata/metadata_syntax_validator.rb +78 -0
  37. data/lib/pdk/validate/metadata/metadata_validator_group.rb +20 -0
  38. data/lib/pdk/validate/puppet/puppet_epp_validator.rb +133 -0
  39. data/lib/pdk/validate/puppet/puppet_lint_validator.rb +66 -0
  40. data/lib/pdk/validate/puppet/puppet_syntax_validator.rb +137 -0
  41. data/lib/pdk/validate/puppet/puppet_validator_group.rb +21 -0
  42. data/lib/pdk/validate/ruby/ruby_rubocop_validator.rb +80 -0
  43. data/lib/pdk/validate/ruby/ruby_validator_group.rb +19 -0
  44. data/lib/pdk/validate/tasks/tasks_metadata_lint_validator.rb +88 -0
  45. data/lib/pdk/validate/tasks/tasks_name_validator.rb +50 -0
  46. data/lib/pdk/validate/tasks/tasks_validator_group.rb +20 -0
  47. data/lib/pdk/validate/validator.rb +111 -0
  48. data/lib/pdk/validate/validator_group.rb +103 -0
  49. data/lib/pdk/validate/yaml/yaml_syntax_validator.rb +95 -0
  50. data/lib/pdk/validate/yaml/yaml_validator_group.rb +19 -0
  51. data/lib/pdk/version.rb +1 -1
  52. data/locales/pdk.pot +161 -125
  53. metadata +29 -17
  54. data/lib/pdk/validate/base_validator.rb +0 -215
  55. data/lib/pdk/validate/metadata/metadata_json_lint.rb +0 -82
  56. data/lib/pdk/validate/metadata/metadata_syntax.rb +0 -111
  57. data/lib/pdk/validate/metadata_validator.rb +0 -26
  58. data/lib/pdk/validate/puppet/puppet_epp.rb +0 -135
  59. data/lib/pdk/validate/puppet/puppet_lint.rb +0 -64
  60. data/lib/pdk/validate/puppet/puppet_syntax.rb +0 -135
  61. data/lib/pdk/validate/puppet_validator.rb +0 -26
  62. data/lib/pdk/validate/ruby/rubocop.rb +0 -72
  63. data/lib/pdk/validate/ruby_validator.rb +0 -26
  64. data/lib/pdk/validate/tasks/metadata_lint.rb +0 -130
  65. data/lib/pdk/validate/tasks/name.rb +0 -90
  66. data/lib/pdk/validate/tasks_validator.rb +0 -29
  67. data/lib/pdk/validate/yaml/syntax.rb +0 -125
  68. data/lib/pdk/validate/yaml_validator.rb +0 -28
@@ -0,0 +1,66 @@
1
+ require 'pdk'
2
+
3
+ module PDK
4
+ module Validate
5
+ module Puppet
6
+ class PuppetLintValidator < ExternalCommandValidator
7
+ def name
8
+ 'puppet-lint'
9
+ end
10
+
11
+ def cmd
12
+ 'puppet-lint'
13
+ end
14
+
15
+ def pattern
16
+ contextual_pattern('**/*.pp')
17
+ end
18
+
19
+ def spinner_text_for_targets(_targets)
20
+ _('Checking Puppet manifest style (%{pattern}).') % { pattern: pattern.join(' ') }
21
+ end
22
+
23
+ def parse_options(targets)
24
+ cmd_options = ['--json', '--relative']
25
+
26
+ cmd_options << '--fix' if options[:auto_correct]
27
+
28
+ cmd_options.concat(targets)
29
+ end
30
+
31
+ def parse_output(report, result, targets)
32
+ begin
33
+ json_data = JSON.parse(result[:stdout]).flatten
34
+ rescue JSON::ParserError
35
+ raise PDK::Validate::ParseOutputError, result[:stdout]
36
+ end
37
+
38
+ # puppet-lint does not include files without problems in its JSON
39
+ # output, so we need to go through the list of targets and add passing
40
+ # events to the report for any target not listed in the JSON output.
41
+ targets.reject { |target| json_data.any? { |j| j['path'] == target } }.each do |target|
42
+ report.add_event(
43
+ file: target,
44
+ source: name,
45
+ severity: 'ok',
46
+ state: :passed,
47
+ )
48
+ end
49
+
50
+ json_data.each do |offense|
51
+ report.add_event(
52
+ file: offense['path'],
53
+ source: name,
54
+ line: offense['line'],
55
+ column: offense['column'],
56
+ message: offense['message'],
57
+ test: offense['check'],
58
+ severity: (offense['kind'] == 'fixed') ? 'corrected' : offense['kind'],
59
+ state: :failure,
60
+ )
61
+ end
62
+ end
63
+ end
64
+ end
65
+ end
66
+ end
@@ -0,0 +1,137 @@
1
+ require 'pdk'
2
+
3
+ module PDK
4
+ module Validate
5
+ module Puppet
6
+ class PuppetSyntaxValidator < ExternalCommandValidator
7
+ # In Puppet >= 5.3.4, the error context formatting was changed to facilitate localization
8
+ ERROR_CONTEXT = %r{(?:file:\s(?<file>.+?)|line:\s(?<line>.+?)|column:\s(?<column>.+?))}
9
+ # In Puppet < 5.3.3, the error context was formatted in these variations:
10
+ # - "at file_path:line_num:col_num"
11
+ # - "at file_path:line_num"
12
+ # - "at line line_num"
13
+ # - "in file_path"
14
+ ERROR_CONTEXT_LEGACY = %r{(?:at\sline\s(?<line>\d+)|at\s(?<file>.+?):(?<line>\d+):(?<column>\d+)|at\s(?<file>.+?):(?<line>\d+)|in\s(?<file>.+?))}
15
+
16
+ PUPPET_LOGGER_PREFIX = %r{^(debug|info|notice|warning|error|alert|critical):\s.+?$}i
17
+ PUPPET_SYNTAX_PATTERN = %r{^
18
+ (?<severity>.+?):\s
19
+ (?<message>.+?)
20
+ (?:
21
+ \s\(#{ERROR_CONTEXT}(,\s#{ERROR_CONTEXT})*\)| # attempt to match the new localisation friendly location
22
+ \s#{ERROR_CONTEXT_LEGACY}| # attempt to match the old " at file:line:column" location
23
+ $ # handle cases where the output has no location
24
+ )
25
+ $}x
26
+
27
+ def name
28
+ 'puppet-syntax'
29
+ end
30
+
31
+ def cmd
32
+ 'puppet'
33
+ end
34
+
35
+ def pattern
36
+ contextual_pattern('**/*.pp')
37
+ end
38
+
39
+ def pattern_ignore
40
+ contextual_pattern('plans/**/*.pp')
41
+ end
42
+
43
+ def spinner_text_for_targets(_targets)
44
+ _('Checking Puppet manifest syntax (%{pattern}).') % { pattern: pattern.join(' ') }
45
+ end
46
+
47
+ def parse_options(targets)
48
+ # Due to PDK-1266 we need to run `puppet parser validate` with an empty
49
+ # modulepath. On *nix, Ruby treats `/dev/null` as an empty directory
50
+ # however it doesn't do so with `NUL` on Windows. The workaround for
51
+ # this to ensure consistent behaviour is to create an empty temporary
52
+ # directory and use that as the modulepath.
53
+ ['parser', 'validate', '--config', null_file, '--modulepath', validate_tmpdir].concat(targets)
54
+ end
55
+
56
+ def invoke(report)
57
+ super
58
+ ensure
59
+ remove_validate_tmpdir
60
+ end
61
+
62
+ def validate_tmpdir
63
+ require 'tmpdir'
64
+
65
+ @validate_tmpdir ||= Dir.mktmpdir('puppet-parser-validate')
66
+ end
67
+
68
+ def remove_validate_tmpdir
69
+ return unless @validate_tmpdir
70
+ return unless PDK::Util::Filesystem.directory?(@validate_tmpdir)
71
+
72
+ PDK::Util::Filesystem.remove_entry_secure(@validate_tmpdir)
73
+ @validate_tmpdir = nil
74
+ end
75
+
76
+ def null_file
77
+ Gem.win_platform? ? 'NUL' : '/dev/null'
78
+ end
79
+
80
+ def parse_output(report, result, targets)
81
+ # Due to PUP-7504, we will have to programmatically construct the json
82
+ # object from the text output for now.
83
+ output = result[:stderr].split(%r{\r?\n}).reject { |entry| entry.empty? }
84
+
85
+ results_data = []
86
+ output.each do |offense|
87
+ offense_data = parse_offense(offense)
88
+ results_data << offense_data
89
+ end
90
+
91
+ # puppet parser validate does not include files without problems in its
92
+ # output, so we need to go through the list of targets and add passing
93
+ # events to the report for any target not listed in the output.
94
+ targets.reject { |target| results_data.any? { |j| j[:file] =~ %r{#{target}} } }.each do |target|
95
+ report.add_event(
96
+ file: target,
97
+ source: name,
98
+ severity: :ok,
99
+ state: :passed,
100
+ )
101
+ end
102
+
103
+ results_data.each do |offense|
104
+ report.add_event(offense)
105
+ end
106
+ end
107
+
108
+ def parse_offense(offense)
109
+ sanitize_console_output(offense)
110
+
111
+ offense_data = {
112
+ source: name,
113
+ state: :failure,
114
+ }
115
+
116
+ if offense.match(PUPPET_LOGGER_PREFIX)
117
+ attributes = offense.match(PUPPET_SYNTAX_PATTERN)
118
+
119
+ unless attributes.nil?
120
+ attributes.names.each do |name|
121
+ offense_data[name.to_sym] = attributes[name] unless attributes[name].nil?
122
+ end
123
+ end
124
+ else
125
+ offense_data[:message] = offense
126
+ end
127
+
128
+ offense_data
129
+ end
130
+
131
+ def sanitize_console_output(line)
132
+ line.gsub!(%r{\e\[([;\d]+)?m}, '')
133
+ end
134
+ end
135
+ end
136
+ end
137
+ end
@@ -0,0 +1,21 @@
1
+ require 'pdk'
2
+
3
+ module PDK
4
+ module Validate
5
+ module Puppet
6
+ class PuppetValidatorGroup < ValidatorGroup
7
+ def name
8
+ 'puppet'
9
+ end
10
+
11
+ def validators
12
+ [
13
+ PuppetSyntaxValidator,
14
+ PuppetLintValidator,
15
+ PuppetEPPValidator,
16
+ ].freeze
17
+ end
18
+ end
19
+ end
20
+ end
21
+ end
@@ -0,0 +1,80 @@
1
+ require 'pdk'
2
+
3
+ module PDK
4
+ module Validate
5
+ module Ruby
6
+ class RubyRubocopValidator < ExternalCommandValidator
7
+ def allow_empty_targets?
8
+ true
9
+ end
10
+
11
+ def name
12
+ 'rubocop'
13
+ end
14
+
15
+ def cmd
16
+ 'rubocop'
17
+ end
18
+
19
+ def pattern
20
+ if context.is_a?(PDK::Context::ControlRepo)
21
+ ['Puppetfile', '**/**.rb']
22
+ else
23
+ '**/**.rb'
24
+ end
25
+ end
26
+
27
+ def spinner_text_for_targets(_targets)
28
+ _('Checking Ruby code style (%{pattern}).') % { pattern: pattern }
29
+ end
30
+
31
+ def parse_options(targets)
32
+ cmd_options = ['--format', 'json']
33
+
34
+ if options[:auto_correct]
35
+ cmd_options << '--auto-correct'
36
+ end
37
+
38
+ cmd_options.concat(targets)
39
+ end
40
+
41
+ def parse_output(report, result, _targets)
42
+ return if result[:stdout].empty?
43
+
44
+ begin
45
+ json_data = JSON.parse(result[:stdout])
46
+ rescue JSON::ParserError
47
+ raise PDK::Validate::ParseOutputError, result[:stdout]
48
+ end
49
+
50
+ return unless json_data.key?('files')
51
+
52
+ json_data['files'].each do |file_info|
53
+ next unless file_info.key?('offenses')
54
+ result = {
55
+ file: file_info['path'],
56
+ source: 'rubocop',
57
+ }
58
+
59
+ if file_info['offenses'].empty?
60
+ report.add_event(result.merge(state: :passed, severity: :ok))
61
+ else
62
+ file_info['offenses'].each do |offense|
63
+ report.add_event(
64
+ result.merge(
65
+ line: offense['location']['line'],
66
+ column: offense['location']['column'],
67
+ message: offense['message'],
68
+ severity: offense['corrected'] ? 'corrected' : offense['severity'],
69
+ test: offense['cop_name'],
70
+ state: :failure,
71
+ ),
72
+ )
73
+ end
74
+ end
75
+ end
76
+ end
77
+ end
78
+ end
79
+ end
80
+ end
@@ -0,0 +1,19 @@
1
+ require 'pdk'
2
+
3
+ module PDK
4
+ module Validate
5
+ module Ruby
6
+ class RubyValidatorGroup < ValidatorGroup
7
+ def name
8
+ 'ruby'
9
+ end
10
+
11
+ def validators
12
+ [
13
+ RubyRubocopValidator,
14
+ ].freeze
15
+ end
16
+ end
17
+ end
18
+ end
19
+ end
@@ -0,0 +1,88 @@
1
+ require 'pdk'
2
+
3
+ module PDK
4
+ module Validate
5
+ module Tasks
6
+ class TasksMetadataLintValidator < InternalRubyValidator
7
+ FORGE_SCHEMA_URL = 'https://forgeapi.puppet.com/schemas/task.json'.freeze
8
+
9
+ def name
10
+ 'task-metadata-lint'
11
+ end
12
+
13
+ def pattern
14
+ contextual_pattern('tasks/*.json')
15
+ end
16
+
17
+ def spinner_text
18
+ _('Checking task metadata style (%{pattern}).') % {
19
+ pattern: pattern.join(' '),
20
+ }
21
+ end
22
+
23
+ def schema_file
24
+ require 'pdk/util/vendored_file'
25
+
26
+ schema = PDK::Util::VendoredFile.new('task.json', FORGE_SCHEMA_URL).read
27
+
28
+ JSON.parse(schema)
29
+ rescue PDK::Util::VendoredFile::DownloadError => e
30
+ raise PDK::CLI::FatalError, e.message
31
+ rescue JSON::ParserError
32
+ raise PDK::CLI::FatalError, _('Failed to parse Task Metadata Schema file.')
33
+ end
34
+
35
+ def validate_target(report, target)
36
+ unless PDK::Util::Filesystem.readable?(target)
37
+ report.add_event(
38
+ file: target,
39
+ source: name,
40
+ state: :failure,
41
+ severity: 'error',
42
+ message: _('Could not be read.'),
43
+ )
44
+ return 1
45
+ end
46
+
47
+ require 'json-schema'
48
+ begin
49
+ # Need to set the JSON Parser and State Generator to the Native one to be
50
+ # compatible with the multi_json adapter.
51
+ JSON.parser = JSON::Ext::Parser if defined?(JSON::Ext::Parser)
52
+ JSON.generator = JSON::Ext::Generator
53
+
54
+ begin
55
+ errors = JSON::Validator.fully_validate(schema_file, PDK::Util::Filesystem.read_file(target)) || []
56
+ rescue JSON::Schema::SchemaError => e
57
+ raise PDK::CLI::FatalError, _('Unable to validate Task Metadata. %{error}.') % { error: e.message }
58
+ end
59
+
60
+ if errors.empty?
61
+ report.add_event(
62
+ file: target,
63
+ source: name,
64
+ state: :passed,
65
+ severity: 'ok',
66
+ )
67
+ return 0
68
+ else
69
+ errors.each do |error|
70
+ # strip off the trailing parts that aren't relevant
71
+ error = error.split('in schema').first if error.include? 'in schema'
72
+
73
+ report.add_event(
74
+ file: target,
75
+ source: name,
76
+ state: :failure,
77
+ severity: 'error',
78
+ message: error,
79
+ )
80
+ end
81
+ return 1
82
+ end
83
+ end
84
+ end
85
+ end
86
+ end
87
+ end
88
+ end
@@ -0,0 +1,50 @@
1
+ require 'pdk'
2
+
3
+ module PDK
4
+ module Validate
5
+ module Tasks
6
+ class TasksNameValidator < InternalRubyValidator
7
+ INVALID_TASK_MSG = _(
8
+ 'Invalid task name. Task names must start with a lowercase letter ' \
9
+ 'and can only contain lowercase letters, numbers, and underscores.',
10
+ )
11
+
12
+ def name
13
+ 'task-name'
14
+ end
15
+
16
+ def pattern
17
+ contextual_pattern('tasks/**/*')
18
+ end
19
+
20
+ def spinner_text
21
+ _('Checking task names (%{pattern}).') % {
22
+ pattern: pattern.join(' '),
23
+ }
24
+ end
25
+
26
+ def validate_target(report, target)
27
+ task_name = File.basename(target, File.extname(target))
28
+ if PDK::CLI::Util::OptionValidator.valid_task_name?(task_name)
29
+ report.add_event(
30
+ file: target,
31
+ source: name,
32
+ state: :passed,
33
+ severity: 'ok',
34
+ )
35
+ return 0
36
+ else
37
+ report.add_event(
38
+ file: target,
39
+ source: name,
40
+ state: :failure,
41
+ severity: 'error',
42
+ message: INVALID_TASK_MSG,
43
+ )
44
+ return 1
45
+ end
46
+ end
47
+ end
48
+ end
49
+ end
50
+ end