Skip to content

Commit 509534e

Browse files
committed
Introduce YARD-Lint
We've long had YARD setup for code-level documentation, but without a linter it's quite hard to know if we're using it at all, let along doing a good job of it. Last year, YARD-Lint was published and it's worth trying out to see if it'll work well for us. The hope is that by using GitHub Actions, we'll expand the existing code level docs and be able to verify along the way if they're useful for people. This takes the default configuration at the time of committing (which finds a lot of errors). We run it in a new linting workflow, but only comparing to `main`. https://mensfeld.pl/2025/11/yard-lint-ruby-documentation-linter/ https://github.com/mensfeld/yard-lint
1 parent 1e2e823 commit 509534e

File tree

9 files changed

+281
-0
lines changed

9 files changed

+281
-0
lines changed

.github/workflows/lint.yml

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -32,3 +32,18 @@ jobs:
3232
run: yarn install
3333
- name: Run Stylelint
3434
run: yarn run lint:css
35+
36+
yard-lint:
37+
runs-on: ubuntu-latest
38+
steps:
39+
- uses: actions/checkout@v6
40+
with:
41+
fetch-depth: 0
42+
- name: Set up Ruby
43+
uses: ruby/setup-ruby@v1
44+
with:
45+
bundler-cache: true
46+
- name: Install Ruby dependencies
47+
run: bundle install
48+
- name: Run YARD Lint
49+
run: bundle exec yard-lint --diff origin/${{ github.base_ref }}

.yard-lint.yml

Lines changed: 256 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,256 @@
1+
---
2+
AllValidators:
3+
YardOptions:
4+
- --private
5+
- --protected
6+
Exclude:
7+
- '\.git'
8+
- 'vendor/**/*'
9+
- 'node_modules/**/*'
10+
- 'spec/**/*'
11+
- 'test/**/*'
12+
FailOnSeverity: warning
13+
14+
Documentation/UndocumentedObjects:
15+
Description: 'Checks for classes, modules, and methods without documentation.'
16+
Enabled: true
17+
Severity: warning
18+
ExcludedMethods:
19+
- 'initialize/0' # Exclude parameter-less initialize
20+
- '/^_/' # Exclude private methods (by convention)
21+
22+
Documentation/UndocumentedMethodArguments:
23+
Description: 'Checks for method parameters without @param tags.'
24+
Enabled: true
25+
Severity: warning
26+
27+
Documentation/UndocumentedBooleanMethods:
28+
Description: 'Checks that question mark methods document their boolean return.'
29+
Enabled: true
30+
Severity: warning
31+
32+
Documentation/UndocumentedOptions:
33+
Description: 'Detects methods with options hash parameters but no @option tags.'
34+
Enabled: true
35+
Severity: warning
36+
37+
Documentation/MarkdownSyntax:
38+
Description: 'Detects common markdown syntax errors in documentation.'
39+
Enabled: true
40+
Severity: warning
41+
42+
Documentation/EmptyCommentLine:
43+
Description: 'Detects empty comment lines at the start or end of documentation blocks.'
44+
Enabled: true
45+
Severity: convention
46+
EnabledPatterns:
47+
Leading: true
48+
Trailing: true
49+
50+
Documentation/BlankLineBeforeDefinition:
51+
Description: 'Detects blank lines between YARD documentation and method definition.'
52+
Enabled: true
53+
Severity: convention
54+
OrphanedSeverity: convention
55+
EnabledPatterns:
56+
SingleBlankLine: true
57+
OrphanedDocs: true
58+
59+
Tags/Order:
60+
Description: 'Enforces consistent ordering of YARD tags.'
61+
Enabled: true
62+
Severity: convention
63+
EnforcedOrder:
64+
- param
65+
- option
66+
- yield
67+
- yieldparam
68+
- yieldreturn
69+
- return
70+
- raise
71+
- see
72+
- example
73+
- note
74+
- todo
75+
76+
Tags/InvalidTypes:
77+
Description: 'Validates type definitions in @param, @return, @option tags.'
78+
Enabled: true
79+
Severity: warning
80+
ValidatedTags:
81+
- param
82+
- option
83+
- return
84+
85+
Tags/TypeSyntax:
86+
Description: 'Validates YARD type syntax using YARD parser.'
87+
Enabled: true
88+
Severity: warning
89+
ValidatedTags:
90+
- param
91+
- option
92+
- return
93+
- yieldreturn
94+
95+
Tags/MeaninglessTag:
96+
Description: 'Detects @param/@option tags on classes, modules, or constants.'
97+
Enabled: true
98+
Severity: warning
99+
CheckedTags:
100+
- param
101+
- option
102+
InvalidObjectTypes:
103+
- class
104+
- module
105+
- constant
106+
107+
Tags/CollectionType:
108+
Description: 'Validates Hash collection syntax consistency.'
109+
Enabled: true
110+
Severity: convention
111+
EnforcedStyle: long # 'long' for Hash{K => V} (YARD standard), 'short' for {K => V}
112+
ValidatedTags:
113+
- param
114+
- option
115+
- return
116+
- yieldreturn
117+
118+
Tags/TagTypePosition:
119+
Description: 'Validates type annotation position in tags.'
120+
Enabled: true
121+
Severity: convention
122+
CheckedTags:
123+
- param
124+
- option
125+
EnforcedStyle: type_after_name
126+
127+
Tags/ApiTags:
128+
Description: 'Enforces @api tags on public objects.'
129+
Enabled: false # Opt-in validator
130+
Severity: warning
131+
AllowedApis:
132+
- public
133+
- private
134+
- internal
135+
136+
Tags/OptionTags:
137+
Description: 'Requires @option tags for methods with options parameters.'
138+
Enabled: true
139+
Severity: warning
140+
141+
Tags/ExampleSyntax:
142+
Description: 'Validates Ruby syntax in @example tags.'
143+
Enabled: true
144+
Severity: warning
145+
146+
Tags/RedundantParamDescription:
147+
Description: 'Detects meaningless parameter descriptions that add no value.'
148+
Enabled: true
149+
Severity: convention
150+
CheckedTags:
151+
- param
152+
- option
153+
Articles:
154+
- The
155+
- the
156+
- A
157+
- a
158+
- An
159+
- an
160+
MaxRedundantWords: 6
161+
GenericTerms:
162+
- object
163+
- instance
164+
- value
165+
- data
166+
- item
167+
- element
168+
EnabledPatterns:
169+
ArticleParam: true
170+
PossessiveParam: true
171+
TypeRestatement: true
172+
ParamToVerb: true
173+
IdPattern: true
174+
DirectionalDate: true
175+
TypeGeneric: true
176+
177+
Tags/InformalNotation:
178+
Description: 'Detects informal tag notation patterns like "Note:" instead of @note.'
179+
Enabled: true
180+
Severity: warning
181+
CaseSensitive: false
182+
RequireStartOfLine: true
183+
Patterns:
184+
Note: '@note'
185+
Todo: '@todo'
186+
TODO: '@todo'
187+
FIXME: '@todo'
188+
See: '@see'
189+
See also: '@see'
190+
Warning: '@deprecated'
191+
Deprecated: '@deprecated'
192+
Author: '@author'
193+
Version: '@version'
194+
Since: '@since'
195+
Returns: '@return'
196+
Raises: '@raise'
197+
Example: '@example'
198+
199+
Tags/NonAsciiType:
200+
Description: 'Detects non-ASCII characters in type annotations.'
201+
Enabled: true
202+
Severity: warning
203+
ValidatedTags:
204+
- param
205+
- option
206+
- return
207+
- yieldreturn
208+
- yieldparam
209+
210+
Tags/TagGroupSeparator:
211+
Description: 'Enforces blank line separators between different YARD tag groups.'
212+
Enabled: false # Opt-in validator
213+
Severity: convention
214+
TagGroups:
215+
param: [param, option]
216+
return: [return]
217+
error: [raise, throws]
218+
example: [example]
219+
meta: [see, note, todo, deprecated, since, version, api]
220+
yield: [yield, yieldparam, yieldreturn]
221+
RequireAfterDescription: false
222+
223+
Warnings/UnknownTag:
224+
Description: 'Detects unknown YARD tags.'
225+
Enabled: true
226+
Severity: error
227+
228+
Warnings/UnknownDirective:
229+
Description: 'Detects unknown YARD directives.'
230+
Enabled: true
231+
Severity: error
232+
233+
Warnings/InvalidTagFormat:
234+
Description: 'Detects malformed tag syntax.'
235+
Enabled: true
236+
Severity: error
237+
238+
Warnings/InvalidDirectiveFormat:
239+
Description: 'Detects malformed directive syntax.'
240+
Enabled: true
241+
Severity: error
242+
243+
Warnings/DuplicatedParameterName:
244+
Description: 'Detects duplicate @param tags.'
245+
Enabled: true
246+
Severity: error
247+
248+
Warnings/UnknownParameterName:
249+
Description: 'Detects @param tags for non-existent parameters.'
250+
Enabled: true
251+
Severity: error
252+
253+
Semantic/AbstractMethods:
254+
Description: 'Ensures @abstract methods do not have real implementations.'
255+
Enabled: true
256+
Severity: warning

