pdk 1.7.1 → 1.8.0

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