puppet-lint-module_reference-check 0.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +7 -0
- data/LICENSE +21 -0
- data/README.md +27 -0
- data/lib/puppet-lint/plugins/check_module_reference.rb +199 -0
- data/lib/puppet-lint-module_reference-check/reference.rb +114 -0
- data/lib/puppet-lint-module_reference-check/reference_workflow.rb +25 -0
- data/spec/puppet-lint/plugins/check_module_reference_spec.rb +270 -0
- data/spec/spec_helper.rb +9 -0
- metadata +165 -0
checksums.yaml
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
---
|
2
|
+
SHA256:
|
3
|
+
metadata.gz: 165ae50c695f4525d85426dea4ec555b9d4f44b7321f642d86783277319787fb
|
4
|
+
data.tar.gz: 952b6da27bc82a66b29a23df6bae49ecd16e742750a94cb75f0ce0d4646af1f8
|
5
|
+
SHA512:
|
6
|
+
metadata.gz: 96cdff2aed65b20e028d2093dc27668a7b32c8479ad3f552bc9bbdc59f655a6858d3ee2a36713367b949242978cd048854e0cb2c0a2b6cabd6d8c78caf476587
|
7
|
+
data.tar.gz: 124c80e7b55984c5a06d2fc5ecf70e192625d8984003d2f9277fbce6d4130e08028c4ce6140d4ae1105d44e6d155e6d9eee7ec7ef704212a6424204841c3f3ad
|
data/LICENSE
ADDED
@@ -0,0 +1,21 @@
|
|
1
|
+
MIT License
|
2
|
+
|
3
|
+
Copyright (c) 2022 DO! DevOps
|
4
|
+
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
7
|
+
in the Software without restriction, including without limitation the rights
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
10
|
+
furnished to do so, subject to the following conditions:
|
11
|
+
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
13
|
+
copies or substantial portions of the Software.
|
14
|
+
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
21
|
+
SOFTWARE.
|
data/README.md
ADDED
@@ -0,0 +1,27 @@
|
|
1
|
+
# Puppet lint module reference check
|
2
|
+
|
3
|
+
This checks whether used modules are properly referenced in the comments by these rules:
|
4
|
+
|
5
|
+
- All internal prefixed with profile:: or role:: based on the Puppet role/profile concept) modules have to be
|
6
|
+
referenced using a @see tag
|
7
|
+
- All component modules have to be referenced like the following
|
8
|
+
- All used classes and defined types need to be gathered in a comma-separated list of regexps in a @ref tag
|
9
|
+
- The full module name (vendor-module) has to be referenced in a @note tag
|
10
|
+
- At minimum one @see tag with a reference to the puppet forge page of the component module
|
11
|
+
- Example:
|
12
|
+
```
|
13
|
+
# @ref apache.*,a2mod
|
14
|
+
# @note puppetlabs-apache
|
15
|
+
# @see https://forge.puppet.com/modules/puppetlabs/apache
|
16
|
+
```
|
17
|
+
- Modules referenced by features (using our own role::include_features function) have to be referenced with @see
|
18
|
+
- The module references have to be sorted alphabetically and grouped by this:
|
19
|
+
- component module
|
20
|
+
- internal modules
|
21
|
+
- modules referenced by features
|
22
|
+
|
23
|
+
## Usage
|
24
|
+
|
25
|
+
To use the plugin, add the following line to the Gemfile:
|
26
|
+
|
27
|
+
gem 'puppet-lint-module_reference-check'
|
@@ -0,0 +1,199 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require_relative '../../puppet-lint-module_reference-check/reference'
|
4
|
+
|
5
|
+
# Find the header comments for a class or a defined type
|
6
|
+
#
|
7
|
+
# @param tokens The list of all tokens
|
8
|
+
# @param token_start The index of the token to start from upwards
|
9
|
+
# @return The head comments
|
10
|
+
def get_comments(tokens, token_start)
|
11
|
+
comments = []
|
12
|
+
token_pointer = token_start - 1
|
13
|
+
while token_pointer >= 0
|
14
|
+
break unless %i[COMMENT NEWLINE].include? tokens[token_pointer].type
|
15
|
+
|
16
|
+
comments.append(tokens[token_pointer])
|
17
|
+
token_pointer -= 1
|
18
|
+
end
|
19
|
+
comments.reject { |comment| comment.type == :NEWLINE }.reverse!
|
20
|
+
end
|
21
|
+
|
22
|
+
def get_first_index_of_type(comments, type, feature_refs)
|
23
|
+
comments.each_with_index do |comment, index|
|
24
|
+
comment.match(/@see (?<name>\S+)/) do |match|
|
25
|
+
next if feature_refs.any? { |ref| ref[:name] == match.named_captures['name'] }
|
26
|
+
return index if match.named_captures['name'].match?(INTERNAL_MODULE_REGEXP) && type == REF_TYPE_ENUM[:internal]
|
27
|
+
return index if !match.named_captures['name'].match?(INTERNAL_MODULE_REGEXP) && type == REF_TYPE_ENUM[:component]
|
28
|
+
end
|
29
|
+
end
|
30
|
+
0
|
31
|
+
end
|
32
|
+
|
33
|
+
PuppetLint.new_check(:module_reference) do
|
34
|
+
def initialize
|
35
|
+
@workflow = Reference.new
|
36
|
+
# noinspection RubySuperCallWithoutSuperclassInspection
|
37
|
+
super
|
38
|
+
end
|
39
|
+
|
40
|
+
def warn(message, line = 1, column = 1)
|
41
|
+
notify :warning, { message: message, line: line, column: column }
|
42
|
+
false
|
43
|
+
end
|
44
|
+
|
45
|
+
def check_sees_exist(references, comments)
|
46
|
+
references.each do |reference|
|
47
|
+
next if comments.any? { |comment| comment.match?("^@see #{reference[:name]}\\s?") }
|
48
|
+
|
49
|
+
return warn(
|
50
|
+
"Module #{reference[:name]} not referenced in the comments",
|
51
|
+
reference[:token].line,
|
52
|
+
reference[:token].column
|
53
|
+
)
|
54
|
+
end
|
55
|
+
true
|
56
|
+
end
|
57
|
+
|
58
|
+
def check_reference_order(references, comments, below_index)
|
59
|
+
references.each do |reference|
|
60
|
+
if comments.find_index { |comment| comment.match?("^@see #{reference[:name]}\\s?") } < below_index
|
61
|
+
return warn("Reference to #{reference[:name]} was found higher than #{comments[below_index]}")
|
62
|
+
end
|
63
|
+
end
|
64
|
+
true
|
65
|
+
end
|
66
|
+
|
67
|
+
def check_internal_references(comments)
|
68
|
+
internal_references = @workflow.references.filter { |ref| ref[:type] == REF_TYPE_ENUM[:internal] }
|
69
|
+
return false unless check_sees_exist(internal_references, comments)
|
70
|
+
return false unless check_reference_order(
|
71
|
+
internal_references,
|
72
|
+
comments,
|
73
|
+
get_first_index_of_type(
|
74
|
+
comments,
|
75
|
+
REF_TYPE_ENUM[:component],
|
76
|
+
@workflow.references.filter { |ref| ref[:type] == REF_TYPE_ENUM[:feature] }
|
77
|
+
)
|
78
|
+
)
|
79
|
+
|
80
|
+
true
|
81
|
+
end
|
82
|
+
|
83
|
+
def find_ref_index(name, comments)
|
84
|
+
comments.each_with_index do |comment, index|
|
85
|
+
comment.match(/^@ref (?<ref_regexps>.+)\s?/) do |match|
|
86
|
+
match.named_captures['ref_regexps'].split(',').each do |ref_regexp|
|
87
|
+
return index if name.match?(ref_regexp)
|
88
|
+
end
|
89
|
+
end
|
90
|
+
end
|
91
|
+
nil
|
92
|
+
end
|
93
|
+
|
94
|
+
def check_component_references(comments)
|
95
|
+
component_refs = @workflow.references.filter { |ref| ref[:type] == REF_TYPE_ENUM[:component] }
|
96
|
+
component_refs.each do |reference|
|
97
|
+
ref_index = find_ref_index(reference[:name], comments)
|
98
|
+
return warn("Can't find @ref tag for reference #{reference[:name]}") if ref_index.nil?
|
99
|
+
return warn("Missing @note tag for reference #{reference[:name]}") unless comments[ref_index + 1].match?(/@note/)
|
100
|
+
return warn("Missing @see tag for reference #{reference[:name]}") unless comments[ref_index + 2].match?(/@see/)
|
101
|
+
next if comments[ref_index + 2].include?('https://forge.puppet.com/')
|
102
|
+
|
103
|
+
return warn("First @see for reference #{reference[:name]} is not the Puppet forge")
|
104
|
+
end
|
105
|
+
true
|
106
|
+
end
|
107
|
+
|
108
|
+
def check_feature_references(comments)
|
109
|
+
feature_refs = @workflow.references.filter { |ref| ref[:type] == REF_TYPE_ENUM[:feature] }
|
110
|
+
return false unless check_sees_exist(feature_refs, comments)
|
111
|
+
return false unless check_reference_order(
|
112
|
+
feature_refs,
|
113
|
+
comments,
|
114
|
+
get_first_index_of_type(
|
115
|
+
comments,
|
116
|
+
REF_TYPE_ENUM[:internal],
|
117
|
+
@workflow.references.filter { |ref| ref[:type] == REF_TYPE_ENUM[:feature] }
|
118
|
+
)
|
119
|
+
)
|
120
|
+
|
121
|
+
true
|
122
|
+
end
|
123
|
+
|
124
|
+
def get_relevant_name(captures, comments, index)
|
125
|
+
return_object = {
|
126
|
+
name: captures['name'],
|
127
|
+
type: nil
|
128
|
+
}
|
129
|
+
if captures['type'] == 'see' && (index == 0 || index > 0 && !comments[index - 1].match(/@note/))
|
130
|
+
return return_object if captures['name'].match?(%r(https?://))
|
131
|
+
|
132
|
+
reference = @workflow.references.select { |ref| ref[:name] == captures['name'] }
|
133
|
+
return warn("Can't find referenced module #{captures['name']}") if reference.empty?
|
134
|
+
|
135
|
+
return_object[:type] = reference.first[:type]
|
136
|
+
else
|
137
|
+
@workflow
|
138
|
+
.references
|
139
|
+
.select { |ref| ref[:type] == REF_TYPE_ENUM[:component] }
|
140
|
+
.each do |ref|
|
141
|
+
next unless captures['name'].split(',').any? { |refmatch| ref[:name].match?(refmatch) }
|
142
|
+
|
143
|
+
comments[index + 1].match(/^@note (?<name>.+)$/) do |matchdata|
|
144
|
+
return_object[:name] = matchdata['name']
|
145
|
+
return_object[:type] = ref[:type]
|
146
|
+
break
|
147
|
+
end
|
148
|
+
end
|
149
|
+
end
|
150
|
+
return_object
|
151
|
+
end
|
152
|
+
|
153
|
+
def check_order(comments)
|
154
|
+
last_comment = nil
|
155
|
+
current_type = REF_TYPE_ENUM[:component]
|
156
|
+
comments.each_with_index do |comment, index|
|
157
|
+
comment.match(/@(?<type>see|ref) (?<name>\S+)/) do |match|
|
158
|
+
ref_object = get_relevant_name(match.named_captures, comments, index)
|
159
|
+
return false unless ref_object
|
160
|
+
|
161
|
+
current_comment = ref_object[:name]
|
162
|
+
return warn("No relevant name found for #{comment}") if current_comment.nil?
|
163
|
+
next if current_comment.match?('https?://')
|
164
|
+
|
165
|
+
if ref_object[:type] != current_type
|
166
|
+
last_comment = nil
|
167
|
+
current_type = ref_object[:type]
|
168
|
+
end
|
169
|
+
last_comment = current_comment if last_comment.nil?
|
170
|
+
return warn("#{current_comment} sorted after #{last_comment}") if last_comment > current_comment
|
171
|
+
end
|
172
|
+
end
|
173
|
+
end
|
174
|
+
|
175
|
+
# Check class or defined type indexes
|
176
|
+
def check_indexes(indexes)
|
177
|
+
indexes.each do |index|
|
178
|
+
comments = get_comments(tokens, index[:start] - 1)
|
179
|
+
.map { |comment| comment.value.strip }
|
180
|
+
.filter { |comment| comment.match?(/^@(see|note|ref)/) }
|
181
|
+
begin
|
182
|
+
@workflow.process(tokens[index[:start], index[:end]])
|
183
|
+
rescue InvalidTokenForState => e
|
184
|
+
warn(e.message, e.token.line, e.token.column)
|
185
|
+
end
|
186
|
+
return false unless check_internal_references(comments)
|
187
|
+
return false unless check_component_references(comments)
|
188
|
+
return false unless check_feature_references(comments)
|
189
|
+
return false unless check_order(comments)
|
190
|
+
end
|
191
|
+
true
|
192
|
+
end
|
193
|
+
|
194
|
+
# Run the check
|
195
|
+
def check
|
196
|
+
return unless check_indexes(class_indexes)
|
197
|
+
return unless check_indexes(defined_type_indexes)
|
198
|
+
end
|
199
|
+
end
|
@@ -0,0 +1,114 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require_relative 'reference_workflow'
|
4
|
+
|
5
|
+
REF_TYPE_ENUM = {
|
6
|
+
internal: 0,
|
7
|
+
component: 1,
|
8
|
+
feature: 2
|
9
|
+
}.freeze
|
10
|
+
|
11
|
+
INTERNAL_MODULE_REGEXP = /^(role|profile)::/.freeze
|
12
|
+
|
13
|
+
# Comment received in an invalid state
|
14
|
+
class InvalidTokenForState < StandardError
|
15
|
+
def initialize(token, state)
|
16
|
+
@token = token
|
17
|
+
@state = state
|
18
|
+
super "Can not process the token '#{@token.value.strip}' in the state #{@state}"
|
19
|
+
end
|
20
|
+
|
21
|
+
attr_reader :token
|
22
|
+
end
|
23
|
+
|
24
|
+
# A utility class to process tokens and analyze includes
|
25
|
+
class Reference
|
26
|
+
def initialize
|
27
|
+
@workflow = ReferenceWorkflow.new(self)
|
28
|
+
|
29
|
+
reset
|
30
|
+
end
|
31
|
+
|
32
|
+
def reset
|
33
|
+
@current_token = nil
|
34
|
+
@references = []
|
35
|
+
end
|
36
|
+
|
37
|
+
def get_body_start(tokens)
|
38
|
+
params_started = false
|
39
|
+
params_ended = false
|
40
|
+
params_brackets = 0
|
41
|
+
tokens.each_with_index do |token, index|
|
42
|
+
params_started = true if token.type == :LPAREN && !params_started
|
43
|
+
params_brackets += 1 if token.type == :LPAREN && params_started
|
44
|
+
params_brackets -= 1 if token.type == :RPAREN && params_started
|
45
|
+
params_ended = true if params_started && params_brackets.zero?
|
46
|
+
return index + 1 if params_ended && token.type == :LBRACE
|
47
|
+
end
|
48
|
+
warn('No class or type body found')
|
49
|
+
end
|
50
|
+
|
51
|
+
def process(tokens)
|
52
|
+
tokens = tokens.drop(get_body_start(tokens))
|
53
|
+
feature_includes = []
|
54
|
+
tokens.reject { |token| %i[WHITESPACE NEWLINE INDENT].include? token.type }.each do |token|
|
55
|
+
@current_token = token
|
56
|
+
@workflow.got_include if token.value == 'include'
|
57
|
+
@workflow.got_class if token.value == 'class'
|
58
|
+
@workflow.got_features_start if token.value == 'role::include_features'
|
59
|
+
@workflow.got_feature if token.type == :LBRACK && @workflow.current == :awaiting_feature
|
60
|
+
@workflow.got_feature_end if token.type == :RBRACK && @workflow.current == :awaiting_feature_include
|
61
|
+
@workflow.got_features_end(feature_includes) if token.type == :RBRACE && @workflow.current == :got_feature_start
|
62
|
+
next unless %i[NAME SSTRING].include?(token.type)
|
63
|
+
next if %w[include class role::include_features].include?(token.value)
|
64
|
+
|
65
|
+
# noinspection RubyCaseWithoutElseBlockInspection
|
66
|
+
case @workflow.current
|
67
|
+
when :awaiting_include_name
|
68
|
+
@workflow.got_include_name(token.value)
|
69
|
+
when :awaiting_class_name
|
70
|
+
@workflow.got_class_name(token.value)
|
71
|
+
when :awaiting_feature_include
|
72
|
+
feature_includes.append(token.value)
|
73
|
+
end
|
74
|
+
end
|
75
|
+
end
|
76
|
+
|
77
|
+
def got_include_name_trigger(include_name)
|
78
|
+
@references.append(
|
79
|
+
{
|
80
|
+
type: include_name.match(INTERNAL_MODULE_REGEXP) ? REF_TYPE_ENUM[:internal] : REF_TYPE_ENUM[:component],
|
81
|
+
name: include_name,
|
82
|
+
token: @current_token
|
83
|
+
}
|
84
|
+
)
|
85
|
+
end
|
86
|
+
|
87
|
+
def got_class_name_trigger(class_name)
|
88
|
+
@references.append(
|
89
|
+
{
|
90
|
+
type: class_name.match(INTERNAL_MODULE_REGEXP) ? REF_TYPE_ENUM[:internal] : REF_TYPE_ENUM[:component],
|
91
|
+
name: class_name,
|
92
|
+
token: @current_token
|
93
|
+
}
|
94
|
+
)
|
95
|
+
end
|
96
|
+
|
97
|
+
def got_features_end_trigger(feature_includes)
|
98
|
+
feature_includes.each do |include_name|
|
99
|
+
@references.append(
|
100
|
+
{
|
101
|
+
type: REF_TYPE_ENUM[:feature],
|
102
|
+
name: include_name,
|
103
|
+
token: @current_token
|
104
|
+
}
|
105
|
+
)
|
106
|
+
end
|
107
|
+
end
|
108
|
+
|
109
|
+
def invalid_state
|
110
|
+
raise InvalidTokenForState.new @current_token, @workflow.current
|
111
|
+
end
|
112
|
+
|
113
|
+
attr_reader :references
|
114
|
+
end
|
@@ -0,0 +1,25 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require 'finite_machine'
|
4
|
+
|
5
|
+
# A workflow to describe includes and class declarations
|
6
|
+
class ReferenceWorkflow < FiniteMachine::Definition
|
7
|
+
initial :start
|
8
|
+
|
9
|
+
event :got_include, from: :start, to: :awaiting_include_name
|
10
|
+
event :got_include_name, from: :awaiting_include_name, to: :start
|
11
|
+
|
12
|
+
event :got_class, from: :start, to: :awaiting_class_name
|
13
|
+
event :got_class_name, from: :awaiting_class_name, to: :start
|
14
|
+
|
15
|
+
event :got_features_start, from: :start, to: :awaiting_feature
|
16
|
+
event :got_feature, from: :awaiting_feature, to: :awaiting_feature_include
|
17
|
+
event :got_feature_end, from: :awaiting_feature_include, to: :got_feature_start
|
18
|
+
event :got_features_end, from: :got_feature_start, to: :start
|
19
|
+
|
20
|
+
on_before(:got_include_name) { |_, include_name| target.got_include_name_trigger(include_name) }
|
21
|
+
on_before(:got_class_name) { |_, class_name| target.got_class_name_trigger(class_name) }
|
22
|
+
on_before(:got_features_end) { |_, feature_includes| target.got_features_end_trigger(feature_includes) }
|
23
|
+
|
24
|
+
handle FiniteMachine::InvalidStateError, with: -> { target.invalid_state }
|
25
|
+
end
|
@@ -0,0 +1,270 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require_relative '../../spec_helper'
|
4
|
+
|
5
|
+
describe 'module_reference' do
|
6
|
+
context 'valid code' do
|
7
|
+
let(:code) do
|
8
|
+
<<~CODE
|
9
|
+
# @ref apache
|
10
|
+
# @note puppetlabs-apache
|
11
|
+
# @see https://forge.puppet.com/modules/puppetlabs/apache
|
12
|
+
#
|
13
|
+
# @see profile::test
|
14
|
+
#
|
15
|
+
# @see profile::testfeature - Feature "test"
|
16
|
+
class test () {
|
17
|
+
include profile::test
|
18
|
+
class {
|
19
|
+
'apache':
|
20
|
+
}
|
21
|
+
include apache
|
22
|
+
|
23
|
+
role::include_features({
|
24
|
+
'testfeature' => [
|
25
|
+
profile::testfeature,
|
26
|
+
],
|
27
|
+
})
|
28
|
+
}
|
29
|
+
CODE
|
30
|
+
end
|
31
|
+
|
32
|
+
it 'should not detect any problems' do
|
33
|
+
expect(problems).to have(0).problems
|
34
|
+
end
|
35
|
+
end
|
36
|
+
|
37
|
+
context 'code with missing internal link' do
|
38
|
+
let(:code) do
|
39
|
+
<<~CODE
|
40
|
+
class test () {
|
41
|
+
include profile::test
|
42
|
+
}
|
43
|
+
CODE
|
44
|
+
end
|
45
|
+
|
46
|
+
it 'should detect exactly one problem' do
|
47
|
+
expect(problems).to have(1).problems
|
48
|
+
end
|
49
|
+
|
50
|
+
it 'should create a warning' do
|
51
|
+
expect(problems).to contain_warning('Module profile::test not referenced in the comments').on_line(2).in_column(11)
|
52
|
+
end
|
53
|
+
end
|
54
|
+
|
55
|
+
context 'code with missing component link' do
|
56
|
+
let(:code) do
|
57
|
+
<<~CODE
|
58
|
+
class test () {
|
59
|
+
class {
|
60
|
+
'apache':
|
61
|
+
}
|
62
|
+
}
|
63
|
+
CODE
|
64
|
+
end
|
65
|
+
|
66
|
+
it 'should detect exactly one problem' do
|
67
|
+
expect(problems).to have(1).problems
|
68
|
+
end
|
69
|
+
|
70
|
+
it 'should create a warning' do
|
71
|
+
expect(problems).to contain_warning('Can\'t find @ref tag for reference apache').on_line(1).in_column(1)
|
72
|
+
end
|
73
|
+
end
|
74
|
+
|
75
|
+
context 'code with wrong component @see' do
|
76
|
+
let(:code) do
|
77
|
+
<<~CODE
|
78
|
+
# @ref apache
|
79
|
+
# @note puppetlabs-apache
|
80
|
+
# @see https://example.com
|
81
|
+
class test () {
|
82
|
+
class {
|
83
|
+
'apache':
|
84
|
+
}
|
85
|
+
}
|
86
|
+
CODE
|
87
|
+
end
|
88
|
+
|
89
|
+
it 'should detect exactly one problem' do
|
90
|
+
expect(problems).to have(1).problems
|
91
|
+
end
|
92
|
+
|
93
|
+
it 'should create a warning' do
|
94
|
+
expect(problems).to contain_warning('First @see for reference apache is not the Puppet forge').on_line(1).in_column(1)
|
95
|
+
end
|
96
|
+
end
|
97
|
+
|
98
|
+
context 'code with internal references sorted before component references' do
|
99
|
+
let(:code) do
|
100
|
+
<<~CODE
|
101
|
+
# @see profile::test
|
102
|
+
#
|
103
|
+
# @ref apache
|
104
|
+
# @note puppetlabs-apache
|
105
|
+
# @see https://forge.puppet.com/modules/puppetlabs/apache
|
106
|
+
#
|
107
|
+
# @see profile::testfeature - Feature "test"
|
108
|
+
class test () {
|
109
|
+
include profile::test
|
110
|
+
class {
|
111
|
+
'apache':
|
112
|
+
}
|
113
|
+
include apache
|
114
|
+
|
115
|
+
role::include_features({
|
116
|
+
'testfeature' => [
|
117
|
+
profile::testfeature,
|
118
|
+
],
|
119
|
+
})
|
120
|
+
}
|
121
|
+
CODE
|
122
|
+
end
|
123
|
+
|
124
|
+
it 'should detect exactly one problem' do
|
125
|
+
expect(problems).to have(1).problems
|
126
|
+
end
|
127
|
+
|
128
|
+
it 'should create a warning' do
|
129
|
+
expect(problems).to contain_warning('Reference to profile::test was found higher than @see https://forge.puppet.com/modules/puppetlabs/apache').on_line(1).in_column(1)
|
130
|
+
end
|
131
|
+
end
|
132
|
+
|
133
|
+
context 'code with feature references sorted before internal references' do
|
134
|
+
let(:code) do
|
135
|
+
<<~CODE
|
136
|
+
# @ref apache
|
137
|
+
# @note puppetlabs-apache
|
138
|
+
# @see https://forge.puppet.com/modules/puppetlabs/apache
|
139
|
+
#
|
140
|
+
# @see profile::testfeature - Feature "test"
|
141
|
+
#
|
142
|
+
# @see profile::test
|
143
|
+
class test () {
|
144
|
+
include profile::test
|
145
|
+
class {
|
146
|
+
'apache':
|
147
|
+
}
|
148
|
+
include apache
|
149
|
+
|
150
|
+
role::include_features({
|
151
|
+
'testfeature' => [
|
152
|
+
profile::testfeature,
|
153
|
+
],
|
154
|
+
})
|
155
|
+
}
|
156
|
+
CODE
|
157
|
+
end
|
158
|
+
|
159
|
+
it 'should detect exactly one problem' do
|
160
|
+
expect(problems).to have(1).problems
|
161
|
+
end
|
162
|
+
|
163
|
+
it 'should create a warning' do
|
164
|
+
expect(problems).to contain_warning('Reference to profile::testfeature was found higher than @see profile::test').on_line(1).in_column(1)
|
165
|
+
end
|
166
|
+
end
|
167
|
+
|
168
|
+
context 'code with reference to unused module' do
|
169
|
+
let(:code) do
|
170
|
+
<<~CODE
|
171
|
+
# @ref apache
|
172
|
+
# @note puppetlabs-apache
|
173
|
+
# @see https://forge.puppet.com/modules/puppetlabs/apache
|
174
|
+
#
|
175
|
+
# @see profile::test
|
176
|
+
#
|
177
|
+
# @see profile::testfeature - Feature "test"
|
178
|
+
class test () {
|
179
|
+
include profile::test
|
180
|
+
class {
|
181
|
+
'apache':
|
182
|
+
}
|
183
|
+
include apache
|
184
|
+
}
|
185
|
+
CODE
|
186
|
+
end
|
187
|
+
|
188
|
+
it 'should detect exactly one problem' do
|
189
|
+
expect(problems).to have(1).problems
|
190
|
+
end
|
191
|
+
|
192
|
+
it 'should create a warning' do
|
193
|
+
expect(problems).to contain_warning('Can\'t find referenced module profile::testfeature').on_line(1).in_column(1)
|
194
|
+
end
|
195
|
+
end
|
196
|
+
|
197
|
+
context 'code with unsorted component references' do
|
198
|
+
let(:code) do
|
199
|
+
<<~CODE
|
200
|
+
# @ref postgresql
|
201
|
+
# @note puppetlabs-postgresql
|
202
|
+
# @see https://forge.puppet.com/modules/puppetlabs/postgresql
|
203
|
+
#
|
204
|
+
# @ref apache
|
205
|
+
# @note puppetlabs-apache
|
206
|
+
# @see https://forge.puppet.com/modules/puppetlabs/apache
|
207
|
+
class test () {
|
208
|
+
class {
|
209
|
+
'apache':
|
210
|
+
}
|
211
|
+
include postgresql
|
212
|
+
}
|
213
|
+
CODE
|
214
|
+
end
|
215
|
+
|
216
|
+
it 'should detect exactly one problem' do
|
217
|
+
expect(problems).to have(1).problems
|
218
|
+
end
|
219
|
+
|
220
|
+
it 'should create a warning' do
|
221
|
+
expect(problems).to contain_warning('puppetlabs-apache sorted after puppetlabs-postgresql').on_line(1).in_column(1)
|
222
|
+
end
|
223
|
+
end
|
224
|
+
|
225
|
+
context 'code with unsorted internal references' do
|
226
|
+
let(:code) do
|
227
|
+
<<~CODE
|
228
|
+
# @see profile::b
|
229
|
+
# @see profile::a
|
230
|
+
class test () {
|
231
|
+
include profile::a
|
232
|
+
include profile::b
|
233
|
+
}
|
234
|
+
CODE
|
235
|
+
end
|
236
|
+
|
237
|
+
it 'should detect exactly one problem' do
|
238
|
+
expect(problems).to have(1).problems
|
239
|
+
end
|
240
|
+
|
241
|
+
it 'should create a warning' do
|
242
|
+
expect(problems).to contain_warning('profile::a sorted after profile::b').on_line(1).in_column(1)
|
243
|
+
end
|
244
|
+
end
|
245
|
+
|
246
|
+
context 'code with unsorted feature references' do
|
247
|
+
let(:code) do
|
248
|
+
<<~CODE
|
249
|
+
# @see profile::b
|
250
|
+
# @see profile::a
|
251
|
+
class test () {
|
252
|
+
role::include_features({
|
253
|
+
'testfeature' => [
|
254
|
+
profile::a,
|
255
|
+
profile::b
|
256
|
+
],
|
257
|
+
})
|
258
|
+
}
|
259
|
+
CODE
|
260
|
+
end
|
261
|
+
|
262
|
+
it 'should detect exactly one problem' do
|
263
|
+
expect(problems).to have(1).problems
|
264
|
+
end
|
265
|
+
|
266
|
+
it 'should create a warning' do
|
267
|
+
expect(problems).to contain_warning('profile::a sorted after profile::b').on_line(1).in_column(1)
|
268
|
+
end
|
269
|
+
end
|
270
|
+
end
|
data/spec/spec_helper.rb
ADDED
metadata
ADDED
@@ -0,0 +1,165 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: puppet-lint-module_reference-check
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 0.1.0
|
5
|
+
platform: ruby
|
6
|
+
authors:
|
7
|
+
- Dennis Ploeger
|
8
|
+
autorequire:
|
9
|
+
bindir: bin
|
10
|
+
cert_chain: []
|
11
|
+
date: 2022-06-03 00:00:00.000000000 Z
|
12
|
+
dependencies:
|
13
|
+
- !ruby/object:Gem::Dependency
|
14
|
+
name: finite_machine
|
15
|
+
requirement: !ruby/object:Gem::Requirement
|
16
|
+
requirements:
|
17
|
+
- - ">="
|
18
|
+
- !ruby/object:Gem::Version
|
19
|
+
version: '0'
|
20
|
+
type: :runtime
|
21
|
+
prerelease: false
|
22
|
+
version_requirements: !ruby/object:Gem::Requirement
|
23
|
+
requirements:
|
24
|
+
- - ">="
|
25
|
+
- !ruby/object:Gem::Version
|
26
|
+
version: '0'
|
27
|
+
- !ruby/object:Gem::Dependency
|
28
|
+
name: puppet-lint
|
29
|
+
requirement: !ruby/object:Gem::Requirement
|
30
|
+
requirements:
|
31
|
+
- - ">"
|
32
|
+
- !ruby/object:Gem::Version
|
33
|
+
version: '1.0'
|
34
|
+
type: :runtime
|
35
|
+
prerelease: false
|
36
|
+
version_requirements: !ruby/object:Gem::Requirement
|
37
|
+
requirements:
|
38
|
+
- - ">"
|
39
|
+
- !ruby/object:Gem::Version
|
40
|
+
version: '1.0'
|
41
|
+
- !ruby/object:Gem::Dependency
|
42
|
+
name: rake
|
43
|
+
requirement: !ruby/object:Gem::Requirement
|
44
|
+
requirements:
|
45
|
+
- - ">="
|
46
|
+
- !ruby/object:Gem::Version
|
47
|
+
version: '0'
|
48
|
+
type: :development
|
49
|
+
prerelease: false
|
50
|
+
version_requirements: !ruby/object:Gem::Requirement
|
51
|
+
requirements:
|
52
|
+
- - ">="
|
53
|
+
- !ruby/object:Gem::Version
|
54
|
+
version: '0'
|
55
|
+
- !ruby/object:Gem::Dependency
|
56
|
+
name: rspec
|
57
|
+
requirement: !ruby/object:Gem::Requirement
|
58
|
+
requirements:
|
59
|
+
- - "~>"
|
60
|
+
- !ruby/object:Gem::Version
|
61
|
+
version: '3.0'
|
62
|
+
type: :development
|
63
|
+
prerelease: false
|
64
|
+
version_requirements: !ruby/object:Gem::Requirement
|
65
|
+
requirements:
|
66
|
+
- - "~>"
|
67
|
+
- !ruby/object:Gem::Version
|
68
|
+
version: '3.0'
|
69
|
+
- !ruby/object:Gem::Dependency
|
70
|
+
name: rspec-collection_matchers
|
71
|
+
requirement: !ruby/object:Gem::Requirement
|
72
|
+
requirements:
|
73
|
+
- - "~>"
|
74
|
+
- !ruby/object:Gem::Version
|
75
|
+
version: '1.0'
|
76
|
+
type: :development
|
77
|
+
prerelease: false
|
78
|
+
version_requirements: !ruby/object:Gem::Requirement
|
79
|
+
requirements:
|
80
|
+
- - "~>"
|
81
|
+
- !ruby/object:Gem::Version
|
82
|
+
version: '1.0'
|
83
|
+
- !ruby/object:Gem::Dependency
|
84
|
+
name: rspec-its
|
85
|
+
requirement: !ruby/object:Gem::Requirement
|
86
|
+
requirements:
|
87
|
+
- - "~>"
|
88
|
+
- !ruby/object:Gem::Version
|
89
|
+
version: '1.0'
|
90
|
+
type: :development
|
91
|
+
prerelease: false
|
92
|
+
version_requirements: !ruby/object:Gem::Requirement
|
93
|
+
requirements:
|
94
|
+
- - "~>"
|
95
|
+
- !ruby/object:Gem::Version
|
96
|
+
version: '1.0'
|
97
|
+
- !ruby/object:Gem::Dependency
|
98
|
+
name: rubocop
|
99
|
+
requirement: !ruby/object:Gem::Requirement
|
100
|
+
requirements:
|
101
|
+
- - ">="
|
102
|
+
- !ruby/object:Gem::Version
|
103
|
+
version: '0'
|
104
|
+
type: :development
|
105
|
+
prerelease: false
|
106
|
+
version_requirements: !ruby/object:Gem::Requirement
|
107
|
+
requirements:
|
108
|
+
- - ">="
|
109
|
+
- !ruby/object:Gem::Version
|
110
|
+
version: '0'
|
111
|
+
- !ruby/object:Gem::Dependency
|
112
|
+
name: simplecov
|
113
|
+
requirement: !ruby/object:Gem::Requirement
|
114
|
+
requirements:
|
115
|
+
- - ">="
|
116
|
+
- !ruby/object:Gem::Version
|
117
|
+
version: '0'
|
118
|
+
type: :development
|
119
|
+
prerelease: false
|
120
|
+
version_requirements: !ruby/object:Gem::Requirement
|
121
|
+
requirements:
|
122
|
+
- - ">="
|
123
|
+
- !ruby/object:Gem::Version
|
124
|
+
version: '0'
|
125
|
+
description: |2
|
126
|
+
A puppet-lint plugin to check that manifest files contain valid reference comments for included
|
127
|
+
modules.
|
128
|
+
email: develop@dieploegers.de
|
129
|
+
executables: []
|
130
|
+
extensions: []
|
131
|
+
extra_rdoc_files: []
|
132
|
+
files:
|
133
|
+
- LICENSE
|
134
|
+
- README.md
|
135
|
+
- lib/puppet-lint-module_reference-check/reference.rb
|
136
|
+
- lib/puppet-lint-module_reference-check/reference_workflow.rb
|
137
|
+
- lib/puppet-lint/plugins/check_module_reference.rb
|
138
|
+
- spec/puppet-lint/plugins/check_module_reference_spec.rb
|
139
|
+
- spec/spec_helper.rb
|
140
|
+
homepage: https://github.com/dodevops/puppet-lint-module_reference-check
|
141
|
+
licenses:
|
142
|
+
- MIT
|
143
|
+
metadata: {}
|
144
|
+
post_install_message:
|
145
|
+
rdoc_options: []
|
146
|
+
require_paths:
|
147
|
+
- lib
|
148
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
149
|
+
requirements:
|
150
|
+
- - ">="
|
151
|
+
- !ruby/object:Gem::Version
|
152
|
+
version: '0'
|
153
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
154
|
+
requirements:
|
155
|
+
- - ">="
|
156
|
+
- !ruby/object:Gem::Version
|
157
|
+
version: '0'
|
158
|
+
requirements: []
|
159
|
+
rubygems_version: 3.0.3.1
|
160
|
+
signing_key:
|
161
|
+
specification_version: 4
|
162
|
+
summary: A puppet-lint plugin to check the @see and @note comments for included modules.
|
163
|
+
test_files:
|
164
|
+
- spec/spec_helper.rb
|
165
|
+
- spec/puppet-lint/plugins/check_module_reference_spec.rb
|