pdk 1.7.1 → 1.8.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.
@@ -0,0 +1,12 @@
1
+ module PDK
2
+ module Util
3
+ module Filesystem
4
+ def write_file(path, content)
5
+ raise ArgumentError unless path.is_a?(String) || path.respond_to?(:to_path)
6
+
7
+ File.open(path, 'wb') { |f| f.write(content.encode(universal_newline: true)) }
8
+ end
9
+ module_function :write_file
10
+ end
11
+ end
12
+ end
@@ -42,7 +42,14 @@ module PDK
42
42
  end
43
43
 
44
44
  def fetch_puppet_dev
45
+ # Check if the source is cloned and is a readable git repo
45
46
  unless PDK::Util::Git.remote_repo? puppet_dev_path
47
+ # Check if the path has something in it already. Delete it and prepare for clone if so.
48
+ if File.exist? puppet_dev_path
49
+ File.delete(puppet_dev_path) if File.file? puppet_dev_path
50
+ FileUtils.rm_rf(puppet_dev_path) if File.directory? puppet_dev_path
51
+ end
52
+
46
53
  FileUtils.mkdir_p puppet_dev_path
47
54
  clone_result = PDK::Util::Git.git('clone', DEFAULT_PUPPET_DEV_URL, puppet_dev_path)
48
55
  return if clone_result[:exit_code].zero?
@@ -52,12 +59,22 @@ module PDK
52
59
  raise PDK::CLI::FatalError, _("Unable to clone git repository at '%{repo}'.") % { repo: DEFAULT_PUPPET_DEV_URL }
53
60
  end
54
61
 
55
- fetch_result = PDK::Util::Git.git('--git-dir', File.join(puppet_dev_path, '.git'), 'pull', '--ff-only')
56
- return if fetch_result[:exit_code].zero?
62
+ # Fetch Updates from remote repository
63
+ fetch_result = PDK::Util::Git.git('-C', puppet_dev_path, 'fetch', 'origin')
64
+
65
+ unless fetch_result[:exit_code].zero?
66
+ PDK.logger.error fetch_result[:stdout]
67
+ PDK.logger.error fetch_result[:stderr]
68
+ raise PDK::CLI::FatalError, _("Unable to fetch from git remote at '%{repo}'.") % { repo: DEFAULT_PUPPET_DEV_URL }
69
+ end
70
+
71
+ # Reset local repo to latest
72
+ reset_result = PDK::Util::Git.git('-C', puppet_dev_path, 'reset', '--hard', 'origin/master')
73
+ return if reset_result[:exit_code].zero?
57
74
 
58
- PDK.logger.error fetch_result[:stdout]
59
- PDK.logger.error fetch_result[:stderr]
60
- raise PDK::CLI::FatalError, _("Unable to pull updates for git repository at '%{cachedir}'.") % { repo: puppet_dev_path }
75
+ PDK.logger.error reset_result[:stdout]
76
+ PDK.logger.error reset_result[:stderr]
77
+ raise PDK::CLI::FatalError, _("Unable to update git repository at '%{cachedir}'.") % { repo: puppet_dev_path }
61
78
  end
62
79
 
63
80
  def find_gem_for(version_str)
@@ -2,6 +2,7 @@ require 'pdk/util'
2
2
  require 'net/https'
3
3
  require 'openssl'
4
4
  require 'fileutils'
5
+ require 'pdk/util/filesystem'
5
6
 
6
7
  module PDK
7
8
  module Util
@@ -22,6 +23,8 @@ module PDK
22
23
  attr_reader :file_name
23
24
  attr_reader :url
24
25
 
26
+ include PDK::Util::Filesystem
27
+
25
28
  def initialize(file_name, url)
26
29
  @file_name = file_name
27
30
  @url = url
@@ -36,9 +39,7 @@ module PDK
36
39
  # TODO: should only write if it's valid JSON
37
40
  # TODO: need a way to invalidate if out of date
38
41
  FileUtils.mkdir_p(File.dirname(gem_vendored_path))
39
- File.open(gem_vendored_path, 'w') do |fd|
40
- fd.write(content)
41
- end
42
+ write_file(gem_vendored_path, content)
42
43
  content
43
44
  end
44
45
 
@@ -1,11 +1,12 @@
1
1
  require 'pdk/validate/metadata_validator'
2
2
  require 'pdk/validate/puppet_validator'
3
3
  require 'pdk/validate/ruby_validator'
