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 +4 -4
- data/README.md +24 -0
- data/lib/puppet-lint/bin.rb +5 -0
- data/lib/puppet-lint/configuration.rb +1 -0
- data/lib/puppet-lint/optparser.rb +4 -0
- data/lib/puppet-lint/plugins/legacy_facts/legacy_facts.rb +17 -11
- data/lib/puppet-lint/report/codeclimate.rb +48 -0
- data/lib/puppet-lint/tasks/puppet-lint.rb +8 -1
- data/lib/puppet-lint/version.rb +1 -1
- data/lib/puppet-lint.rb +11 -7
- data/spec/fixtures/test/reports/code_climate.json +38 -0
- data/spec/unit/puppet-lint/bin_spec.rb +24 -0
- data/spec/unit/puppet-lint/configuration_spec.rb +21 -11
- data/spec/unit/puppet-lint/plugins/legacy_facts/legacy_facts_spec.rb +33 -0
- metadata +4 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 7b11b9e197b9d535a12a054f70eb91e946fb61eab5377617473eff216bea8737
|
4
|
+
data.tar.gz: 111b88c93388f7593e7176ee7bd8de5df04a849844fca0a77feea6a623c073f4
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
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.
|
data/lib/puppet-lint/bin.rb
CHANGED
@@ -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'
|
@@ -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
|
-
#
|
119
|
-
#
|
120
|
-
if token.value.start_with?('::')
|
121
|
-
fact_name = token.value.
|
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
|
-
|
130
|
-
|
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
|
)
|
data/lib/puppet-lint/version.rb
CHANGED
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 =
|
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
|
-
|
179
|
-
|
180
|
-
|
181
|
-
|
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
|
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'
|
59
|
-
'fail_on_warnings'
|
60
|
-
'
|
61
|
-
'
|
62
|
-
'
|
63
|
-
'
|
64
|
-
'
|
65
|
-
'
|
66
|
-
'
|
67
|
-
'
|
68
|
-
'
|
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.
|
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-
|
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
|