foodcritic 10.0.0 → 10.1.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/CHANGELOG.md +12 -1
- data/Gemfile +2 -2
- data/features/031_check_for_metadata_existence.feature +2 -2
- data/features/choose_rules_to_apply.feature +27 -27
- data/features/continuous_integration_support.feature +2 -2
- data/features/support/command_helpers.rb +2 -2
- data/lib/foodcritic/api.rb +29 -3
- data/lib/foodcritic/linter.rb +1 -1
- data/lib/foodcritic/rules/fc001.rb +7 -0
- data/lib/foodcritic/rules/fc002.rb +8 -0
- data/lib/foodcritic/rules/fc004.rb +12 -0
- data/lib/foodcritic/rules/fc005.rb +29 -0
- data/lib/foodcritic/rules/fc006.rb +11 -0
- data/lib/foodcritic/rules/fc007.rb +19 -0
- data/lib/foodcritic/rules/fc008.rb +12 -0
- data/lib/foodcritic/rules/fc009.rb +18 -0
- data/lib/foodcritic/rules/fc010.rb +7 -0
- data/lib/foodcritic/rules/fc011.rb +8 -0
- data/lib/foodcritic/rules/fc012.rb +8 -0
- data/lib/foodcritic/rules/fc013.rb +10 -0
- data/lib/foodcritic/rules/fc014.rb +10 -0
- data/lib/foodcritic/rules/fc015.rb +8 -0
- data/lib/foodcritic/rules/fc016.rb +10 -0
- data/lib/foodcritic/rules/fc017.rb +29 -0
- data/lib/foodcritic/rules/fc018.rb +9 -0
- data/lib/foodcritic/rules/fc019.rb +34 -0
- data/lib/foodcritic/rules/fc021.rb +13 -0
- data/lib/foodcritic/rules/fc022.rb +33 -0
- data/lib/foodcritic/rules/fc024.rb +31 -0
- data/lib/foodcritic/rules/fc025.rb +17 -0
- data/lib/foodcritic/rules/fc026.rb +14 -0
- data/lib/foodcritic/rules/fc027.rb +9 -0
- data/lib/foodcritic/rules/fc028.rb +8 -0
- data/lib/foodcritic/rules/fc029.rb +14 -0
- data/lib/foodcritic/rules/fc030.rb +11 -0
- data/lib/foodcritic/rules/fc031.rb +8 -0
- data/lib/foodcritic/rules/fc032.rb +15 -0
- data/lib/foodcritic/rules/fc033.rb +26 -0
- data/lib/foodcritic/rules/fc034.rb +31 -0
- data/lib/foodcritic/rules/fc037.rb +14 -0
- data/lib/foodcritic/rules/fc038.rb +17 -0
- data/lib/foodcritic/rules/fc039.rb +19 -0
- data/lib/foodcritic/rules/fc040.rb +12 -0
- data/lib/foodcritic/rules/fc041.rb +9 -0
- data/lib/foodcritic/rules/fc042.rb +6 -0
- data/lib/foodcritic/rules/fc043.rb +8 -0
- data/lib/foodcritic/rules/fc044.rb +22 -0
- data/lib/foodcritic/rules/fc045.rb +13 -0
- data/lib/foodcritic/rules/fc046.rb +8 -0
- data/lib/foodcritic/rules/fc047.rb +16 -0
- data/lib/foodcritic/rules/fc048.rb +13 -0
- data/lib/foodcritic/rules/fc049.rb +10 -0
- data/lib/foodcritic/rules/fc050.rb +8 -0
- data/lib/foodcritic/rules/fc051.rb +15 -0
- data/lib/foodcritic/rules/fc052.rb +6 -0
- data/lib/foodcritic/rules/fc053.rb +6 -0
- data/lib/foodcritic/rules/fc055.rb +6 -0
- data/lib/foodcritic/rules/fc056.rb +6 -0
- data/lib/foodcritic/rules/fc057.rb +8 -0
- data/lib/foodcritic/rules/fc058.rb +9 -0
- data/lib/foodcritic/rules/fc059.rb +10 -0
- data/lib/foodcritic/rules/fc060.rb +10 -0
- data/lib/foodcritic/rules/fc061.rb +10 -0
- data/lib/foodcritic/rules/fc062.rb +6 -0
- data/lib/foodcritic/rules/fc063.rb +8 -0
- data/lib/foodcritic/rules/fc064.rb +6 -0
- data/lib/foodcritic/rules/fc065.rb +6 -0
- data/lib/foodcritic/version.rb +1 -1
- data/spec/foodcritic/api_spec.rb +62 -0
- data/spec/foodcritic/coverage/assets/0.10.0/application.css +799 -0
- data/spec/foodcritic/coverage/assets/0.10.0/application.js +1707 -0
- data/spec/foodcritic/coverage/assets/0.10.0/colorbox/border.png +0 -0
- data/spec/foodcritic/coverage/assets/0.10.0/colorbox/controls.png +0 -0
- data/spec/foodcritic/coverage/assets/0.10.0/colorbox/loading.gif +0 -0
- data/spec/foodcritic/coverage/assets/0.10.0/colorbox/loading_background.png +0 -0
- data/spec/foodcritic/coverage/assets/0.10.0/favicon_green.png +0 -0
- data/spec/foodcritic/coverage/assets/0.10.0/favicon_red.png +0 -0
- data/spec/foodcritic/coverage/assets/0.10.0/favicon_yellow.png +0 -0
- data/spec/foodcritic/coverage/assets/0.10.0/loading.gif +0 -0
- data/spec/foodcritic/coverage/assets/0.10.0/magnify.png +0 -0
- data/spec/foodcritic/coverage/assets/0.10.0/smoothness/images/ui-bg_flat_0_aaaaaa_40x100.png +0 -0
- data/spec/foodcritic/coverage/assets/0.10.0/smoothness/images/ui-bg_flat_75_ffffff_40x100.png +0 -0
- data/spec/foodcritic/coverage/assets/0.10.0/smoothness/images/ui-bg_glass_55_fbf9ee_1x400.png +0 -0
- data/spec/foodcritic/coverage/assets/0.10.0/smoothness/images/ui-bg_glass_65_ffffff_1x400.png +0 -0
- data/spec/foodcritic/coverage/assets/0.10.0/smoothness/images/ui-bg_glass_75_dadada_1x400.png +0 -0
- data/spec/foodcritic/coverage/assets/0.10.0/smoothness/images/ui-bg_glass_75_e6e6e6_1x400.png +0 -0
- data/spec/foodcritic/coverage/assets/0.10.0/smoothness/images/ui-bg_glass_95_fef1ec_1x400.png +0 -0
- data/spec/foodcritic/coverage/assets/0.10.0/smoothness/images/ui-bg_highlight-soft_75_cccccc_1x100.png +0 -0
- data/spec/foodcritic/coverage/assets/0.10.0/smoothness/images/ui-icons_222222_256x240.png +0 -0
- data/spec/foodcritic/coverage/assets/0.10.0/smoothness/images/ui-icons_2e83ff_256x240.png +0 -0
- data/spec/foodcritic/coverage/assets/0.10.0/smoothness/images/ui-icons_454545_256x240.png +0 -0
- data/spec/foodcritic/coverage/assets/0.10.0/smoothness/images/ui-icons_888888_256x240.png +0 -0
- data/spec/foodcritic/coverage/assets/0.10.0/smoothness/images/ui-icons_cd0a0a_256x240.png +0 -0
- data/spec/foodcritic/coverage/index.html +72 -0
- data/spec/foodcritic/linter_spec.rb +7 -6
- data/spec/regression/expected-output.txt +0 -12
- data/spec/spec_helper.rb +3 -1
- metadata +88 -6
- data/features/023_check_for_condition_around_resource.feature +0 -52
- data/lib/foodcritic/rules.rb +0 -852
Binary file
|
Binary file
|
Binary file
|
Binary file
|
Binary file
|
Binary file
|
Binary file
|
Binary file
|
Binary file
|
Binary file
|
Binary file
|
Binary file
|
Binary file
|
Binary file
|
Binary file
|
Binary file
|
Binary file
|
Binary file
|
Binary file
|
Binary file
|
Binary file
|
@@ -0,0 +1,72 @@
|
|
1
|
+
<!DOCTYPE html>
|
2
|
+
<html xmlns='http://www.w3.org/1999/xhtml'>
|
3
|
+
<head>
|
4
|
+
<title>Code coverage for Foodcritic</title>
|
5
|
+
<meta http-equiv="content-type" content="text/html; charset=utf-8" />
|
6
|
+
<script src='./assets/0.10.0/application.js' type='text/javascript'></script>
|
7
|
+
<link href='./assets/0.10.0/application.css' media='screen, projection, print' rel='stylesheet' type='text/css'>
|
8
|
+
<link rel="shortcut icon" type="image/png" href="./assets/0.10.0/favicon_green.png" />
|
9
|
+
<link rel="icon" type="image/png" href="./assets/0.10.0/favicon.png" />
|
10
|
+
</head>
|
11
|
+
|
12
|
+
<body>
|
13
|
+
<div id="loading">
|
14
|
+
<img src="./assets/0.10.0/loading.gif" alt="loading"/>
|
15
|
+
</div>
|
16
|
+
<div id="wrapper" style="display:none;">
|
17
|
+
<div class="timestamp">Generated <abbr class="timeago" title="2017-03-20T23:38:31-07:00">2017-03-20T23:38:31-07:00</abbr></div>
|
18
|
+
<ul class="group_tabs"></ul>
|
19
|
+
|
20
|
+
<div id="content">
|
21
|
+
<div class="file_list_container" id="AllFiles">
|
22
|
+
<h2>
|
23
|
+
<span class="group_name">All Files</span>
|
24
|
+
(<span class="covered_percent"><span class="green">100.0%</span></span>
|
25
|
+
covered at
|
26
|
+
<span class="covered_strength">
|
27
|
+
<span class="red">
|
28
|
+
0.0
|
29
|
+
</span>
|
30
|
+
</span> hits/line)
|
31
|
+
</h2>
|
32
|
+
<a name="AllFiles"></a>
|
33
|
+
<div>
|
34
|
+
<b>0</b> files in total.
|
35
|
+
<b>0.0</b> relevant lines.
|
36
|
+
<span class="green"><b>0.0</b> lines covered</span> and
|
37
|
+
<span class="red"><b>0.0</b> lines missed </span>
|
38
|
+
</div>
|
39
|
+
<table class="file_list">
|
40
|
+
<thead>
|
41
|
+
<tr>
|
42
|
+
<th>File</th>
|
43
|
+
<th>% covered</th>
|
44
|
+
<th>Lines</th>
|
45
|
+
<th>Relevant Lines</th>
|
46
|
+
<th>Lines covered</th>
|
47
|
+
<th>Lines missed</th>
|
48
|
+
<th>Avg. Hits / Line</th>
|
49
|
+
</tr>
|
50
|
+
</thead>
|
51
|
+
<tbody>
|
52
|
+
|
53
|
+
</tbody>
|
54
|
+
</table>
|
55
|
+
</div>
|
56
|
+
|
57
|
+
|
58
|
+
|
59
|
+
</div>
|
60
|
+
|
61
|
+
<div id="footer">
|
62
|
+
Generated by <a href="http://github.com/colszowka/simplecov">simplecov</a> v0.14.1
|
63
|
+
and simplecov-html v0.10.0<br/>
|
64
|
+
using RSpec
|
65
|
+
</div>
|
66
|
+
|
67
|
+
<div class="source_files">
|
68
|
+
|
69
|
+
</div>
|
70
|
+
</div>
|
71
|
+
</body>
|
72
|
+
</html>
|
@@ -11,7 +11,7 @@ describe FoodCritic::Linter do
|
|
11
11
|
|
12
12
|
describe "chef version" do
|
13
13
|
it "should be the latest stable version of Chef" do
|
14
|
-
FoodCritic::Linter::DEFAULT_CHEF_VERSION.must_equal "12.
|
14
|
+
FoodCritic::Linter::DEFAULT_CHEF_VERSION.must_equal "12.19.36"
|
15
15
|
end
|
16
16
|
end
|
17
17
|
|
@@ -54,20 +54,21 @@ describe FoodCritic::Linter do
|
|
54
54
|
end
|
55
55
|
|
56
56
|
describe "#load_files!" do
|
57
|
-
let(:
|
58
|
-
|
57
|
+
let(:default_rule_files) do
|
58
|
+
# an array of each of the absolute paths to the default rules
|
59
|
+
Dir.glob(File.expand_path(File.join(File.dirname(__FILE__), "../../lib/foodcritic/rules/*")))
|
59
60
|
end
|
60
61
|
|
61
62
|
let(:rule_dsl_load_mock) { MiniTest::Mock.new }
|
62
63
|
|
63
64
|
it "should add the default rule file" do
|
64
|
-
rule_dsl_load_mock.expect(:call, nil, [
|
65
|
+
rule_dsl_load_mock.expect(:call, nil, [default_rule_files, nil])
|
65
66
|
verify_loaded
|
66
67
|
end
|
67
68
|
|
68
69
|
it "should include rules found in gems if the :search_gems option is true" do
|
69
70
|
gem_rules = ["/path/to/rules1.rb", "/path/to/rules2.rb"]
|
70
|
-
expected_rules = [
|
71
|
+
expected_rules = [*default_rule_files, *gem_rules]
|
71
72
|
rule_dsl_load_mock.expect(:call, nil, [expected_rules, nil])
|
72
73
|
|
73
74
|
metaclass = class << linter; self; end
|
@@ -80,7 +81,7 @@ describe FoodCritic::Linter do
|
|
80
81
|
|
81
82
|
it "should include files found in :include_rules option" do
|
82
83
|
include_rules = ["/path/to/rules1.rb", "/path/to/rules2.rb"]
|
83
|
-
expected_rules = [
|
84
|
+
expected_rules = [*default_rule_files, *include_rules]
|
84
85
|
rule_dsl_load_mock.expect(:call, nil, [expected_rules, nil])
|
85
86
|
|
86
87
|
verify_loaded :include_rules => include_rules
|
@@ -292,18 +292,6 @@ FC019: Access node attributes in a consistent manner: perl/recipes/default.rb:26
|
|
292
292
|
FC019: Access node attributes in a consistent manner: powershell/recipes/default.rb:69
|
293
293
|
FC019: Access node attributes in a consistent manner: ufw/attributes/default.rb:1
|
294
294
|
FC019: Access node attributes in a consistent manner: ufw/attributes/default.rb:2
|
295
|
-
FC023: Prefer conditional attributes: ./apt/providers/repository.rb:26
|
296
|
-
FC023: Prefer conditional attributes: ./apt/providers/repository.rb:68
|
297
|
-
FC023: Prefer conditional attributes: ./chef-client/recipes/config.rb:62
|
298
|
-
FC023: Prefer conditional attributes: ./database/recipes/ebs_backup.rb:81
|
299
|
-
FC023: Prefer conditional attributes: ./gecode/recipes/package.rb:29
|
300
|
-
FC023: Prefer conditional attributes: ./homebrew/providers/tap.rb:14
|
301
|
-
FC023: Prefer conditional attributes: ./homebrew/providers/tap.rb:22
|
302
|
-
FC023: Prefer conditional attributes: ./lvm/providers/volume_group.rb:12
|
303
|
-
FC023: Prefer conditional attributes: ./passenger_apache2/recipes/mod_rails.rb:27
|
304
|
-
FC023: Prefer conditional attributes: ./users/providers/manage.rb:70
|
305
|
-
FC023: Prefer conditional attributes: ./users/providers/manage.rb:101
|
306
|
-
FC023: Prefer conditional attributes: ./yum/providers/key.rb:59
|
307
295
|
FC024: Consider adding platform equivalents: ./mysql/recipes/server.rb:130
|
308
296
|
FC024: Consider adding platform equivalents: ./mysql/recipes/server.rb:132
|
309
297
|
FC024: Consider adding platform equivalents: ./transmission/recipes/default.rb:42
|
data/spec/spec_helper.rb
CHANGED
@@ -7,7 +7,9 @@ rescue LoadError
|
|
7
7
|
warn "warning: simplecov gem not found; skipping coverage"
|
8
8
|
end
|
9
9
|
|
10
|
-
require "minitest/pride"
|
11
10
|
require "minitest/spec"
|
11
|
+
require "minitest/autorun"
|
12
|
+
require "minitest/reporters"
|
13
|
+
MiniTest::Reporters.use!
|
12
14
|
|
13
15
|
require_relative "../lib/foodcritic"
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: foodcritic
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 10.
|
4
|
+
version: 10.1.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Andrew Crump
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2017-03-
|
11
|
+
date: 2017-03-29 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: cucumber-core
|
@@ -161,7 +161,6 @@ files:
|
|
161
161
|
- features/019_check_for_consistent_node_access.feature
|
162
162
|
- features/021_check_for_dodgy_lwrp_conditions.feature
|
163
163
|
- features/022_check_for_dodgy_conditions_within_loop.feature
|
164
|
-
- features/023_check_for_condition_around_resource.feature
|
165
164
|
- features/024_check_for_missing_platforms.feature
|
166
165
|
- features/025_check_for_deprecated_gem_install.feature
|
167
166
|
- features/026_check_for_conditional_block_string.feature
|
@@ -232,7 +231,65 @@ files:
|
|
232
231
|
- lib/foodcritic/notifications.rb
|
233
232
|
- lib/foodcritic/output.rb
|
234
233
|
- lib/foodcritic/rake_task.rb
|
235
|
-
- lib/foodcritic/rules.rb
|
234
|
+
- lib/foodcritic/rules/fc001.rb
|
235
|
+
- lib/foodcritic/rules/fc002.rb
|
236
|
+
- lib/foodcritic/rules/fc004.rb
|
237
|
+
- lib/foodcritic/rules/fc005.rb
|
238
|
+
- lib/foodcritic/rules/fc006.rb
|
239
|
+
- lib/foodcritic/rules/fc007.rb
|
240
|
+
- lib/foodcritic/rules/fc008.rb
|
241
|
+
- lib/foodcritic/rules/fc009.rb
|
242
|
+
- lib/foodcritic/rules/fc010.rb
|
243
|
+
- lib/foodcritic/rules/fc011.rb
|
244
|
+
- lib/foodcritic/rules/fc012.rb
|
245
|
+
- lib/foodcritic/rules/fc013.rb
|
246
|
+
- lib/foodcritic/rules/fc014.rb
|
247
|
+
- lib/foodcritic/rules/fc015.rb
|
248
|
+
- lib/foodcritic/rules/fc016.rb
|
249
|
+
- lib/foodcritic/rules/fc017.rb
|
250
|
+
- lib/foodcritic/rules/fc018.rb
|
251
|
+
- lib/foodcritic/rules/fc019.rb
|
252
|
+
- lib/foodcritic/rules/fc021.rb
|
253
|
+
- lib/foodcritic/rules/fc022.rb
|
254
|
+
- lib/foodcritic/rules/fc024.rb
|
255
|
+
- lib/foodcritic/rules/fc025.rb
|
256
|
+
- lib/foodcritic/rules/fc026.rb
|
257
|
+
- lib/foodcritic/rules/fc027.rb
|
258
|
+
- lib/foodcritic/rules/fc028.rb
|
259
|
+
- lib/foodcritic/rules/fc029.rb
|
260
|
+
- lib/foodcritic/rules/fc030.rb
|
261
|
+
- lib/foodcritic/rules/fc031.rb
|
262
|
+
- lib/foodcritic/rules/fc032.rb
|
263
|
+
- lib/foodcritic/rules/fc033.rb
|
264
|
+
- lib/foodcritic/rules/fc034.rb
|
265
|
+
- lib/foodcritic/rules/fc037.rb
|
266
|
+
- lib/foodcritic/rules/fc038.rb
|
267
|
+
- lib/foodcritic/rules/fc039.rb
|
268
|
+
- lib/foodcritic/rules/fc040.rb
|
269
|
+
- lib/foodcritic/rules/fc041.rb
|
270
|
+
- lib/foodcritic/rules/fc042.rb
|
271
|
+
- lib/foodcritic/rules/fc043.rb
|
272
|
+
- lib/foodcritic/rules/fc044.rb
|
273
|
+
- lib/foodcritic/rules/fc045.rb
|
274
|
+
- lib/foodcritic/rules/fc046.rb
|
275
|
+
- lib/foodcritic/rules/fc047.rb
|
276
|
+
- lib/foodcritic/rules/fc048.rb
|
277
|
+
- lib/foodcritic/rules/fc049.rb
|
278
|
+
- lib/foodcritic/rules/fc050.rb
|
279
|
+
- lib/foodcritic/rules/fc051.rb
|
280
|
+
- lib/foodcritic/rules/fc052.rb
|
281
|
+
- lib/foodcritic/rules/fc053.rb
|
282
|
+
- lib/foodcritic/rules/fc055.rb
|
283
|
+
- lib/foodcritic/rules/fc056.rb
|
284
|
+
- lib/foodcritic/rules/fc057.rb
|
285
|
+
- lib/foodcritic/rules/fc058.rb
|
286
|
+
- lib/foodcritic/rules/fc059.rb
|
287
|
+
- lib/foodcritic/rules/fc060.rb
|
288
|
+
- lib/foodcritic/rules/fc061.rb
|
289
|
+
- lib/foodcritic/rules/fc062.rb
|
290
|
+
- lib/foodcritic/rules/fc063.rb
|
291
|
+
- lib/foodcritic/rules/fc064.rb
|
292
|
+
- lib/foodcritic/rules/fc065.rb
|
236
293
|
- lib/foodcritic/template.rb
|
237
294
|
- lib/foodcritic/version.rb
|
238
295
|
- lib/foodcritic/xml.rb
|
@@ -242,6 +299,31 @@ files:
|
|
242
299
|
- spec/foodcritic/api_spec.rb
|
243
300
|
- spec/foodcritic/chef_spec.rb
|
244
301
|
- spec/foodcritic/command_line_spec.rb
|
302
|
+
- spec/foodcritic/coverage/assets/0.10.0/application.css
|
303
|
+
- spec/foodcritic/coverage/assets/0.10.0/application.js
|
304
|
+
- spec/foodcritic/coverage/assets/0.10.0/colorbox/border.png
|
305
|
+
- spec/foodcritic/coverage/assets/0.10.0/colorbox/controls.png
|
306
|
+
- spec/foodcritic/coverage/assets/0.10.0/colorbox/loading.gif
|
307
|
+
- spec/foodcritic/coverage/assets/0.10.0/colorbox/loading_background.png
|
308
|
+
- spec/foodcritic/coverage/assets/0.10.0/favicon_green.png
|
309
|
+
- spec/foodcritic/coverage/assets/0.10.0/favicon_red.png
|
310
|
+
- spec/foodcritic/coverage/assets/0.10.0/favicon_yellow.png
|
311
|
+
- spec/foodcritic/coverage/assets/0.10.0/loading.gif
|
312
|
+
- spec/foodcritic/coverage/assets/0.10.0/magnify.png
|
313
|
+
- spec/foodcritic/coverage/assets/0.10.0/smoothness/images/ui-bg_flat_0_aaaaaa_40x100.png
|
314
|
+
- spec/foodcritic/coverage/assets/0.10.0/smoothness/images/ui-bg_flat_75_ffffff_40x100.png
|
315
|
+
- spec/foodcritic/coverage/assets/0.10.0/smoothness/images/ui-bg_glass_55_fbf9ee_1x400.png
|
316
|
+
- spec/foodcritic/coverage/assets/0.10.0/smoothness/images/ui-bg_glass_65_ffffff_1x400.png
|
317
|
+
- spec/foodcritic/coverage/assets/0.10.0/smoothness/images/ui-bg_glass_75_dadada_1x400.png
|
318
|
+
- spec/foodcritic/coverage/assets/0.10.0/smoothness/images/ui-bg_glass_75_e6e6e6_1x400.png
|
319
|
+
- spec/foodcritic/coverage/assets/0.10.0/smoothness/images/ui-bg_glass_95_fef1ec_1x400.png
|
320
|
+
- spec/foodcritic/coverage/assets/0.10.0/smoothness/images/ui-bg_highlight-soft_75_cccccc_1x100.png
|
321
|
+
- spec/foodcritic/coverage/assets/0.10.0/smoothness/images/ui-icons_222222_256x240.png
|
322
|
+
- spec/foodcritic/coverage/assets/0.10.0/smoothness/images/ui-icons_2e83ff_256x240.png
|
323
|
+
- spec/foodcritic/coverage/assets/0.10.0/smoothness/images/ui-icons_454545_256x240.png
|
324
|
+
- spec/foodcritic/coverage/assets/0.10.0/smoothness/images/ui-icons_888888_256x240.png
|
325
|
+
- spec/foodcritic/coverage/assets/0.10.0/smoothness/images/ui-icons_cd0a0a_256x240.png
|
326
|
+
- spec/foodcritic/coverage/index.html
|
245
327
|
- spec/foodcritic/domain_spec.rb
|
246
328
|
- spec/foodcritic/linter_spec.rb
|
247
329
|
- spec/foodcritic/template_spec.rb
|
@@ -270,8 +352,8 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
270
352
|
version: '0'
|
271
353
|
requirements: []
|
272
354
|
rubyforge_project:
|
273
|
-
rubygems_version: 2.6.
|
355
|
+
rubygems_version: 2.6.11
|
274
356
|
signing_key:
|
275
357
|
specification_version: 4
|
276
|
-
summary: foodcritic-10.
|
358
|
+
summary: foodcritic-10.1.0
|
277
359
|
test_files: []
|
@@ -1,52 +0,0 @@
|
|
1
|
-
Feature: Check for condition around resource
|
2
|
-
|
3
|
-
In order to express conditions in a idiomatic way
|
4
|
-
As a developer
|
5
|
-
I want to identify resources nested in a condition that would be better expressed as a conditional attribute
|
6
|
-
|
7
|
-
Scenario: No conditions
|
8
|
-
Given a cookbook recipe that declares a resource with no conditions at all
|
9
|
-
When I check the cookbook
|
10
|
-
Then the prefer conditional attributes warning 023 should not be displayed
|
11
|
-
|
12
|
-
Scenario Outline: Resource wrapped in condition
|
13
|
-
Given a cookbook recipe that declares a resource nested in a <wrapping_condition> condition with <condition_attribute>
|
14
|
-
When I check the cookbook
|
15
|
-
Then the prefer conditional attributes warning 023 <warning>
|
16
|
-
|
17
|
-
Examples:
|
18
|
-
| wrapping_condition | condition_attribute | warning |
|
19
|
-
| if | no condition attribute | should be displayed |
|
20
|
-
| unless | no condition attribute | should be displayed |
|
21
|
-
| if_else | no condition attribute | should not be displayed |
|
22
|
-
| unless_else | no condition attribute | should not be displayed |
|
23
|
-
| if_elsif | no condition attribute | should not be displayed |
|
24
|
-
| if_elsif_else | no condition attribute | should not be displayed |
|
25
|
-
| if | only_if block | should not be displayed |
|
26
|
-
| if | only_if string | should not be displayed |
|
27
|
-
| unless | only_if block | should not be displayed |
|
28
|
-
| unless | only_if string | should not be displayed |
|
29
|
-
| if | not_if block | should not be displayed |
|
30
|
-
| if | not_if string | should not be displayed |
|
31
|
-
| unless | not_if block | should not be displayed |
|
32
|
-
| unless | not_if string | should not be displayed |
|
33
|
-
|
34
|
-
Scenario: Wrapped condition includes Ruby statements
|
35
|
-
Given a cookbook recipe that has a wrapping condition containing a resource with no condition attribute and a Ruby statement
|
36
|
-
When I check the cookbook
|
37
|
-
Then the prefer conditional attributes warning 023 should not be displayed
|
38
|
-
|
39
|
-
Scenario: Wrapped condition includes resource in a loop
|
40
|
-
Given a cookbook recipe that has a wrapping condition containing a resource with no condition attribute within a loop
|
41
|
-
When I check the cookbook
|
42
|
-
Then the prefer conditional attributes warning 023 should not be displayed
|
43
|
-
|
44
|
-
Scenario Outline: Multiple nested resources
|
45
|
-
Given a cookbook recipe that declares multiple resources nested in a <wrapping_condition> condition with no condition attribute
|
46
|
-
When I check the cookbook
|
47
|
-
Then the prefer conditional attributes warning 023 should not be displayed
|
48
|
-
|
49
|
-
Examples:
|
50
|
-
| wrapping_condition |
|
51
|
-
| if |
|
52
|
-
| unless |
|
data/lib/foodcritic/rules.rb
DELETED
@@ -1,852 +0,0 @@
|
|
1
|
-
# This file contains all of the rules that ship with foodcritic.
|
2
|
-
#
|
3
|
-
# * Foodcritic rules perform static code analysis - rather than the cookbook
|
4
|
-
# code being loaded by the interpreter it is parsed into a tree (AST) that is
|
5
|
-
# then passed to each rule.
|
6
|
-
# * Rules can use a number of API functions that ship with foodcritic to make
|
7
|
-
# sense of the parse tree.
|
8
|
-
# * Rules can also use XPath to query the AST. A rule can consist of a XPath
|
9
|
-
# query only, as any nodes returned from a `recipe` block will be converted
|
10
|
-
# into warnings.
|
11
|
-
|
12
|
-
rule "FC001",
|
13
|
-
"Use strings in preference to symbols to access node attributes" do
|
14
|
-
tags %w{style attributes}
|
15
|
-
recipe do |ast|
|
16
|
-
attribute_access(ast, type: :symbol)
|
17
|
-
end
|
18
|
-
end
|
19
|
-
|
20
|
-
rule "FC002", "Avoid string interpolation where not required" do
|
21
|
-
tags %w{style strings}
|
22
|
-
recipe do |ast|
|
23
|
-
ast.xpath(%q{//*[self::string_literal | self::assoc_new]/string_add[
|
24
|
-
count(descendant::string_embexpr) = 1 and
|
25
|
-
count(string_add) = 0]})
|
26
|
-
end
|
27
|
-
end
|
28
|
-
|
29
|
-
# FC003 was yanked and the number should not be reused
|
30
|
-
|
31
|
-
rule "FC004", "Use a service resource to start and stop services" do
|
32
|
-
tags %w{style services}
|
33
|
-
recipe do |ast|
|
34
|
-
find_resources(ast, type: "execute").find_all do |cmd|
|
35
|
-
cmd_str = (resource_attribute(cmd, "command") || resource_name(cmd)).to_s
|
36
|
-
(cmd_str.include?("/etc/init.d") || ["service ", "/sbin/service ",
|
37
|
-
"start ", "stop ", "invoke-rc.d "].any? do |service_cmd|
|
38
|
-
cmd_str.start_with?(service_cmd)
|
39
|
-
end) && %w{start stop restart reload}.any? { |a| cmd_str.include?(a) }
|
40
|
-
end
|
41
|
-
end
|
42
|
-
end
|
43
|
-
|
44
|
-
rule "FC005", "Avoid repetition of resource declarations" do
|
45
|
-
tags %w{style}
|
46
|
-
recipe do |ast|
|
47
|
-
resources = find_resources(ast).map do |res|
|
48
|
-
resource_attributes(res).merge({ type: resource_type(res),
|
49
|
-
ast: res })
|
50
|
-
end.chunk do |res|
|
51
|
-
res[:type] +
|
52
|
-
res[:ast].xpath("ancestor::*[self::if | self::unless | self::elsif |
|
53
|
-
self::else | self::when | self::method_add_block/call][position() = 1]/
|
54
|
-
descendant::pos[position() = 1]").to_s +
|
55
|
-
res[:ast].xpath("ancestor::method_add_block/command[
|
56
|
-
ident/@value='action']/args_add_block/descendant::ident/@value").to_s
|
57
|
-
end.reject { |res| res[1].size < 3 }
|
58
|
-
resources.map do |cont_res|
|
59
|
-
first_resource = cont_res[1][0][:ast]
|
60
|
-
# we have contiguous resources of the same type, but do they share the
|
61
|
-
# same attributes?
|
62
|
-
sorted_atts = cont_res[1].map do |atts|
|
63
|
-
atts.delete_if { |k| k == :ast }.to_a.sort do |x, y|
|
64
|
-
x.first.to_s <=> y.first.to_s
|
65
|
-
end
|
66
|
-
end
|
67
|
-
first_resource if sorted_atts.all? do |att|
|
68
|
-
(att - sorted_atts.inject { |atts, a| atts & a }).length == 1
|
69
|
-
end
|
70
|
-
end.compact
|
71
|
-
end
|
72
|
-
end
|
73
|
-
|
74
|
-
rule "FC006",
|
75
|
-
"Mode should be quoted or fully specified when "\
|
76
|
-
"setting file permissions" do
|
77
|
-
tags %w{correctness files}
|
78
|
-
recipe do |ast|
|
79
|
-
ast.xpath(%q{//ident[@value='mode']/parent::command/
|
80
|
-
descendant::int[string-length(@value) < 5
|
81
|
-
and not(starts-with(@value, "0")
|
82
|
-
and string-length(@value) = 4)][count(ancestor::aref) = 0]/
|
83
|
-
ancestor::method_add_block})
|
84
|
-
end
|
85
|
-
end
|
86
|
-
|
87
|
-
rule "FC007", "Ensure recipe dependencies are reflected "\
|
88
|
-
"in cookbook metadata" do
|
89
|
-
tags %w{correctness metadata}
|
90
|
-
recipe do |ast, filename|
|
91
|
-
metadata_path = Pathname.new(
|
92
|
-
File.join(File.dirname(filename), "..", "metadata.rb")).cleanpath
|
93
|
-
next unless File.exist? metadata_path
|
94
|
-
actual_included = included_recipes(ast, with_partial_names: false)
|
95
|
-
undeclared = actual_included.keys.map do |recipe|
|
96
|
-
recipe.split("::").first
|
97
|
-
end - [cookbook_name(filename)] -
|
98
|
-
declared_dependencies(read_ast(metadata_path))
|
99
|
-
actual_included.map do |recipe, include_stmts|
|
100
|
-
if undeclared.include?(recipe) ||
|
101
|
-
undeclared.any? { |u| recipe.start_with?("#{u}::") }
|
102
|
-
include_stmts
|
103
|
-
end
|
104
|
-
end.flatten.compact
|
105
|
-
end
|
106
|
-
end
|
107
|
-
|
108
|
-
rule "FC008", "Generated cookbook metadata needs updating" do
|
109
|
-
tags %w{style metadata}
|
110
|
-
metadata do |ast, filename|
|
111
|
-
{
|
112
|
-
"maintainer" => "YOUR_COMPANY_NAME",
|
113
|
-
"maintainer_email" => "YOUR_EMAIL",
|
114
|
-
}.map do |field, value|
|
115
|
-
ast.xpath(%Q{//command[ident/@value='#{field}']/
|
116
|
-
descendant::tstring_content[@value='#{value}']})
|
117
|
-
end
|
118
|
-
end
|
119
|
-
end
|
120
|
-
|
121
|
-
rule "FC009", "Resource attribute not recognised" do
|
122
|
-
tags %w{correctness}
|
123
|
-
recipe do |ast|
|
124
|
-
matches = []
|
125
|
-
resource_attributes_by_type(ast).each do |type, resources|
|
126
|
-
resources.each do |resource|
|
127
|
-
resource.keys.map(&:to_sym).reject do |att|
|
128
|
-
resource_attribute?(type.to_sym, att)
|
129
|
-
end.each do |invalid_att|
|
130
|
-
matches << find_resources(ast, type: type).find do |res|
|
131
|
-
resource_attributes(res).include?(invalid_att.to_s)
|
132
|
-
end
|
133
|
-
end
|
134
|
-
end
|
135
|
-
end
|
136
|
-
matches
|
137
|
-
end
|
138
|
-
end
|
139
|
-
|
140
|
-
rule "FC010", "Invalid search syntax" do
|
141
|
-
tags %w{correctness search}
|
142
|
-
recipe do |ast|
|
143
|
-
# This only works for literal search strings
|
144
|
-
literal_searches(ast).reject { |search| valid_query?(search["value"]) }
|
145
|
-
end
|
146
|
-
end
|
147
|
-
|
148
|
-
rule "FC011", "Missing README in markdown format" do
|
149
|
-
tags %w{style readme}
|
150
|
-
cookbook do |filename|
|
151
|
-
unless File.exist?(File.join(filename, "README.md"))
|
152
|
-
[file_match(File.join(filename, "README.md"))]
|
153
|
-
end
|
154
|
-
end
|
155
|
-
end
|
156
|
-
|
157
|
-
rule "FC012", "Use Markdown for README rather than RDoc" do
|
158
|
-
tags %w{style readme}
|
159
|
-
cookbook do |filename|
|
160
|
-
if File.exist?(File.join(filename, "README.rdoc"))
|
161
|
-
[file_match(File.join(filename, "README.rdoc"))]
|
162
|
-
end
|
163
|
-
end
|
164
|
-
end
|
165
|
-
|
166
|
-
rule "FC013", "Use file_cache_path rather than hard-coding tmp paths" do
|
167
|
-
tags %w{style files}
|
168
|
-
recipe do |ast|
|
169
|
-
find_resources(ast, type: "remote_file").find_all do |download|
|
170
|
-
path = (resource_attribute(download, "path") ||
|
171
|
-
resource_name(download)).to_s
|
172
|
-
path.start_with?("/tmp/")
|
173
|
-
end
|
174
|
-
end
|
175
|
-
end
|
176
|
-
|
177
|
-
rule "FC014", "Consider extracting long ruby_block to library" do
|
178
|
-
tags %w{style libraries}
|
179
|
-
recipe do |ast|
|
180
|
-
find_resources(ast, type: "ruby_block").find_all do |rb|
|
181
|
-
lines = rb.xpath("descendant::fcall[ident/@value='block']/../../
|
182
|
-
descendant::*[@line]/@line").map { |n| n.value.to_i }.sort
|
183
|
-
(!lines.empty?) && (lines.last - lines.first) > 15
|
184
|
-
end
|
185
|
-
end
|
186
|
-
end
|
187
|
-
|
188
|
-
rule "FC015", "Consider converting definition to a Custom Resource" do
|
189
|
-
tags %w{style definitions lwrp}
|
190
|
-
cookbook do |dir|
|
191
|
-
Dir[File.join(dir, "definitions", "*.rb")].reject do |entry|
|
192
|
-
[".", ".."].include? entry
|
193
|
-
end.map { |entry| file_match(entry) }
|
194
|
-
end
|
195
|
-
end
|
196
|
-
|
197
|
-
rule "FC016", "LWRP does not declare a default action" do
|
198
|
-
tags %w{correctness lwrp}
|
199
|
-
resource do |ast, filename|
|
200
|
-
unless ["//ident/@value='default_action'",
|
201
|
-
"//def/bodystmt/descendant::assign/
|
202
|
-
var_field/ivar/@value='@action'"].any? { |expr| ast.xpath(expr) }
|
203
|
-
[file_match(filename)]
|
204
|
-
end
|
205
|
-
end
|
206
|
-
end
|
207
|
-
|
208
|
-
rule "FC017", "LWRP does not notify when updated" do
|
209
|
-
tags %w{correctness lwrp}
|
210
|
-
provider do |ast, filename|
|
211
|
-
|
212
|
-
use_inline_resources = !ast.xpath('//*[self::vcall or self::var_ref]/ident
|
213
|
-
[@value="use_inline_resources"]').empty?
|
214
|
-
|
215
|
-
unless use_inline_resources
|
216
|
-
actions = ast.xpath('//method_add_block/command[ident/@value="action"]/
|
217
|
-
args_add_block/descendant::symbol/ident')
|
218
|
-
|
219
|
-
actions.reject do |action|
|
220
|
-
blk = action.xpath('ancestor::command[1]/
|
221
|
-
following-sibling::*[self::do_block or self::brace_block]')
|
222
|
-
empty = !blk.xpath("stmts_add/void_stmt").empty?
|
223
|
-
converge_by = !blk.xpath('descendant::*[self::command or self::fcall]
|
224
|
-
/ident[@value="converge_by"]').empty?
|
225
|
-
|
226
|
-
updated_by_last_action = !blk.xpath('descendant::*[self::call or
|
227
|
-
self::command_call]/*[self::vcall or self::var_ref/ident/
|
228
|
-
@value="new_resource"]/../ident[@value="updated_by_last_action"]
|
229
|
-
').empty?
|
230
|
-
|
231
|
-
empty || converge_by || updated_by_last_action
|
232
|
-
end
|
233
|
-
end
|
234
|
-
|
235
|
-
end
|
236
|
-
end
|
237
|
-
|
238
|
-
rule "FC018", "LWRP uses deprecated notification syntax" do
|
239
|
-
tags %w{style lwrp deprecated}
|
240
|
-
provider do |ast|
|
241
|
-
ast.xpath("//assign/var_field/ivar[@value='@updated']").map do |class_var|
|
242
|
-
match(class_var)
|
243
|
-
end + ast.xpath(%q{//assign/field/*[self::vcall or self::var_ref/ident/
|
244
|
-
@value='new_resource']/../ident[@value='updated']})
|
245
|
-
end
|
246
|
-
end
|
247
|
-
|
248
|
-
rule "FC019", "Access node attributes in a consistent manner" do
|
249
|
-
tags %w{style attributes}
|
250
|
-
cookbook do |cookbook_dir|
|
251
|
-
asts = {}; files = Dir["#{cookbook_dir}/*/*.rb"].reject do |file|
|
252
|
-
relative_path = Pathname.new(file).relative_path_from(
|
253
|
-
Pathname.new(cookbook_dir))
|
254
|
-
relative_path.to_s.split(File::SEPARATOR).include?("spec")
|
255
|
-
end.map do |file|
|
256
|
-
{ path: file, ast: read_ast(file) }
|
257
|
-
end
|
258
|
-
types = [:string, :symbol, :vivified].map do |type|
|
259
|
-
{
|
260
|
-
access_type: type, count: files.map do |file|
|
261
|
-
attribute_access(file[:ast], type: type, ignore_calls: true,
|
262
|
-
cookbook_dir: cookbook_dir, ignore: "run_state").tap do |ast|
|
263
|
-
unless ast.empty?
|
264
|
-
(asts[type] ||= []) << { ast: ast, path: file[:path] }
|
265
|
-
end
|
266
|
-
end.size
|
267
|
-
end.inject(:+)
|
268
|
-
}
|
269
|
-
end.reject { |type| type[:count] == 0 }
|
270
|
-
if asts.size > 1
|
271
|
-
least_used = asts[types.min do |a, b|
|
272
|
-
a[:count] <=> b[:count]
|
273
|
-
end[:access_type]]
|
274
|
-
least_used.map do |file|
|
275
|
-
file[:ast].map do |ast|
|
276
|
-
match(ast).merge(filename: file[:path])
|
277
|
-
end.flatten
|
278
|
-
end
|
279
|
-
end
|
280
|
-
end
|
281
|
-
end
|
282
|
-
|
283
|
-
rule "FC021", "Resource condition in provider may not behave as expected" do
|
284
|
-
tags %w{correctness lwrp}
|
285
|
-
provider do |ast|
|
286
|
-
find_resources(ast).map do |resource|
|
287
|
-
condition = resource.xpath(%q{//method_add_block/
|
288
|
-
descendant::ident[@value='not_if' or @value='only_if']/
|
289
|
-
ancestor::*[self::method_add_block or self::command][1][descendant::
|
290
|
-
ident/@value='new_resource']/ancestor::stmts_add[2]/method_add_block/
|
291
|
-
command[count(descendant::string_embexpr) = 0]})
|
292
|
-
condition
|
293
|
-
end.compact
|
294
|
-
end
|
295
|
-
end
|
296
|
-
|
297
|
-
rule "FC022", "Resource condition within loop may not behave as expected" do
|
298
|
-
tags %w{correctness}
|
299
|
-
recipe do |ast|
|
300
|
-
ast.xpath("//call[ident/@value='each']/../do_block[count(ancestor::
|
301
|
-
method_add_block/method_add_arg/fcall/ident[@value='only_if' or
|
302
|
-
@value = 'not_if']) = 0]").map do |lp|
|
303
|
-
block_vars = lp.xpath("block_var/params/child::*").map do |n|
|
304
|
-
n.name.sub(/^ident/, "")
|
305
|
-
end + lp.xpath("block_var/params/child::*/descendant::ident").map do |v|
|
306
|
-
v["value"]
|
307
|
-
end
|
308
|
-
find_resources(lp).map do |resource|
|
309
|
-
# if any of the parameters to the block are used in a condition then we
|
310
|
-
# have a match
|
311
|
-
unless (block_vars &
|
312
|
-
(resource.xpath(%q{descendant::ident[@value='not_if' or
|
313
|
-
@value='only_if']/ancestor::*[self::method_add_block or
|
314
|
-
self::command][1]/descendant::ident/@value}).map do |a|
|
315
|
-
a.value
|
316
|
-
end)).empty?
|
317
|
-
c = resource.xpath("command[count(descendant::string_embexpr) = 0]")
|
318
|
-
if resource.xpath("command/ident/@value").first.value == "define"
|
319
|
-
next
|
320
|
-
end
|
321
|
-
resource unless c.empty? || block_vars.any? do |var|
|
322
|
-
!resource.xpath(%Q{command/args_add_block/args_add/
|
323
|
-
var_ref/ident[@value='#{var}']}).empty?
|
324
|
-
end
|
325
|
-
end
|
326
|
-
end
|
327
|
-
end.flatten.compact
|
328
|
-
end
|
329
|
-
end
|
330
|
-
|
331
|
-
rule "FC023", "Prefer conditional attributes" do
|
332
|
-
tags %w{style}
|
333
|
-
recipe do |ast|
|
334
|
-
ast.xpath(%q{//method_add_block[command/ident][count(descendant::ident
|
335
|
-
[@value='only_if' or @value='not_if']) = 0]/ancestor::*[self::if or
|
336
|
-
self::unless][count(descendant::method_add_block[command/ident]) = 1]
|
337
|
-
[count(stmts_add/method_add_block/call) = 0]
|
338
|
-
[count(stmts_add/stmts_add) = 0]
|
339
|
-
[count(descendant::*[self::else or self::elsif]) = 0]})
|
340
|
-
end
|
341
|
-
end
|
342
|
-
|
343
|
-
rule "FC024", "Consider adding platform equivalents" do
|
344
|
-
tags %w{portability}
|
345
|
-
RHEL = %w{amazon centos redhat scientific oracle}
|
346
|
-
recipe do |ast, filename|
|
347
|
-
next if Pathname.new(filename).basename.to_s == "metadata.rb"
|
348
|
-
metadata_path = Pathname.new(
|
349
|
-
File.join(File.dirname(filename), "..", "metadata.rb")).cleanpath
|
350
|
-
md_platforms = if File.exist?(metadata_path)
|
351
|
-
supported_platforms(read_ast(
|
352
|
-
metadata_path)).map { |p| p[:platform] }
|
353
|
-
else
|
354
|
-
[]
|
355
|
-
end
|
356
|
-
md_platforms = RHEL if md_platforms.empty?
|
357
|
-
|
358
|
-
['//method_add_arg[fcall/ident/@value="platform?"]/
|
359
|
-
arg_paren/args_add_block',
|
360
|
-
"//when"].map do |expr|
|
361
|
-
ast.xpath(expr).map do |whn|
|
362
|
-
platforms = whn.xpath('args_add/
|
363
|
-
descendant::tstring_content').map do |p|
|
364
|
-
p["value"]
|
365
|
-
end.sort
|
366
|
-
unless platforms.size == 1 || (md_platforms & platforms).empty?
|
367
|
-
whn unless (platforms & RHEL).empty? ||
|
368
|
-
((md_platforms & RHEL) - (platforms & RHEL)).empty?
|
369
|
-
end
|
370
|
-
end.compact
|
371
|
-
end.flatten
|
372
|
-
end
|
373
|
-
end
|
374
|
-
|
375
|
-
rule "FC025", "Prefer chef_gem to compile-time gem install" do
|
376
|
-
tags %w{style deprecated}
|
377
|
-
recipe do |ast|
|
378
|
-
gem_install = ast.xpath("//stmts_add/assign[method_add_block[command/ident/
|
379
|
-
@value='gem_package'][do_block/stmts_add/command[ident/@value='action']
|
380
|
-
[descendant::ident/@value='nothing']]]")
|
381
|
-
gem_install.map do |install|
|
382
|
-
gem_var = install.xpath("var_field/ident/@value")
|
383
|
-
unless ast.xpath("//method_add_arg[call/
|
384
|
-
var_ref/ident/@value='#{gem_var}']
|
385
|
-
[arg_paren/descendant::ident/@value='install' or
|
386
|
-
arg_paren/descendant::ident/@value='upgrade']").empty?
|
387
|
-
gem_install
|
388
|
-
end
|
389
|
-
end
|
390
|
-
end
|
391
|
-
end
|
392
|
-
|
393
|
-
rule "FC026", "Conditional execution block attribute contains only string" do
|
394
|
-
tags %w{correctness}
|
395
|
-
recipe do |ast|
|
396
|
-
find_resources(ast).map { |r| resource_attributes(r) }.map do |resource|
|
397
|
-
[resource["not_if"], resource["only_if"]]
|
398
|
-
end.flatten.compact.select do |condition|
|
399
|
-
condition.respond_to?(:xpath) &&
|
400
|
-
!condition.xpath("descendant::string_literal").empty? &&
|
401
|
-
!condition.xpath("stmts_add/string_literal").empty? &&
|
402
|
-
condition.xpath('descendant::stmts_add[count(ancestor::
|
403
|
-
string_literal) = 0]').size == 1
|
404
|
-
end
|
405
|
-
end
|
406
|
-
end
|
407
|
-
|
408
|
-
rule "FC027", "Resource sets internal attribute" do
|
409
|
-
tags %w{correctness}
|
410
|
-
recipe do |ast|
|
411
|
-
find_resources(ast, type: :service).map do |service|
|
412
|
-
service unless (resource_attributes(service).keys &
|
413
|
-
%w{enabled running}).empty?
|
414
|
-
end.compact
|
415
|
-
end
|
416
|
-
end
|
417
|
-
|
418
|
-
rule "FC028", "Incorrect #platform? usage" do
|
419
|
-
tags %w{correctness}
|
420
|
-
recipe do |ast|
|
421
|
-
ast.xpath(%q{//*[self::call | self::command_call]
|
422
|
-
[(var_ref|vcall)/ident/@value='node']
|
423
|
-
[ident/@value="platform?"]})
|
424
|
-
end
|
425
|
-
end
|
426
|
-
|
427
|
-
rule "FC029", "No leading cookbook name in recipe metadata" do
|
428
|
-
tags %w{correctness metadata}
|
429
|
-
metadata do |ast, filename|
|
430
|
-
ast.xpath('//command[ident/@value="recipe"]').map do |declared_recipe|
|
431
|
-
next unless declared_recipe.xpath("count(//vcall|//var_ref)").to_i == 0
|
432
|
-
recipe_name = declared_recipe.xpath('args_add_block/
|
433
|
-
descendant::tstring_content[1]/@value').to_s
|
434
|
-
unless recipe_name.empty? ||
|
435
|
-
recipe_name.split("::").first == cookbook_name(filename.to_s)
|
436
|
-
declared_recipe
|
437
|
-
end
|
438
|
-
end.compact
|
439
|
-
end
|
440
|
-
end
|
441
|
-
|
442
|
-
rule "FC030", "Cookbook contains debugger breakpoints" do
|
443
|
-
tags %w{annoyances}
|
444
|
-
def pry_bindings(ast)
|
445
|
-
ast.xpath('//call[(vcall|var_ref)/ident/@value="binding"]
|
446
|
-
[ident/@value="pry"]')
|
447
|
-
end
|
448
|
-
recipe { |ast| pry_bindings(ast) }
|
449
|
-
library { |ast| pry_bindings(ast) }
|
450
|
-
metadata { |ast| pry_bindings(ast) }
|
451
|
-
template { |ast| pry_bindings(ast) }
|
452
|
-
end
|
453
|
-
|
454
|
-
rule "FC031", "Cookbook without metadata file" do
|
455
|
-
tags %w{correctness metadata}
|
456
|
-
cookbook do |filename|
|
457
|
-
if !File.exist?(File.join(filename, "metadata.rb"))
|
458
|
-
[file_match(File.join(filename, "metadata.rb"))]
|
459
|
-
end
|
460
|
-
end
|
461
|
-
end
|
462
|
-
|
463
|
-
rule "FC032", "Invalid notification timing" do
|
464
|
-
tags %w{correctness notifications}
|
465
|
-
recipe do |ast|
|
466
|
-
valid_timings = if resource_attribute?("file", "notifies_before")
|
467
|
-
[:delayed, :immediate, :before]
|
468
|
-
else
|
469
|
-
[:delayed, :immediate]
|
470
|
-
end
|
471
|
-
find_resources(ast).select do |resource|
|
472
|
-
notifications(resource).any? do |notification|
|
473
|
-
! valid_timings.include? notification[:timing]
|
474
|
-
end
|
475
|
-
end
|
476
|
-
end
|
477
|
-
end
|
478
|
-
|
479
|
-
rule "FC033", "Missing template" do
|
480
|
-
tags %w{correctness}
|
481
|
-
recipe do |ast, filename|
|
482
|
-
find_resources(ast, type: :template).reject do |resource|
|
483
|
-
resource_attributes(resource)["local"] ||
|
484
|
-
resource_attributes(resource)["cookbook"]
|
485
|
-
end.map do |resource|
|
486
|
-
file = template_file(resource_attributes(resource,
|
487
|
-
return_expressions: true))
|
488
|
-
{ resource: resource, file: file }
|
489
|
-
end.reject do |resource|
|
490
|
-
resource[:file].respond_to?(:xpath)
|
491
|
-
end.select do |resource|
|
492
|
-
template_paths(filename).none? do |path|
|
493
|
-
relative_path = []
|
494
|
-
Pathname.new(path).ascend do |template_path|
|
495
|
-
relative_path << template_path.basename
|
496
|
-
break if gem_version(chef_version) >= gem_version("12.0.0") &&
|
497
|
-
template_path.dirname.basename.to_s == "templates"
|
498
|
-
break if template_path.dirname.dirname.basename.to_s == "templates"
|
499
|
-
end
|
500
|
-
File.join(relative_path.reverse) == resource[:file]
|
501
|
-
end
|
502
|
-
end.map { |resource| resource[:resource] }
|
503
|
-
end
|
504
|
-
end
|
505
|
-
|
506
|
-
rule "FC034", "Unused template variables" do
|
507
|
-
tags %w{correctness}
|
508
|
-
recipe do |ast, filename|
|
509
|
-
Array(resource_attributes_by_type(ast)["template"]).select do |t|
|
510
|
-
t["variables"] && t["variables"].respond_to?(:xpath)
|
511
|
-
end.map do |resource|
|
512
|
-
all_templates = template_paths(filename)
|
513
|
-
template_paths = all_templates.select do |path|
|
514
|
-
File.basename(path) == template_file(resource)
|
515
|
-
end
|
516
|
-
next unless template_paths.any?
|
517
|
-
passed_vars = resource["variables"].xpath(
|
518
|
-
"symbol/ident/@value").map { |tv| tv.to_s }
|
519
|
-
|
520
|
-
unused_vars_exist = template_paths.all? do |template_path|
|
521
|
-
begin
|
522
|
-
template_vars = templates_included(
|
523
|
-
all_templates, template_path).map do |template|
|
524
|
-
read_ast(template).xpath("//var_ref/ivar/@value").map do |v|
|
525
|
-
v.to_s.sub(/^@/, "")
|
526
|
-
end
|
527
|
-
end.flatten
|
528
|
-
! (passed_vars - template_vars).empty?
|
529
|
-
rescue RecursedTooFarError
|
530
|
-
false
|
531
|
-
end
|
532
|
-
end
|
533
|
-
file_match(template_paths.first) if unused_vars_exist
|
534
|
-
end.compact
|
535
|
-
end
|
536
|
-
end
|
537
|
-
|
538
|
-
rule "FC037", "Invalid notification action" do
|
539
|
-
tags %w{correctness}
|
540
|
-
recipe do |ast|
|
541
|
-
find_resources(ast).select do |resource|
|
542
|
-
notifications(resource).any? do |n|
|
543
|
-
type = case n[:type]
|
544
|
-
when :notifies then n[:resource_type]
|
545
|
-
when :subscribes then resource_type(resource).to_sym
|
546
|
-
end
|
547
|
-
n[:action].size > 0 && !resource_action?(type, n[:action])
|
548
|
-
end
|
549
|
-
end
|
550
|
-
end
|
551
|
-
end
|
552
|
-
|
553
|
-
rule "FC038", "Invalid resource action" do
|
554
|
-
tags %w{correctness}
|
555
|
-
recipe do |ast|
|
556
|
-
find_resources(ast).select do |resource|
|
557
|
-
actions = resource_attributes(resource)["action"]
|
558
|
-
if actions.respond_to?(:xpath)
|
559
|
-
actions = actions.xpath('descendant::array/
|
560
|
-
descendant::symbol/ident/@value')
|
561
|
-
else
|
562
|
-
actions = Array(actions)
|
563
|
-
end
|
564
|
-
actions.reject { |a| a.to_s.empty? }.any? do |action|
|
565
|
-
!resource_action?(resource_type(resource), action)
|
566
|
-
end
|
567
|
-
end
|
568
|
-
end
|
569
|
-
end
|
570
|
-
|
571
|
-
rule "FC039", "Node method cannot be accessed with key" do
|
572
|
-
tags %w{correctness}
|
573
|
-
recipe do |ast|
|
574
|
-
[{ type: :string, path: "@value" },
|
575
|
-
{ type: :symbol, path: "ident/@value" }].map do |access_type|
|
576
|
-
attribute_access(ast, type: access_type[:type]).select do |att|
|
577
|
-
att_name = att.xpath(access_type[:path]).to_s.to_sym
|
578
|
-
att_name != :tags && chef_node_methods.include?(att_name)
|
579
|
-
end.select do |att|
|
580
|
-
!att.xpath('ancestor::args_add_block[position() = 1]
|
581
|
-
[preceding-sibling::vcall | preceding-sibling::var_ref]').empty?
|
582
|
-
end.select do |att|
|
583
|
-
att_type = att.xpath('ancestor::args_add_block[position() = 1]
|
584
|
-
/../var_ref/ident/@value').to_s
|
585
|
-
ast.xpath("//assign/var_field/ident[@value='#{att_type}']").empty?
|
586
|
-
end
|
587
|
-
end.flatten
|
588
|
-
end
|
589
|
-
end
|
590
|
-
|
591
|
-
rule "FC040", "Execute resource used to run git commands" do
|
592
|
-
tags %w{style recipe etsy}
|
593
|
-
recipe do |ast|
|
594
|
-
possible_git_commands = %w{ clone fetch pull checkout reset }
|
595
|
-
find_resources(ast, type: "execute").select do |cmd|
|
596
|
-
cmd_str = (resource_attribute(cmd, "command") || resource_name(cmd)).to_s
|
597
|
-
|
598
|
-
actual_git_commands = cmd_str.scan(/git ([a-z]+)/).map { |c| c.first }
|
599
|
-
(possible_git_commands & actual_git_commands).any?
|
600
|
-
end
|
601
|
-
end
|
602
|
-
end
|
603
|
-
|
604
|
-
rule "FC041", "Execute resource used to run curl or wget commands" do
|
605
|
-
tags %w{style recipe etsy}
|
606
|
-
recipe do |ast|
|
607
|
-
find_resources(ast, type: "execute").select do |cmd|
|
608
|
-
cmd_str = (resource_attribute(cmd, "command") || resource_name(cmd)).to_s
|
609
|
-
(cmd_str.match(/^curl.*(-o|>|--output).*$/) || cmd_str.include?("wget "))
|
610
|
-
end
|
611
|
-
end
|
612
|
-
end
|
613
|
-
|
614
|
-
rule "FC042", "Prefer include_recipe to require_recipe" do
|
615
|
-
tags %w{deprecated}
|
616
|
-
recipe do |ast|
|
617
|
-
ast.xpath('//command[ident/@value="require_recipe"]')
|
618
|
-
end
|
619
|
-
end
|
620
|
-
|
621
|
-
rule "FC043", "Prefer new notification syntax" do
|
622
|
-
tags %w{style notifications deprecated}
|
623
|
-
recipe do |ast|
|
624
|
-
find_resources(ast).select do |resource|
|
625
|
-
notifications(resource).any? { |notify| notify[:style] == :old }
|
626
|
-
end
|
627
|
-
end
|
628
|
-
end
|
629
|
-
|
630
|
-
rule "FC044", "Avoid bare attribute keys" do
|
631
|
-
tags %w{style}
|
632
|
-
attributes do |ast|
|
633
|
-
declared = ast.xpath("//descendant::var_field/ident/@value").map do |v|
|
634
|
-
v.to_s
|
635
|
-
end
|
636
|
-
|
637
|
-
ast.xpath('//assign/*[self::vcall or self::var_ref]
|
638
|
-
[count(child::kw) = 0]/ident').select do |v|
|
639
|
-
|
640
|
-
local_declared = v.xpath("ancestor::*[self::brace_block or self::do_block]
|
641
|
-
/block_var/descendant::ident/@value").map do |v|
|
642
|
-
v.to_s
|
643
|
-
end
|
644
|
-
|
645
|
-
(v["value"] != "secure_password") &&
|
646
|
-
!(declared + local_declared).uniq.include?(v["value"]) &&
|
647
|
-
!v.xpath("ancestor::*[self::brace_block or self::do_block]/block_var/
|
648
|
-
descendant::ident/@value='#{v['value']}'")
|
649
|
-
end
|
650
|
-
end
|
651
|
-
end
|
652
|
-
|
653
|
-
rule "FC045", "Metadata does not contain cookbook name" do
|
654
|
-
tags %w{correctness metadata chef12}
|
655
|
-
metadata do |ast, filename|
|
656
|
-
unless ast.xpath('descendant::stmts_add/command/ident/@value="name"')
|
657
|
-
[file_match(filename)]
|
658
|
-
end
|
659
|
-
end
|
660
|
-
cookbook do |filename|
|
661
|
-
if !File.exist?(File.join(filename, "metadata.rb"))
|
662
|
-
[file_match(File.join(filename, "metadata.rb"))]
|
663
|
-
end
|
664
|
-
end
|
665
|
-
end
|
666
|
-
|
667
|
-
rule "FC046", "Attribute assignment uses assign unless nil" do
|
668
|
-
attributes do |ast|
|
669
|
-
attribute_access(ast).map do |a|
|
670
|
-
a.xpath('ancestor::opassign/op[@value="||="]')
|
671
|
-
end
|
672
|
-
end
|
673
|
-
end
|
674
|
-
|
675
|
-
rule "FC047", "Attribute assignment does not specify precedence" do
|
676
|
-
tags %w{attributes correctness chef11}
|
677
|
-
recipe do |ast|
|
678
|
-
attribute_access(ast).map do |att|
|
679
|
-
exclude_att_types = '[count(following-sibling::ident[
|
680
|
-
is_att_type(@value) or @value = "run_state"]) = 0]'
|
681
|
-
att.xpath(%Q{ancestor::assign[*[self::field | self::aref_field]
|
682
|
-
[descendant::*[self::vcall | self::var_ref][ident/@value="node"]
|
683
|
-
#{exclude_att_types}]]}, AttFilter.new) +
|
684
|
-
att.xpath(%Q{ancestor::binary[@value="<<"]/*[position() = 1]
|
685
|
-
[self::aref]
|
686
|
-
[descendant::*[self::vcall | self::var_ref]#{exclude_att_types}
|
687
|
-
/ident/@value="node"]}, AttFilter.new)
|
688
|
-
end
|
689
|
-
end
|
690
|
-
end
|
691
|
-
|
692
|
-
rule "FC048", "Prefer Mixlib::ShellOut" do
|
693
|
-
tags %w{style processes}
|
694
|
-
recipe do |ast|
|
695
|
-
xstring_literal = ast.xpath("//xstring_literal")
|
696
|
-
next xstring_literal if xstring_literal.any?
|
697
|
-
|
698
|
-
ast.xpath('//*[self::command or self::fcall]/ident[@value="system"]').select do |x|
|
699
|
-
resource_name = x.xpath("ancestor::do_block/preceding-sibling::command/ident/@value")
|
700
|
-
next false if resource_name.any? && resource_name.all? { |r| resource_attribute?(r.to_s, "system") }
|
701
|
-
next x.xpath('count(following-sibling::args_add_block/descendant::kw[@value="true" or @value="false"]) = 0')
|
702
|
-
end
|
703
|
-
end
|
704
|
-
end
|
705
|
-
|
706
|
-
rule "FC049", "Role name does not match containing file name" do
|
707
|
-
tags %w{style roles}
|
708
|
-
role do |ast, filename|
|
709
|
-
role_name_specified = field_value(ast, :name)
|
710
|
-
role_name_file = Pathname.new(filename).basename.sub_ext("").to_s
|
711
|
-
if role_name_specified && role_name_specified != role_name_file
|
712
|
-
field(ast, :name)
|
713
|
-
end
|
714
|
-
end
|
715
|
-
end
|
716
|
-
|
717
|
-
rule "FC050", "Name includes invalid characters" do
|
718
|
-
tags %w{correctness environments roles}
|
719
|
-
def invalid_name(ast)
|
720
|
-
field(ast, :name) unless field_value(ast, :name) =~ /^[a-zA-Z0-9_\-]+$/
|
721
|
-
end
|
722
|
-
environment { |ast| invalid_name(ast) }
|
723
|
-
role { |ast| invalid_name(ast) }
|
724
|
-
end
|
725
|
-
|
726
|
-
rule "FC051", "Template partials loop indefinitely" do
|
727
|
-
tags %w{correctness}
|
728
|
-
recipe do |_, filename|
|
729
|
-
cbk_templates = template_paths(filename)
|
730
|
-
|
731
|
-
cbk_templates.select do |template|
|
732
|
-
begin
|
733
|
-
templates_included(cbk_templates, template)
|
734
|
-
false
|
735
|
-
rescue RecursedTooFarError
|
736
|
-
true
|
737
|
-
end
|
738
|
-
end.map { |t| file_match(t) }
|
739
|
-
end
|
740
|
-
end
|
741
|
-
|
742
|
-
rule "FC052", 'Metadata uses the unimplemented "suggests" keyword' do
|
743
|
-
tags %w{style metadata}
|
744
|
-
metadata do |ast, filename|
|
745
|
-
ast.xpath(%q{//command[ident/@value='suggests']})
|
746
|
-
end
|
747
|
-
end
|
748
|
-
|
749
|
-
rule "FC053", 'Metadata uses the unimplemented "recommends" keyword' do
|
750
|
-
tags %w{style metadata}
|
751
|
-
metadata do |ast, filename|
|
752
|
-
ast.xpath(%q{//command[ident/@value='recommends']})
|
753
|
-
end
|
754
|
-
end
|
755
|
-
|
756
|
-
# NOTE: FC054 was yanked and should be considered reserved, do not reuse it
|
757
|
-
|
758
|
-
rule "FC055", "Ensure maintainer is set in metadata" do
|
759
|
-
tags %w{correctness metadata}
|
760
|
-
metadata do |ast, filename|
|
761
|
-
[file_match(filename)] unless field(ast, "maintainer").any?
|
762
|
-
end
|
763
|
-
end
|
764
|
-
|
765
|
-
rule "FC056", "Ensure maintainer_email is set in metadata" do
|
766
|
-
tags %w{correctness metadata}
|
767
|
-
metadata do |ast, filename|
|
768
|
-
[file_match(filename)] unless field(ast, "maintainer_email").any?
|
769
|
-
end
|
770
|
-
end
|
771
|
-
|
772
|
-
rule "FC057", "Library provider does not declare use_inline_resources" do
|
773
|
-
tags %w{correctness}
|
774
|
-
library do |ast, filename|
|
775
|
-
ast.xpath('//const_path_ref/const[@value="LWRPBase"]/..//const[@value="Provider"]/../../..').select do |x|
|
776
|
-
x.xpath('//*[self::vcall or self::var_ref]/ident[@value="use_inline_resources"]').empty?
|
777
|
-
end
|
778
|
-
end
|
779
|
-
end
|
780
|
-
|
781
|
-
rule "FC058", "Library provider declares use_inline_resources and declares #action_<name> methods" do
|
782
|
-
tags %w{correctness}
|
783
|
-
library do |ast, filename|
|
784
|
-
ast.xpath('//const_path_ref/const[@value="LWRPBase"]/..//const[@value="Provider"]/../../..').select do |x|
|
785
|
-
x.xpath('//*[self::vcall or self::var_ref]/ident[@value="use_inline_resources"]').length > 0 &&
|
786
|
-
x.xpath(%q{//def[ident[contains(@value, 'action_')]]}).length > 0
|
787
|
-
end
|
788
|
-
end
|
789
|
-
end
|
790
|
-
|
791
|
-
rule "FC059", "LWRP provider does not declare use_inline_resources" do
|
792
|
-
tags %w{correctness}
|
793
|
-
provider do |ast, filename|
|
794
|
-
use_inline_resources = !ast.xpath('//*[self::vcall or self::var_ref]/ident
|
795
|
-
[@value="use_inline_resources"]').empty?
|
796
|
-
unless use_inline_resources
|
797
|
-
[file_match(filename)]
|
798
|
-
end
|
799
|
-
end
|
800
|
-
end
|
801
|
-
|
802
|
-
rule "FC060", "LWRP provider declares use_inline_resources and declares #action_<name> methods" do
|
803
|
-
tags %w{correctness}
|
804
|
-
provider do |ast, filename|
|
805
|
-
use_inline_resources = !ast.xpath('//*[self::vcall or self::var_ref]/ident
|
806
|
-
[@value="use_inline_resources"]').empty?
|
807
|
-
if use_inline_resources
|
808
|
-
ast.xpath(%q{//def[ident[contains(@value, 'action_')]]})
|
809
|
-
end
|
810
|
-
end
|
811
|
-
end
|
812
|
-
|
813
|
-
rule "FC061", "Valid cookbook versions are of the form x.y or x.y.z" do
|
814
|
-
tags %w{metadata correctness}
|
815
|
-
metadata do |ast, filename|
|
816
|
-
# matches a version method with a string literal with no interpolation
|
817
|
-
ver = ast.xpath('//command[ident/@value="version"]/args_add_block/args_add/string_literal[not(.//string_embexpr)]//tstring_content/@value')
|
818
|
-
if !ver.empty? && ver.to_s !~ /\A\d+\.\d+(\.\d+)?\z/
|
819
|
-
[file_match(filename)]
|
820
|
-
end
|
821
|
-
end
|
822
|
-
end
|
823
|
-
|
824
|
-
rule "FC062", "Cookbook should have version metadata" do
|
825
|
-
tags %w{metadata}
|
826
|
-
metadata do |ast, filename|
|
827
|
-
[file_match(filename)] unless field(ast, "version").any?
|
828
|
-
end
|
829
|
-
end
|
830
|
-
|
831
|
-
rule "FC063", "Cookbook incorrectly depends on itself" do
|
832
|
-
tags %w{metadata correctness}
|
833
|
-
metadata do |ast, filename|
|
834
|
-
name = cookbook_name(filename)
|
835
|
-
ast.xpath(%Q{//command[ident/@value='depends']/
|
836
|
-
descendant::tstring_content[@value='#{name}']})
|
837
|
-
end
|
838
|
-
end
|
839
|
-
|
840
|
-
rule "FC064", "Ensure issues_url is set in metadata" do
|
841
|
-
tags %w{metadata supermarket chef12}
|
842
|
-
metadata do |ast, filename|
|
843
|
-
[file_match(filename)] unless field(ast, "issues_url").any?
|
844
|
-
end
|
845
|
-
end
|
846
|
-
|
847
|
-
rule "FC065", "Ensure source_url is set in metadata" do
|
848
|
-
tags %w{metadata supermarket chef12}
|
849
|
-
metadata do |ast, filename|
|
850
|
-
[file_match(filename)] unless field(ast, "source_url").any?
|
851
|
-
end
|
852
|
-
end
|