foodcritic 10.0.0 → 10.1.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (101) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +12 -1
  3. data/Gemfile +2 -2
  4. data/features/031_check_for_metadata_existence.feature +2 -2
  5. data/features/choose_rules_to_apply.feature +27 -27
  6. data/features/continuous_integration_support.feature +2 -2
  7. data/features/support/command_helpers.rb +2 -2
  8. data/lib/foodcritic/api.rb +29 -3
  9. data/lib/foodcritic/linter.rb +1 -1
  10. data/lib/foodcritic/rules/fc001.rb +7 -0
  11. data/lib/foodcritic/rules/fc002.rb +8 -0
  12. data/lib/foodcritic/rules/fc004.rb +12 -0
  13. data/lib/foodcritic/rules/fc005.rb +29 -0
  14. data/lib/foodcritic/rules/fc006.rb +11 -0
  15. data/lib/foodcritic/rules/fc007.rb +19 -0
  16. data/lib/foodcritic/rules/fc008.rb +12 -0
  17. data/lib/foodcritic/rules/fc009.rb +18 -0
  18. data/lib/foodcritic/rules/fc010.rb +7 -0
  19. data/lib/foodcritic/rules/fc011.rb +8 -0
  20. data/lib/foodcritic/rules/fc012.rb +8 -0
  21. data/lib/foodcritic/rules/fc013.rb +10 -0
  22. data/lib/foodcritic/rules/fc014.rb +10 -0
  23. data/lib/foodcritic/rules/fc015.rb +8 -0
  24. data/lib/foodcritic/rules/fc016.rb +10 -0
  25. data/lib/foodcritic/rules/fc017.rb +29 -0
  26. data/lib/foodcritic/rules/fc018.rb +9 -0
  27. data/lib/foodcritic/rules/fc019.rb +34 -0
  28. data/lib/foodcritic/rules/fc021.rb +13 -0
  29. data/lib/foodcritic/rules/fc022.rb +33 -0
  30. data/lib/foodcritic/rules/fc024.rb +31 -0
  31. data/lib/foodcritic/rules/fc025.rb +17 -0
  32. data/lib/foodcritic/rules/fc026.rb +14 -0
  33. data/lib/foodcritic/rules/fc027.rb +9 -0
  34. data/lib/foodcritic/rules/fc028.rb +8 -0
  35. data/lib/foodcritic/rules/fc029.rb +14 -0
  36. data/lib/foodcritic/rules/fc030.rb +11 -0
  37. data/lib/foodcritic/rules/fc031.rb +8 -0
  38. data/lib/foodcritic/rules/fc032.rb +15 -0
  39. data/lib/foodcritic/rules/fc033.rb +26 -0
  40. data/lib/foodcritic/rules/fc034.rb +31 -0
  41. data/lib/foodcritic/rules/fc037.rb +14 -0
  42. data/lib/foodcritic/rules/fc038.rb +17 -0
  43. data/lib/foodcritic/rules/fc039.rb +19 -0
  44. data/lib/foodcritic/rules/fc040.rb +12 -0
  45. data/lib/foodcritic/rules/fc041.rb +9 -0
  46. data/lib/foodcritic/rules/fc042.rb +6 -0
  47. data/lib/foodcritic/rules/fc043.rb +8 -0
  48. data/lib/foodcritic/rules/fc044.rb +22 -0
  49. data/lib/foodcritic/rules/fc045.rb +13 -0
  50. data/lib/foodcritic/rules/fc046.rb +8 -0
  51. data/lib/foodcritic/rules/fc047.rb +16 -0
  52. data/lib/foodcritic/rules/fc048.rb +13 -0
  53. data/lib/foodcritic/rules/fc049.rb +10 -0
  54. data/lib/foodcritic/rules/fc050.rb +8 -0
  55. data/lib/foodcritic/rules/fc051.rb +15 -0
  56. data/lib/foodcritic/rules/fc052.rb +6 -0
  57. data/lib/foodcritic/rules/fc053.rb +6 -0
  58. data/lib/foodcritic/rules/fc055.rb +6 -0
  59. data/lib/foodcritic/rules/fc056.rb +6 -0
  60. data/lib/foodcritic/rules/fc057.rb +8 -0
  61. data/lib/foodcritic/rules/fc058.rb +9 -0
  62. data/lib/foodcritic/rules/fc059.rb +10 -0
  63. data/lib/foodcritic/rules/fc060.rb +10 -0
  64. data/lib/foodcritic/rules/fc061.rb +10 -0
  65. data/lib/foodcritic/rules/fc062.rb +6 -0
  66. data/lib/foodcritic/rules/fc063.rb +8 -0
  67. data/lib/foodcritic/rules/fc064.rb +6 -0
  68. data/lib/foodcritic/rules/fc065.rb +6 -0
  69. data/lib/foodcritic/version.rb +1 -1
  70. data/spec/foodcritic/api_spec.rb +62 -0
  71. data/spec/foodcritic/coverage/assets/0.10.0/application.css +799 -0
  72. data/spec/foodcritic/coverage/assets/0.10.0/application.js +1707 -0
  73. data/spec/foodcritic/coverage/assets/0.10.0/colorbox/border.png +0 -0
  74. data/spec/foodcritic/coverage/assets/0.10.0/colorbox/controls.png +0 -0
  75. data/spec/foodcritic/coverage/assets/0.10.0/colorbox/loading.gif +0 -0
  76. data/spec/foodcritic/coverage/assets/0.10.0/colorbox/loading_background.png +0 -0
  77. data/spec/foodcritic/coverage/assets/0.10.0/favicon_green.png +0 -0
  78. data/spec/foodcritic/coverage/assets/0.10.0/favicon_red.png +0 -0
  79. data/spec/foodcritic/coverage/assets/0.10.0/favicon_yellow.png +0 -0
  80. data/spec/foodcritic/coverage/assets/0.10.0/loading.gif +0 -0
  81. data/spec/foodcritic/coverage/assets/0.10.0/magnify.png +0 -0
  82. data/spec/foodcritic/coverage/assets/0.10.0/smoothness/images/ui-bg_flat_0_aaaaaa_40x100.png +0 -0
  83. data/spec/foodcritic/coverage/assets/0.10.0/smoothness/images/ui-bg_flat_75_ffffff_40x100.png +0 -0
  84. data/spec/foodcritic/coverage/assets/0.10.0/smoothness/images/ui-bg_glass_55_fbf9ee_1x400.png +0 -0
  85. data/spec/foodcritic/coverage/assets/0.10.0/smoothness/images/ui-bg_glass_65_ffffff_1x400.png +0 -0
  86. data/spec/foodcritic/coverage/assets/0.10.0/smoothness/images/ui-bg_glass_75_dadada_1x400.png +0 -0
  87. data/spec/foodcritic/coverage/assets/0.10.0/smoothness/images/ui-bg_glass_75_e6e6e6_1x400.png +0 -0
  88. data/spec/foodcritic/coverage/assets/0.10.0/smoothness/images/ui-bg_glass_95_fef1ec_1x400.png +0 -0
  89. data/spec/foodcritic/coverage/assets/0.10.0/smoothness/images/ui-bg_highlight-soft_75_cccccc_1x100.png +0 -0
  90. data/spec/foodcritic/coverage/assets/0.10.0/smoothness/images/ui-icons_222222_256x240.png +0 -0
  91. data/spec/foodcritic/coverage/assets/0.10.0/smoothness/images/ui-icons_2e83ff_256x240.png +0 -0
  92. data/spec/foodcritic/coverage/assets/0.10.0/smoothness/images/ui-icons_454545_256x240.png +0 -0
  93. data/spec/foodcritic/coverage/assets/0.10.0/smoothness/images/ui-icons_888888_256x240.png +0 -0
  94. data/spec/foodcritic/coverage/assets/0.10.0/smoothness/images/ui-icons_cd0a0a_256x240.png +0 -0
  95. data/spec/foodcritic/coverage/index.html +72 -0
  96. data/spec/foodcritic/linter_spec.rb +7 -6
  97. data/spec/regression/expected-output.txt +0 -12
  98. data/spec/spec_helper.rb +3 -1
  99. metadata +88 -6
  100. data/features/023_check_for_condition_around_resource.feature +0 -52
  101. data/lib/foodcritic/rules.rb +0 -852
@@ -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.4.1"
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(:default_rules_file) do
58
- File.expand_path(File.join(File.dirname(__FILE__), "../../lib/foodcritic/rules.rb"))
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, [[default_rules_file], 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 = [default_rules_file, gem_rules].flatten
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 = [default_rules_file, include_rules].flatten
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.0.0
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-14 00:00:00.000000000 Z
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.10
355
+ rubygems_version: 2.6.11
274
356
  signing_key:
275
357
  specification_version: 4
276
- summary: foodcritic-10.0.0
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 |
@@ -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