From 509534e6d25f25dc3abe730345ce7f54cb593846 Mon Sep 17 00:00:00 2001 From: Nick Charlton Date: Mon, 5 Jan 2026 15:08:42 +0000 Subject: [PATCH] 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 --- .github/workflows/lint.yml | 15 +++ .yard-lint.yml | 256 +++++++++++++++++++++++++++++++++++++ Gemfile | 1 + Gemfile.lock | 4 + gemfiles/pundit21.gemfile | 1 + gemfiles/rails60.gemfile | 1 + gemfiles/rails61.gemfile | 1 + gemfiles/rails70.gemfile | 1 + gemfiles/rails80.gemfile | 1 + 9 files changed, 281 insertions(+) create mode 100644 .yard-lint.yml diff --git a/.github/workflows/lint.yml b/.github/workflows/lint.yml index b158e549bc..440c7eec2c 100644 --- a/.github/workflows/lint.yml +++ b/.github/workflows/lint.yml @@ -32,3 +32,18 @@ jobs: run: yarn install - name: Run Stylelint run: yarn run lint:css + + yard-lint: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v6 + with: + fetch-depth: 0 + - name: Set up Ruby + uses: ruby/setup-ruby@v1 + with: + bundler-cache: true + - name: Install Ruby dependencies + run: bundle install + - name: Run YARD Lint + run: bundle exec yard-lint --diff origin/${{ github.base_ref }} diff --git a/.yard-lint.yml b/.yard-lint.yml new file mode 100644 index 0000000000..b5bdb25acf --- /dev/null +++ b/.yard-lint.yml @@ -0,0 +1,256 @@ +--- +AllValidators: + YardOptions: + - --private + - --protected + Exclude: + - '\.git' + - 'vendor/**/*' + - 'node_modules/**/*' + - 'spec/**/*' + - 'test/**/*' + FailOnSeverity: warning + +Documentation/UndocumentedObjects: + Description: 'Checks for classes, modules, and methods without documentation.' + Enabled: true + Severity: warning + ExcludedMethods: + - 'initialize/0' # Exclude parameter-less initialize + - '/^_/' # Exclude private methods (by convention) + +Documentation/UndocumentedMethodArguments: + Description: 'Checks for method parameters without @param tags.' + Enabled: true + Severity: warning + +Documentation/UndocumentedBooleanMethods: + Description: 'Checks that question mark methods document their boolean return.' + Enabled: true + Severity: warning + +Documentation/UndocumentedOptions: + Description: 'Detects methods with options hash parameters but no @option tags.' + Enabled: true + Severity: warning + +Documentation/MarkdownSyntax: + Description: 'Detects common markdown syntax errors in documentation.' + Enabled: true + Severity: warning + +Documentation/EmptyCommentLine: + Description: 'Detects empty comment lines at the start or end of documentation blocks.' + Enabled: true + Severity: convention + EnabledPatterns: + Leading: true + Trailing: true + +Documentation/BlankLineBeforeDefinition: + Description: 'Detects blank lines between YARD documentation and method definition.' + Enabled: true + Severity: convention + OrphanedSeverity: convention + EnabledPatterns: + SingleBlankLine: true + OrphanedDocs: true + +Tags/Order: + Description: 'Enforces consistent ordering of YARD tags.' + Enabled: true + Severity: convention + EnforcedOrder: + - param + - option + - yield + - yieldparam + - yieldreturn + - return + - raise + - see + - example + - note + - todo + +Tags/InvalidTypes: + Description: 'Validates type definitions in @param, @return, @option tags.' + Enabled: true + Severity: warning + ValidatedTags: + - param + - option + - return + +Tags/TypeSyntax: + Description: 'Validates YARD type syntax using YARD parser.' + Enabled: true + Severity: warning + ValidatedTags: + - param + - option + - return + - yieldreturn + +Tags/MeaninglessTag: + Description: 'Detects @param/@option tags on classes, modules, or constants.' + Enabled: true + Severity: warning + CheckedTags: + - param + - option + InvalidObjectTypes: + - class + - module + - constant + +Tags/CollectionType: + Description: 'Validates Hash collection syntax consistency.' + Enabled: true + Severity: convention + EnforcedStyle: long # 'long' for Hash{K => V} (YARD standard), 'short' for {K => V} + ValidatedTags: + - param + - option + - return + - yieldreturn + +Tags/TagTypePosition: + Description: 'Validates type annotation position in tags.' + Enabled: true + Severity: convention + CheckedTags: + - param + - option + EnforcedStyle: type_after_name + +Tags/ApiTags: + Description: 'Enforces @api tags on public objects.' + Enabled: false # Opt-in validator + Severity: warning + AllowedApis: + - public + - private + - internal + +Tags/OptionTags: + Description: 'Requires @option tags for methods with options parameters.' + Enabled: true + Severity: warning + +Tags/ExampleSyntax: + Description: 'Validates Ruby syntax in @example tags.' + Enabled: true + Severity: warning + +Tags/RedundantParamDescription: + Description: 'Detects meaningless parameter descriptions that add no value.' + Enabled: true + Severity: convention + CheckedTags: + - param + - option + Articles: + - The + - the + - A + - a + - An + - an + MaxRedundantWords: 6 + GenericTerms: + - object + - instance + - value + - data + - item + - element + EnabledPatterns: + ArticleParam: true + PossessiveParam: true + TypeRestatement: true + ParamToVerb: true + IdPattern: true + DirectionalDate: true + TypeGeneric: true + +Tags/InformalNotation: + Description: 'Detects informal tag notation patterns like "Note:" instead of @note.' + Enabled: true + Severity: warning + CaseSensitive: false + RequireStartOfLine: true + Patterns: + Note: '@note' + Todo: '@todo' + TODO: '@todo' + FIXME: '@todo' + See: '@see' + See also: '@see' + Warning: '@deprecated' + Deprecated: '@deprecated' + Author: '@author' + Version: '@version' + Since: '@since' + Returns: '@return' + Raises: '@raise' + Example: '@example' + +Tags/NonAsciiType: + Description: 'Detects non-ASCII characters in type annotations.' + Enabled: true + Severity: warning + ValidatedTags: + - param + - option + - return + - yieldreturn + - yieldparam + +Tags/TagGroupSeparator: + Description: 'Enforces blank line separators between different YARD tag groups.' + Enabled: false # Opt-in validator + Severity: convention + TagGroups: + param: [param, option] + return: [return] + error: [raise, throws] + example: [example] + meta: [see, note, todo, deprecated, since, version, api] + yield: [yield, yieldparam, yieldreturn] + RequireAfterDescription: false + +Warnings/UnknownTag: + Description: 'Detects unknown YARD tags.' + Enabled: true + Severity: error + +Warnings/UnknownDirective: + Description: 'Detects unknown YARD directives.' + Enabled: true + Severity: error + +Warnings/InvalidTagFormat: + Description: 'Detects malformed tag syntax.' + Enabled: true + Severity: error + +Warnings/InvalidDirectiveFormat: + Description: 'Detects malformed directive syntax.' + Enabled: true + Severity: error + +Warnings/DuplicatedParameterName: + Description: 'Detects duplicate @param tags.' + Enabled: true + Severity: error + +Warnings/UnknownParameterName: + Description: 'Detects @param tags for non-existent parameters.' + Enabled: true + Severity: error + +Semantic/AbstractMethods: + Description: 'Ensures @abstract methods do not have real implementations.' + Enabled: true + Severity: warning diff --git a/Gemfile b/Gemfile index c42fad2b2d..ab3b45ca57 100644 --- a/Gemfile +++ b/Gemfile @@ -32,6 +32,7 @@ group :development, :test do gem "i18n-tasks" gem "standard" gem "yard" + gem "yard-lint" end group :test do diff --git a/Gemfile.lock b/Gemfile.lock index e4766fe455..ac8de0d153 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -408,6 +408,9 @@ GEM xpath (3.2.0) nokogiri (~> 1.8) yard (0.9.38) + yard-lint (1.3.0) + yard (~> 0.9) + zeitwerk (~> 2.6) zeitwerk (2.7.4) PLATFORMS @@ -454,6 +457,7 @@ DEPENDENCIES webrick xpath (= 3.2.0) yard + yard-lint RUBY VERSION ruby 4.0.0p0 diff --git a/gemfiles/pundit21.gemfile b/gemfiles/pundit21.gemfile index ba748ae92d..60117bc574 100644 --- a/gemfiles/pundit21.gemfile +++ b/gemfiles/pundit21.gemfile @@ -32,6 +32,7 @@ group :development, :test do gem "i18n-tasks" gem "standard" gem "yard" + gem "yard-lint" end group :test do diff --git a/gemfiles/rails60.gemfile b/gemfiles/rails60.gemfile index 0157e6c9cd..a2fea0fed0 100644 --- a/gemfiles/rails60.gemfile +++ b/gemfiles/rails60.gemfile @@ -36,6 +36,7 @@ group :development, :test do gem "i18n-tasks" gem "standard" gem "yard" + gem "yard-lint" end group :test do diff --git a/gemfiles/rails61.gemfile b/gemfiles/rails61.gemfile index 26d78c6fe6..5af531cabe 100644 --- a/gemfiles/rails61.gemfile +++ b/gemfiles/rails61.gemfile @@ -35,6 +35,7 @@ group :development, :test do gem "i18n-tasks" gem "standard" gem "yard" + gem "yard-lint" end group :test do diff --git a/gemfiles/rails70.gemfile b/gemfiles/rails70.gemfile index 25b6141442..8ea61f781f 100644 --- a/gemfiles/rails70.gemfile +++ b/gemfiles/rails70.gemfile @@ -33,6 +33,7 @@ group :development, :test do gem "i18n-tasks" gem "standard" gem "yard" + gem "yard-lint" end group :test do diff --git a/gemfiles/rails80.gemfile b/gemfiles/rails80.gemfile index 5a97db6da9..cf3d5e2fac 100644 --- a/gemfiles/rails80.gemfile +++ b/gemfiles/rails80.gemfile @@ -33,6 +33,7 @@ group :development, :test do gem "i18n-tasks" gem "standard" gem "yard" + gem "yard-lint" end group :test do