4
+ require 'pdk/validate/tasks_validator'
4
5
 
5
6
  module PDK
6
7
  module Validate
7
8
  def self.validators
8
- @validators ||= [MetadataValidator, PuppetValidator, RubyValidator].freeze
9
+ @validators ||= [MetadataValidator, PuppetValidator, RubyValidator, TasksValidator].freeze
9
10
  end
10
11
 
11
12
  class ParseOutputError < StandardError; end
@@ -1,5 +1,6 @@
1
1
  require 'pdk'
2
2
  require 'pdk/cli/exec'
3
+ require 'pdk/module'
3
4
 
4
5
  module PDK
5
6
  module Validate
@@ -12,6 +13,14 @@ module PDK
12
13
  # separately.
13
14
  INVOKE_STYLE = :once
14
15
 
16
+ # Controls how the validator behaves if not passed any targets.
17
+ #
18
+ # true - PDK will not pass the globbed targets to the validator command
19
+ # and it will instead rely on the underlying tool to find its
20
+ # own default targets.
21
+ # false - PDK will pass the globbed targets to the validator command.
22
+ ALLOW_EMPTY_TARGETS = false
23
+
15
24
  def self.cmd_path
16
25
  File.join(PDK::Util.module_root, 'bin', cmd)
17
26
  end
@@ -30,13 +39,9 @@ module PDK
30
39
  def self.parse_targets(options)
31
40
  # If no targets are specified, then we will run validations from the
32
41
  # base module directory.
33
- targets = if options[:targets].nil? || options[:targets].empty?
34
- [PDK::Util.module_root]
35
- else
36
- options[:targets]
37
- end
38
42
 
39
- fixtures_pattern = File.join('**', 'spec', 'fixtures', '**', '*')
43
+ targets = options.fetch(:targets, []).empty? ? [PDK::Util.module_root] : options[:targets]
44
+
40
45
  targets.map! { |r| r.gsub(File::ALT_SEPARATOR, File::SEPARATOR) } if File::ALT_SEPARATOR
41
46
  skipped = []
42
47
  invalid = []
@@ -45,14 +50,16 @@ module PDK
45
50
  if File.directory?(target)
46
51
  target_root = PDK::Util.module_root
47
52
  pattern_glob = Array(pattern).map { |p| Dir.glob(File.join(target_root, p)) }
48
- pattern_glob = pattern_glob.flatten.reject { |file| File.fnmatch(fixtures_pattern, file) }
49
53
 
50
- target_list = pattern_glob.map do |file|
54
+ target_list = pattern_glob.flatten.map do |file|
51
55
  if File.fnmatch(File.join(File.expand_path(target), '*'), file)
52
56
  Pathname.new(file).relative_path_from(Pathname.new(PDK::Util.module_root)).to_s
53
57
  end
54
58
  end
55
59
 
60
+ ignore_list = ignore_pathspec
61
+ target_list = target_list.reject { |file| ignore_list.match(file) }
62
+
56
63
  skipped << target if target_list.flatten.empty?
57
64
  target_list
58
65
  elsif File.file?(target)
@@ -75,6 +82,18 @@ module PDK
75
82
  [matched, skipped, invalid]
76
83
  end
77
84
 
85
+ def self.ignore_pathspec
86
+ ignore_pathspec = PDK::Module.default_ignored_pathspec.dup
87
+
88
+ if respond_to?(:pattern_ignore)
89
+ Array(pattern_ignore).each do |pattern|
90
+ ignore_pathspec.add(pattern)
91
+ end
92
+ end
93
+
94
+ ignore_pathspec
95
+ end
96
+
78
97
  def self.parse_options(_options, targets)
79
98
  targets
80
99
  end
@@ -109,6 +128,10 @@ module PDK
109
128
  end
110
129
  end
111
130
 
131
+ def self.allow_empty_targets?
132
+ self::ALLOW_EMPTY_TARGETS == true
133
+ end
134
+
112
135
  def self.invoke(report, options = {})
113
136
  targets, skipped, invalid = parse_targets(options)
114
137
 
@@ -132,6 +155,10 @@ module PDK
132
155
  options[:split_exec] = PDK::CLI::ExecGroup.new(spinner_text(targets), parallel: false)
133
156
  end
134
157
 
158
+ if options.fetch(:targets, []).empty? && allow_empty_targets?
159
+ targets = [[]]
160
+ end
161
+
135
162
  exit_codes = []
136
163
 
