puppet-lint 3.1.0 → 3.3.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
  SHA256:
3
- metadata.gz: 4fce16244bedd61b8ca330f4dd9c2947e8111382830ebd63b22d9f2af8d2a50b
4
- data.tar.gz: 727de95b4de5c2c2ee24172eea4f9e2551c816e31f3898bcc20e498e4ee7d614
3
+ metadata.gz: 7b11b9e197b9d535a12a054f70eb91e946fb61eab5377617473eff216bea8737
4
+ data.tar.gz: 111b88c93388f7593e7176ee7bd8de5df04a849844fca0a77feea6a623c073f4
5
5
  SHA512:
6
- metadata.gz: cb7870bfd9fd0a5bc5a616c651b0d614e6134761b15149c2db34f8796358b23dee66ae465c47f8361f37c9066286d49a619e764277acd6a49aa9ba7c68e2842c
7
- data.tar.gz: fc2ea5694a8dd0660018d2a8cd1fbee55bb813fd68b052c06127a3857ba00b99e6e67970e9fb58ae1681f34cb4ddb8d6fda8c74690b9bf7c403f37ba80cc024e
6
+ metadata.gz: ae49b487202623f3684530562a0277c5306d62b3c45473f1b49266478b1fc3df2e1d46924cbffd4ad371d4b299be4ccd6737edc3082d9a5c9f347977e57216f0
7
+ data.tar.gz: 23a4ff18eccb4dc1503ce403df40df7a430123fbfff01e77bf56bbe408b5b5ad8cc2ab3dbfebb159b654639ce130a9d329e78df7e3fc9e31abf513efcf09c421
data/README.md CHANGED
@@ -215,6 +215,30 @@ There is a GitHub Actions action available to get linter feedback in workflows:
215
215
 