Gemfile

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,7 @@ group :development, :test do
3232
gem "i18n-tasks"
3333
gem "standard"
3434
gem "yard"
35+
gem "yard-lint"
3536
end
3637

3738
group :test do

Gemfile.lock

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -408,6 +408,9 @@ GEM
408408
xpath (3.2.0)
409409
nokogiri (~> 1.8)
410410
yard (0.9.38)
411+
yard-lint (1.3.0)
412+
yard (~> 0.9)
413+
zeitwerk (~> 2.6)
411414
zeitwerk (2.7.4)
412415

413416
PLATFORMS
@@ -454,6 +457,7 @@ DEPENDENCIES
454457
webrick
455458
xpath (= 3.2.0)
456459
yard
460+
yard-lint
457461

458462
RUBY VERSION
459463
ruby 4.0.0p0

gemfiles/pundit21.gemfile

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,7 @@ group :development, :test do
3232
gem "i18n-tasks"
3333
gem "standard"
3434
gem "yard"
35+
gem "yard-lint"
3536
end
3637

3738
group :test do

gemfiles/rails60.gemfile

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -36,6 +36,7 @@ group :development, :test do
3636
gem "i18n-tasks"
3737
gem "standard"
3838
gem "yard"
39+
gem "yard-lint"
3940
end
4041

4142
group :test do

gemfiles/rails61.gemfile

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -35,6 +35,7 @@ group :development, :test do
3535
gem "i18n-tasks"
3636
gem "standard"
3737
gem "yard"
38+
gem "yard-lint"
3839
end
3940

4041
group :test do

gemfiles/rails70.gemfile

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,7 @@ group :development, :test do
3333
gem "i18n-tasks"
3434
gem "standard"
3535
gem "yard"
36+
gem "yard-lint"
3637
end
3738

3839
group :test do

gemfiles/rails80.gemfile

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,7 @@ group :development, :test do
3333
gem "i18n-tasks"
3434
gem "standard"
3535
gem "yard"
36+
gem "yard-lint"
3637
end
3738

3839
group :test do

0 commit comments

Comments
 (0)