137
164
  targets.each do |invokation_targets|
@@ -140,6 +167,7 @@ module PDK
140
167
 
141
168
  command = PDK::CLI::Exec::Command.new(*cmd_argv).tap do |c|
142
169
  c.context = :module
170
+ c.environment = { 'PUPPET_GEM_VERSION' => options[:puppet] } if options[:puppet]
143
171
  unless options[:split_exec]
144
172
  exec_group = options[:exec_group]
145
173
  if exec_group
@@ -3,7 +3,6 @@ require 'pdk/cli/exec'
3
3
  require 'pdk/validate/base_validator'
4
4
  require 'pdk/validate/metadata/metadata_json_lint'
5
5
  require 'pdk/validate/metadata/metadata_syntax'
6
- require 'pdk/validate/metadata/task_metadata_lint'
7
6
 
8
7
  module PDK
9
8
  module Validate
@@ -13,7 +12,7 @@ module PDK
13
12
  end
14
13
 
15
14
  def self.metadata_validators
16
- [MetadataSyntax, MetadataJSONLint, TaskMetadataLint]
15
+ [MetadataSyntax, MetadataJSONLint]
17
16
  end
18
17
 
19
18
  def self.invoke(report, options = {})
@@ -37,6 +37,10 @@ module PDK
37
37
  '**/**.pp'
38
38
  end
39
39
 
40
+ def self.pattern_ignore
41
+ '/plans/**/*.pp'
42
+ end
43
+
40
44
  def self.spinner_text(_targets = nil)
41
45
  _('Checking Puppet manifest syntax (%{pattern}).') % { pattern: pattern }
42
46
  end
@@ -8,6 +8,8 @@ require 'pdk/validate/ruby_validator'
8
8
  module PDK
9
9
  module Validate
10
10
  class Rubocop < BaseValidator
11
+ ALLOW_EMPTY_TARGETS = true
12
+
11
13
  def self.name
12
14
  'rubocop'
13
15
  end