216
216
  * [puppet-lint-action](https://github.com/marketplace/actions/puppet-lint-action)
217
217
 
218
+ ## Integration with GitLab Code Quality
219
+
220
+ [GitLab](https://gitlab.com/) users can use the `--codeclimate-report-file` configuration option to generate a report for use with the
221
+ [Code Quality](https://docs.gitlab.com/ee/ci/testing/code_quality.html) feature.
222
+
223
+ The easiest way to set this option, (and without having to modify rake tasks), is with the `CODECLIMATE_REPORT_FILE` environment variable.
224
+
225
+ For example, the following GitLab job sets the environment variable and
226
+ [archives the report](https://docs.gitlab.com/ee/ci/yaml/artifacts_reports.html#artifactsreportscodequality) produced.
227
+ ```yaml
228
+ validate lint check rubocop-Ruby 2.7.2-Puppet ~> 7:
229
+ stage: syntax
230
+ image: ruby:2.7.2
231
+ script:
232
+ - bundle exec rake validate lint check rubocop
233
+ variables:
234
+ PUPPET_GEM_VERSION: '~> 7'
235
+ CODECLIMATE_REPORT_FILE: 'gl-code-quality-report.json'
236
+ artifacts:
237
+ reports:
238
+ codequality: gl-code-quality-report.json
239
+ expire_in: 1 week
240
+ ```
241
+
218
242
  ## Options
219
243
 
220
244
  See `puppet-lint --help` for a full list of command line options and checks.
@@ -1,6 +1,7 @@
1
1
  require 'pathname'
2
2
  require 'uri'
3
3
  require 'puppet-lint/optparser'
4
+ require 'puppet-lint/report/codeclimate'
4
5
 
5
6
  # Internal: The logic of the puppet-lint bin script, contained in a class for
6
7
  # ease of testing.
@@ -104,6 +105,10 @@ class PuppetLint::Bin
104
105
  puts JSON.pretty_generate(all_problems)
105
106
  end
106
107
 
108
+ if PuppetLint.configuration.codeclimate_report_file
109
+ PuppetLint::Report::CodeClimateReporter.write_report_file(all_problems, PuppetLint.configuration.codeclimate_report_file)
110
+ end
111
+
107
112
  return_val
108
113
  rescue PuppetLint::NoCodeError
109
114
  puts 'puppet-lint: no file specified or specified file does not exist'
@@ -153,5 +153,6 @@ class PuppetLint::Configuration
153
153
  self.show_ignored = false
154
154
  self.ignore_paths = ['vendor/**/*.pp']
155
155
  self.github_actions = ENV.key?('GITHUB_ACTION')
156
+ self.codeclimate_report_file = ENV['CODECLIMATE_REPORT_FILE']
156
157
  end
157
158
  end
@@ -102,6 +102,10 @@ class PuppetLint::OptParser
102
102
  PuppetLint.configuration.sarif = true
103
103
  end
104
104
 
105
+ opts.on('--codeclimate-report-file FILE', 'Save a code climate compatible report to this file') do |file|
106
+ PuppetLint.configuration.codeclimate_report_file = file
107
+ end
108
+
105
109
  opts.on('--list-checks', 'List available check names.') do
106
110
  PuppetLint.configuration.list_checks = true
107
111
  end
@@ -62,11 +62,11 @@ PuppetLint.new_check(:legacy_facts) do
62
62
  'lsbminordistrelease' => "facts['os']['distro']['release']['minor']",
63
63
  'lsbrelease' => "facts['os']['distro']['release']['specification']",
64
64
  'macaddress' => "facts['networking']['mac']",
65
- 'macosx_buildversion' => "facts['os']['build']",
66
- 'macosx_productname' => "facts['os']['product']",
67
- 'macosx_productversion' => "facts['os']['version']['full']",
68
- 'macosx_productversion_major' => "facts['os']['version']['major']",
69
- 'macosx_productversion_minor' => "facts['os']['version']['minor']",
65
+ 'macosx_buildversion' => "facts['os']['macosx']['build']",
66
+ 'macosx_productname' => "facts['os']['macosx']['product']",
67
+ 'macosx_productversion' => "facts['os']['macosx']['version']['full']",
68
+ 'macosx_productversion_major' => "facts['os']['macosx']['version']['major']",
69
+ 'macosx_productversion_minor' => "facts['os']['macosx']['version']['minor']",
70
70
  'manufacturer' => "facts['dmi']['manufacturer']",
71
71
  'memoryfree' => "facts['memory']['system']['available']",
72
72
  'memorysize' => "facts['memory']['system']['total']",
@@ -115,10 +115,14 @@ PuppetLint.new_check(:legacy_facts) do
115
115
  tokens.select { |x| LEGACY_FACTS_VAR_TYPES.include?(x.type) }.each do |token|
116
116
  fact_name = ''
117
117
 
118
- # Get rid of the top scope before we do our work. We don't need to
119
- # preserve it because it won't work with the new structured facts.
120
- if token.value.start_with?('::')
121
- fact_name = token.value.sub(%r{^::}, '')
118
+ # This matches legacy facts defined in the fact hash that use the top scope
119
+ # fact assignment.
120
+ if token.value.start_with?('::facts[')
121
+ fact_name = token.value.match(%r{::facts\['(.*)'\]})[1]
122
+
123
+ # This matches legacy facts defined in the fact hash.
124
+ elsif token.value.start_with?("facts['")
125
+ fact_name = token.value.match(%r{facts\['(.*)'\]})[1]
122
126
 
123
127
  # This matches using legacy facts in a the new structured fact. For
124
128
  # example this would match 'uuid' in $facts['uuid'] so it can be converted
@@ -126,8 +130,10 @@ PuppetLint.new_check(:legacy_facts) do
126
130
  elsif token.value == 'facts'
127
131
  fact_name = hash_key_for(token)
128
132
 
129
- elsif token.value.start_with?("facts['")
130
- fact_name = token.value.match(%r{facts\['(.*)'\]})[1]
133
+ # Now we can get rid of top scopes. We don't need to
134
+ # preserve it because it won't work with the new structured facts.
135
+ elsif token.value.start_with?('::')
136
+ fact_name = token.value.sub(%r{^::}, '')
131
137
  end
132
138
 
133
139
  next unless EASY_FACTS.include?(fact_name) || UNCONVERTIBLE_FACTS.include?(fact_name) || fact_name.match(Regexp.union(REGEX_FACTS))
@@ -0,0 +1,48 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'digest'
4
+ require 'json'
5
+
6
+ class PuppetLint::Report
7
+ # Formats problems and writes them to a file as a code climate compatible report.
8
+ class CodeClimateReporter
9
+ def self.write_report_file(problems, report_file)
10
+ report = []
11
+ problems.each do |messages|
12
+ messages.each do |message|
13
+ case message[:kind]
14
+ when :warning
15
+ severity = 'minor'
16
+ when :error
17
+ severity = 'major'
18
+ else
19
+ next
20
+ end
21
+
22
+ issue = {
23
+ type: :issue,
24
+ check_name: message[:check],
25
+ description: message[:message],
26
+ categories: [:Style],
27
+ severity: severity,
28
+ location: {
29
+ path: message[:path],
30
+ lines: {
31
+ begin: message[:line],
32
+ end: message[:line],
33
+ }
34
+ },
35
+ }
36
+
37
+ issue[:fingerprint] = Digest::MD5.hexdigest(Marshal.dump(issue))
38
+
39
+ if message.key?(:description) && message.key?(:help_uri)
40
+ issue[:content] = "#{message[:description].chomp('.')}. See [this page](#{message[:help_uri]}) for more information about the `#{message[:check]}` check."
41
+ end
42
+ report << issue
43
+ end
44
+ end
45
+ File.write(report_file, "#{JSON.pretty_generate(report)}\n")
46
+ end
47
+ end
48
+ end
@@ -4,6 +4,7 @@ require 'puppet-lint'
4
4
  require 'puppet-lint/optparser'
5
5
  require 'rake'
6
6
  require 'rake/tasklib'
7
+ require 'puppet-lint/report/codeclimate'
7
8
 
8
9
  # Public: A Rake task that can be loaded and used with everything you need.
9
10
  #
@@ -90,6 +91,7 @@ class PuppetLint::RakeTask < ::Rake::TaskLib
90
91
  RakeFileUtils.send(:verbose, true) do
91
92
  linter = PuppetLint.new
92
93
  matched_files = FileList[@pattern]
94
+ all_problems = []
93
95
 
94
96
  matched_files = matched_files.exclude(*@ignore_paths)
95
97
 
@@ -97,12 +99,17 @@ class PuppetLint::RakeTask < ::Rake::TaskLib
97
99
  next unless File.file?(puppet_file)
98
100
  linter.file = puppet_file
99
101
  linter.run
100
- linter.print_problems
102
+ all_problems << linter.print_problems
101
103
 
102
104
  if PuppetLint.configuration.fix && linter.problems.none? { |e| e[:check] == :syntax }
103
105
  IO.write(puppet_file, linter.manifest)
104
106
  end
105
107
  end
108
+
109
+ if PuppetLint.configuration.codeclimate_report_file
110
+ PuppetLint::Report::CodeClimateReporter.write_report_file(all_problems, PuppetLint.configuration.codeclimate_report_file)
111
+ end
112
+
106
113
  abort if linter.errors? || (
107
114
  linter.warnings? && PuppetLint.configuration.fail_on_warnings
108
115
  )
@@ -1,3 +1,3 @@
1
1
  class PuppetLint
2
- VERSION = '3.1.0'.freeze
2
+ VERSION = '3.3.0'.freeze
3
3
  end
data/lib/puppet-lint.rb CHANGED
@@ -124,6 +124,7 @@ class PuppetLint
124
124
  puts format % message
125
125
 
126
126
  puts " #{message[:reason]}" if message[:kind] == :ignored && !message[:reason].nil?
127
+ print_context(message)
127
128
  end
128
129
 
129
130
  # Internal: Format a problem message and print it to STDOUT so GitHub Actions
@@ -154,7 +155,8 @@ class PuppetLint
154
155
  def print_context(message)
155
156
  return if message[:check] == 'documentation'
156
157
  return if message[:kind] == :fixed
157
- line = get_context(message)
158
+ line = message[:context]
159
+ return unless line
158
160
  offset = line.index(%r{\S}) || 1
159
161
  puts "\n #{line.strip}"
160
162
  printf("%#{message[:column] + 2 - offset}s\n\n", '^')
@@ -168,6 +170,8 @@ class PuppetLint
168
170
  # Returns array of problem.
169
171
  def report(problems)
170
172
  json = []
173
+ print_stdout = !(configuration.json || configuration.sarif)
174
+
171
175
  problems.each do |message|
172
176
  next if message[:kind] == :ignored && !PuppetLint.configuration.show_ignored
173
177
 
@@ -175,12 +179,12 @@ class PuppetLint
175
179
 
176
180
  next unless message[:kind] == :fixed || [message[:kind], :all].include?(configuration.error_level)
177
181
 
178
- if configuration.json || configuration.sarif
179
- message['context'] = get_context(message) if configuration.with_context
180
- json << message
181
- else
182
+ message[:context] = get_context(message) if configuration.with_context
183
+
184
+ json << message
185
+
186
+ if print_stdout
182
187
  format_message(message)
183
- print_context(message) if configuration.with_context
184
188
  print_github_annotation(message) if configuration.github_actions
185
189
  end
186
190
  end
@@ -225,7 +229,7 @@ class PuppetLint
225
229
 
226
230
  # Public: Print any problems that were found out to stdout.
227
231
  #
228
- # Returns nothing.
232
+ # Returns an array of problems.
229
233
  def print_problems
230
234
  report(@problems)
231
235
  end
@@ -0,0 +1,38 @@
1
+ [
2
+ {
3
+ "type": "issue",
4
+ "check_name": "autoloader_layout",
5
+ "description": "test::foo not in autoload module layout",
6
+ "categories": [
7
+ "Style"
8
+ ],
9
+ "severity": "major",
10
+ "location": {
11
+ "path": "spec/fixtures/test/manifests/fail.pp",
12
+ "lines": {
13
+ "begin": 2,
14
+ "end": 2
15
+ }
16
+ },
17
+ "fingerprint": "f12bce7a776454ab9ffac2d20dcc34ba",
18
+ "content": "Test the manifest tokens for any classes or defined types that are not in an appropriately named file for the autoloader to detect and record an error of each instance found. See [this page](https://puppet.com/docs/puppet/latest/style_guide.html#separate-files) for more information about the `autoloader_layout` check."
19
+ },
20
+ {
21
+ "type": "issue",
22
+ "check_name": "parameter_order",
23
+ "description": "optional parameter listed before required parameter",
24
+ "categories": [
25
+ "Style"
26
+ ],
27
+ "severity": "minor",
28
+ "location": {
29
+ "path": "spec/fixtures/test/manifests/warning.pp",
30
+ "lines": {
31
+ "begin": 2,
32
+ "end": 2
33
+ }
34
+ },
35
+ "fingerprint": "e34cf289e008446b633d1be7cf58120e",
36
+ "content": "Test the manifest tokens for any parameterised classes or defined types that take parameters and record a warning if there are any optional parameters listed before required parameters. See [this page](https://puppet.com/docs/puppet/latest/style_guide.html#display-order-of-parameters) for more information about the `parameter_order` check."
37
+ }
38
+ ]
@@ -437,6 +437,30 @@ describe PuppetLint::Bin do
437
437
  end
438
438
  end
439
439
 
440
+ context 'when outputting code climate report' do
441
+ let(:report_file) do
442
+ Tempfile.new('report_file.json')
443
+ end
444
+
445
+ let(:args) do
446
+ [
447
+ '--codeclimate-report-file',
448
+ report_file.path,
449
+ 'spec/fixtures/test/manifests/fail.pp',
450
+ 'spec/fixtures/test/manifests/warning.pp',
451
+ ]
452
+ end
453
+
454
+ after(:each) do
455
+ report_file.unlink
456
+ end
457
+
458
+ it 'creates a code climate report' do
459
+ expect(bin.exitstatus).to eq(1)
460
+ expect(FileUtils.compare_file(report_file.path, 'spec/fixtures/test/reports/code_climate.json')).to be_truthy
461
+ end
462
+ end
463
+
440
464
  context 'when hiding ignored problems' do
441
465
  let(:args) do
442
466
  [
@@ -55,17 +55,18 @@ describe PuppetLint::Configuration do
55
55
  end
56
56
 
57
57
  expect(config.settings).to eq(
58
- 'with_filename' => false,
59
- 'fail_on_warnings' => false,
60
- 'error_level' => :all,
61
- 'log_format' => '',
62
- 'sarif' => false,
63
- 'with_context' => false,
64
- 'fix' => false,
65
- 'github_actions' => false,
66
- 'show_ignored' => false,
67
- 'json' => false,
68
- 'ignore_paths' => ['vendor/**/*.pp'],
58
+ 'with_filename' => false,
59
+ 'fail_on_warnings' => false,
60
+ 'codeclimate_report_file' => nil,
61
+ 'error_level' => :all,
62
+ 'log_format' => '',
63
+ 'sarif' => false,
64
+ 'with_context' => false,
65
+ 'fix' => false,
66
+ 'github_actions' => false,
67
+ 'show_ignored' => false,
68
+ 'json' => false,
69
+ 'ignore_paths' => ['vendor/**/*.pp'],
69
70
  )
70
71
  end
71
72
 
@@ -78,6 +79,15 @@ describe PuppetLint::Configuration do
78
79
  expect(config.settings['github_actions']).to be(true)
79
80
  end
80
81
 
82
+ it 'defaults codeclimate_report_file to the CODECLIMATE_REPORT_FILE environment variable' do
83
+ override_env do
84
+ ENV['CODECLIMATE_REPORT_FILE'] = '/path/to/report.json'
85
+ config.defaults
86
+ end
87
+
88
+ expect(config.settings['codeclimate_report_file']).to eq('/path/to/report.json')
89
+ end
90
+
81
91
  def override_env
82
92
  old_env = ENV.to_h
83
93
  yield
@@ -113,6 +113,14 @@ describe 'legacy_facts' do
113
113
  expect(problems).to have(1).problem
114
114
  end
115
115
  end
116
+
117
+ context 'top scoped fact variable using legacy facts hash variable in interpolation' do
118
+ let(:code) { "$::facts['osfamily']" }
119
+
120
+ it 'detects a single problem' do
121
+ expect(problems).to have(1).problem
122
+ end
123
+ end
116
124
  end
117
125
 
118
126
  context 'with fix enabled' do
@@ -165,6 +173,31 @@ describe 'legacy_facts' do
165
173
  end
166
174
  end
167
175
 
176
+ context 'fact variable using top scope $::facts hash' do
177
+ let(:code) { "$::facts['os']['family']" }
178
+
179
+ it 'does not detect any problems' do
180
+ expect(problems).to have(0).problem
181
+ end
182
+ end
183
+
184
+ context "fact variable using legacy top scope $::facts['osfamily']" do
185
+ let(:code) { "$::facts['osfamily']" }
186
+ let(:msg) { "legacy fact 'osfamily'" }
187
+
188
+ it 'only detects a single problem' do
189
+ expect(problems).to have(1).problem
190
+ end
191
+
192
+ it 'fixes the problem' do
193
+ expect(problems).to contain_fixed(msg).on_line(1).in_column(1)
194
+ end
195
+
196
+ it 'uses the facts hash' do
197
+ expect(manifest).to eq("$facts['os']['family']")
198
+ end
199
+ end
200
+
168
201
  context 'fact variable using legacy $::osfamily' do
169
202
  let(:code) { '$::osfamily' }
170
203
  let(:msg) { "legacy fact 'osfamily'" }
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: puppet-lint
3
3
  version: !ruby/object:Gem::Version
4
- version: 3.1.0
4
+ version: 3.3.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Tim Sharpe
@@ -10,7 +10,7 @@ authors:
10
10
  autorequire:
11
11
  bindir: bin
12
12
  cert_chain: []
13
- date: 2023-02-28 00:00:00.000000000 Z
13
+ date: 2023-03-07 00:00:00.000000000 Z
14
14
  dependencies: []
15
15
  description: " Checks your Puppet manifests against the Puppetlabs style guide
16
16
  and alerts you to any discrepancies.\n"
@@ -77,6 +77,7 @@ files:
77
77
  - lib/puppet-lint/plugins/check_whitespace/trailing_whitespace.rb
78
78
  - lib/puppet-lint/plugins/legacy_facts/legacy_facts.rb
79
79
  - lib/puppet-lint/plugins/top_scope_facts/top_scope_facts.rb
80
+ - lib/puppet-lint/report/codeclimate.rb
80
81
  - lib/puppet-lint/report/github.rb
81
82
  - lib/puppet-lint/report/sarif_template.json
82
83
  - lib/puppet-lint/tasks/gemfile_rewrite.rb
@@ -97,6 +98,7 @@ files:
97
98
  - spec/fixtures/test/manifests/unterminated_control_comment.pp
98
99
  - spec/fixtures/test/manifests/url_interpolation.pp
99
100
  - spec/fixtures/test/manifests/warning.pp
101
+ - spec/fixtures/test/reports/code_climate.json
100
102
  - spec/spec_helper.rb
101
103
  - spec/spec_helper_acceptance.rb
102
104
  - spec/spec_helper_acceptance_local.rb