@@ -0,0 +1,126 @@
1
+ require 'pdk'
2
+ require 'pdk/cli/exec'
3
+ require 'pdk/validate/base_validator'
4
+ require 'pdk/util'
5
+ require 'pathname'
6
+ require 'json-schema'
7
+
8
+ module PDK
9
+ module Validate
10
+ class Tasks
11
+ class MetadataLint < BaseValidator
12
+ FORGE_SCHEMA_URL = 'https://forgeapi.puppet.com/schemas/task.json'.freeze
13
+
14
+ def self.name
15
+ 'task-metadata-lint'
16
+ end
17
+
18
+ def self.pattern
19
+ 'tasks/*.json'
20
+ end
21
+
22
+ def self.spinner_text(_targets = [])
23
+ _('Checking task metadata style (%{targets}).') % {
24
+ targets: pattern,
25
+ }
26
+ end
27
+
28
+ def self.create_spinner(targets = [], options = {})
29
+ return unless PDK::CLI::Util.interactive?
30
+ options = options.merge(PDK::CLI::Util.spinner_opts_for_platform)
31
+
32
+ exec_group = options[:exec_group]
33
+ @spinner = if exec_group
34
+ exec_group.add_spinner(spinner_text(targets), options)
35
+ else
36
+ TTY::Spinner.new("[:spinner] #{spinner_text(targets)}", options)
37
+ end
38
+ @spinner.auto_spin
39
+ end
40
+
41
+ def self.stop_spinner(exit_code)
42
+ if exit_code.zero? && @spinner
43
+ @spinner.success
44
+ elsif @spinner
45
+ @spinner.error
46
+ end
47
+ end
48
+
49
+ def self.schema_file
50
+ schema = PDK::Util::VendoredFile.new('task.json', FORGE_SCHEMA_URL).read
51
+
52
+ JSON.parse(schema)
53
+ rescue PDK::Util::VendoredFile::DownloadError => e
54
+ raise PDK::CLI::FatalError, e.message
55
+ rescue JSON::ParserError
56
+ raise PDK::CLI::FatalError, _('Failed to parse Task Metadata Schema file.')
57
+ end
58
+
59
+ def self.invoke(report, options = {})
60
+ targets, skipped, invalid = parse_targets(options)
61
+
62
+ process_skipped(report, skipped)
63
+ process_invalid(report, invalid)
64
+
65
+ return 0 if targets.empty?
66
+
67
+ return_val = 0
68
+ create_spinner(targets, options)
69
+
70
+ targets.each do |target|
71
+ unless File.readable?(target)
72
+ report.add_event(
73
+ file: target,
74
+ source: name,
75
+ state: :failure,
76
+ severity: 'error',
77
+ message: _('Could not be read.'),
78
+ )
79
+ return_val = 1
80
+ next
81
+ end
82
+
83
+ begin
84
+ # Need to set the JSON Parser and State Generator to the Native one to be
85
+ # compatible with the multi_json adapter.
86
+ JSON.parser = JSON::Ext::Parser if defined?(JSON::Ext::Parser)
87
+ JSON.generator = JSON::Ext::Generator
88
+
89
+ begin
90
+ errors = JSON::Validator.fully_validate(schema_file, File.read(target)) || []
91
+ rescue JSON::Schema::SchemaError => e
92
+ raise PDK::CLI::FatalError, _('Unable to validate Task Metadata. %{error}.') % { error: e.message }
93
+ end
94
+
95
+ if errors.empty?
96
+ report.add_event(
97
+ file: target,
98
+ source: name,
99
+ state: :passed,
100
+ severity: 'ok',
101
+ )
102
+ else
103
+ errors.each do |error|
104
+ # strip off the trailing parts that aren't relevant
105
+ error = error.split('in schema').first if error.include? 'in schema'
106
+
107
+ report.add_event(
108
+ file: target,
109
+ source: name,
110
+ state: :failure,
111
+ severity: 'error',
112
+ message: error,
113
+ )
114
+ end
115
+ return_val = 1
116
+ end
117
+ end
118
+ end
119
+
120
+ stop_spinner(return_val)
121
+ return_val
122
+ end
123
+ end
124
+ end
125
+ end
126
+ end
@@ -0,0 +1,88 @@
1
+ require 'pdk'
2
+ require 'pdk/validate/base_validator'
3
+ require 'pdk/util'
4
+
5
+ module PDK
6
+ module Validate
7
+ class Tasks
8
+ class Name < BaseValidator
9
+ INVALID_TASK_MSG = _(
10
+ 'Invalid task name. Task names must start with a lowercase letter' \
11
+ 'and can only contain lowercase letters, numbers, and underscores.',
12
+ )
13
+
14
+ def self.name
15
+ 'task-name'
16
+ end
17
+
18
+ def self.pattern
19
+ 'tasks/**/*'
20
+ end
21
+
22
+ def self.spinner_text(_targets = [])
23
+ _('Checking task names (%{targets}).') % {
24
+ targets: pattern,
25
+ }
26
+ end
27
+
28
+ def self.create_spinner(targets = [], options = {})
29
+ return unless PDK::CLI::Util.interactive?
30
+ options = options.merge(PDK::CLI::Util.spinner_opts_for_platform)
31
+
32
+ exec_group = options[:exec_group]
33
+ @spinner = if exec_group
34
+ exec_group.add_spinner(spinner_text(targets), options)
35
+ else
36
+ TTY::Spinner.new("[:spinner] #{spinner_text(targets)}", options)
37
+ end
38
+ @spinner.auto_spin
39
+ end
40
+
41
+ def self.stop_spinner(exit_code)
42
+ if exit_code.zero? && @spinner
43
+ @spinner.success
44
+ elsif @spinner
45
+ @spinner.error
46
+ end
47
+ end
48
+
49
+ def self.invoke(report, options = {})
50
+ targets, skipped, invalid = parse_targets(options)
51
+
52
+ process_skipped(report, skipped)
53
+ process_invalid(report, invalid)
54
+
55
+ return 0 if targets.empty?
56
+
57
+ return_val = 0
58
+ create_spinner(targets, options)
59
+
60
+ targets.each do |target|
61
+ task_name = File.basename(target, File.extname(target))
62
+ if PDK::CLI::Util::OptionValidator.valid_task_name?(task_name)
63
+ report.add_event(
64
+ file: target,
65
+ source: name,
66
+ state: :passed,
67
+ severity: 'ok',
68
+ )
69
+ else
70
+ report.add_event(
71
+ file: target,
72
+ source: name,
73
+ state: :failure,
74
+ severity: 'error',
75
+ message: INVALID_TASK_MSG,
76
+ )
77
+
78
+ return_val = 1
79
+ end
80
+ end
81
+
82
+ stop_spinner(return_val)
83
+ return_val
84
+ end
85
+ end
86
+ end
87
+ end
88
+ end