yard-lint 1.1.0 → 1.2.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 +4 -4
- data/CHANGELOG.md +37 -0
- data/README.md +118 -3
- data/bin/yard-lint +80 -8
- data/lib/yard/lint/command_cache.rb +17 -1
- data/lib/yard/lint/config.rb +20 -0
- data/lib/yard/lint/config_generator.rb +9 -0
- data/lib/yard/lint/ext/irb_notifier_shim.rb +95 -0
- data/lib/yard/lint/git.rb +125 -0
- data/lib/yard/lint/results/aggregate.rb +22 -2
- data/lib/yard/lint/runner.rb +4 -3
- data/lib/yard/lint/stats_calculator.rb +157 -0
- data/lib/yard/lint/version.rb +1 -1
- data/lib/yard/lint.rb +38 -2
- data/lib/yard-lint.rb +5 -0
- metadata +4 -15
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: 72ea0cf00858078029ca28144f64d7722abcf1a646e1365560542a064a37de24
|
|
4
|
+
data.tar.gz: 2d68009b8610f26b2bfaeb053692f36a714e96ff145cc60c71ed5b2c01c2a43c
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 48313464b85f0d161d39cf151fbbc681fcb5b8271efcc87a371f72199f692c6a75bf24f478dd169ad53babe987c43f88987cc89d51835085db7180eb0906a2f1
|
|
7
|
+
data.tar.gz: c2fa9aa46f81613ee4aacdee0dd6aba4492d61bb9776daf11c7754b94aec7011a5337ccfbe937b136004d240974b0fa336c406d91bb0982dc322235c02b1fdc8
|
data/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,42 @@
|
|
|
1
1
|
# YARD-Lint Changelog
|
|
2
2
|
|
|
3
|
+
## 1.2.0 (2025-11-12)
|
|
4
|
+
- **[Fix]** Add Ruby 3.5+ compatibility without requiring IRB gem dependency
|
|
5
|
+
- Ruby 3.5 moved IRB out of default gems, requiring explicit installation
|
|
6
|
+
- YARD's legacy parser depends on `IRB::Notifier` for debug output
|
|
7
|
+
- Created lightweight `IRB::Notifier` shim to satisfy YARD without full IRB gem
|
|
8
|
+
- Shim tries to load real IRB first, only provides fallback if LoadError occurs
|
|
9
|
+
- Does not override or interfere with real IRB gem when present
|
|
10
|
+
- Safe to use in applications that depend on yard-lint and also use IRB
|
|
11
|
+
- Shim automatically loaded in subprocesses via RUBYOPT environment variable
|
|
12
|
+
- Avoids adding IRB and its transitive dependencies to supply chain
|
|
13
|
+
- All 977 tests pass on Ruby 3.5.0-preview1 without IRB gem
|
|
14
|
+
- **[Feature]** Add Documentation Coverage Statistics with minimum threshold enforcement
|
|
15
|
+
- `--min-coverage PERCENT` - Fail if documentation coverage is below threshold (0-100)
|
|
16
|
+
- `--stats` flag now displays coverage metrics (total objects, documented, undocumented, percentage)
|
|
17
|
+
- `MinCoverage` configuration option in `.yard-lint.yml` under `AllValidators` section
|
|
18
|
+
- CLI flag overrides config file setting for flexibility in CI/CD pipelines
|
|
19
|
+
- Coverage calculation uses YARD queries to count documented vs undocumented objects
|
|
20
|
+
- Works seamlessly with diff mode (--diff, --staged, --changed) to calculate coverage for changed files only
|
|
21
|
+
- Exit code 1 when coverage is below minimum threshold, even if no linting offenses found
|
|
22
|
+
- Summary-only output in --quiet mode shows coverage with pass/fail status
|
|
23
|
+
- Comprehensive unit and integration test coverage for all scenarios
|
|
24
|
+
- Performance optimized with auto-cleanup temp directories for large codebases
|
|
25
|
+
- **[Feature]** Add Diff Mode for incremental linting - only analyze files that changed
|
|
26
|
+
- `--diff [REF]` - Lint only files changed since REF (auto-detects main/master if not specified)
|
|
27
|
+
- `--staged` - Lint only staged files (git index)
|
|
28
|
+
- `--changed` - Lint only uncommitted files
|
|
29
|
+
- Enables practical usage in large legacy codebases
|
|
30
|
+
- Perfect for CI/CD pipelines (only check what changed in PR)
|
|
31
|
+
- Ideal for pre-commit hooks (only check staged files)
|
|
32
|
+
- Auto-detects main/master branch with fallback to master
|
|
33
|
+
- Applies global exclusion patterns to git diff results
|
|
34
|
+
- Silently skips deleted files
|
|
35
|
+
- Returns clean result when no files are changed
|
|
36
|
+
- Uses shell-based git commands via Open3 (no new dependencies)
|
|
37
|
+
- Configuration support via `AllValidators.DiffMode` section
|
|
38
|
+
- Mutually exclusive diff flags (--diff, --staged, --changed)
|
|
39
|
+
|
|
3
40
|
## 1.1.0 (2025-11-11)
|
|
4
41
|
- **[Feature]** Add `Tags/ExampleSyntax` validator to validate Ruby syntax in `@example` tags
|
|
5
42
|
- Uses Ruby 3.2's `RubyVM::InstructionSequence.compile()` to parse example code
|
data/README.md
CHANGED
|
@@ -79,16 +79,124 @@ With options:
|
|
|
79
79
|
|
|
80
80
|
```bash
|
|
81
81
|
# Use a specific config file
|
|
82
|
-
yard-lint --config config/yard-lint.yml
|
|
82
|
+
yard-lint lib/ --config config/yard-lint.yml
|
|
83
83
|
|
|
84
84
|
# Output as JSON
|
|
85
|
-
yard-lint --format json
|
|
85
|
+
yard-lint lib/ --format json > report.json
|
|
86
86
|
|
|
87
87
|
# Generate config file (use --force to overwrite existing)
|
|
88
88
|
yard-lint --init
|
|
89
89
|
yard-lint --init --force
|
|
90
90
|
```
|
|
91
91
|
|
|
92
|
+
### Diff Mode (Incremental Linting)
|
|
93
|
+
|
|
94
|
+
Lint only files that changed - perfect for large projects, CI/CD, and pre-commit hooks:
|
|
95
|
+
|
|
96
|
+
```bash
|
|
97
|
+
# Lint only files changed since main branch (auto-detects main/master)
|
|
98
|
+
yard-lint lib/ --diff
|
|
99
|
+
|
|
100
|
+
# Lint only files changed since specific branch/commit
|
|
101
|
+
yard-lint lib/ --diff develop
|
|
102
|
+
yard-lint lib/ --diff HEAD~3
|
|
103
|
+
|
|
104
|
+
# Lint only staged files (perfect for pre-commit hooks)
|
|
105
|
+
yard-lint lib/ --staged
|
|
106
|
+
|
|
107
|
+
# Lint only uncommitted files
|
|
108
|
+
yard-lint lib/ --changed
|
|
109
|
+
```
|
|
110
|
+
|
|
111
|
+
**Use Cases:**
|
|
112
|
+
|
|
113
|
+
**Pre-commit Hook:**
|
|
114
|
+
```bash
|
|
115
|
+
#!/bin/bash
|
|
116
|
+
# .git/hooks/pre-commit
|
|
117
|
+
bundle exec yard-lint lib/ --staged --fail-on-severity error
|
|
118
|
+
```
|
|
119
|
+
|
|
120
|
+
**GitHub Actions CI/CD:**
|
|
121
|
+
```yaml
|
|
122
|
+
name: YARD Lint
|
|
123
|
+
on: [pull_request]
|
|
124
|
+
jobs:
|
|
125
|
+
lint:
|
|
126
|
+
runs-on: ubuntu-latest
|
|
127
|
+
steps:
|
|
128
|
+
- uses: actions/checkout@v4
|
|
129
|
+
with:
|
|
130
|
+
fetch-depth: 0 # Need full history for --diff
|
|
131
|
+
- name: Run YARD-Lint on changed files
|
|
132
|
+
run: bundle exec yard-lint lib/ --diff origin/${{ github.base_ref }}
|
|
133
|
+
```
|
|
134
|
+
|
|
135
|
+
**Legacy Codebase Incremental Adoption:**
|
|
136
|
+
```bash
|
|
137
|
+
# Only enforce rules on NEW code
|
|
138
|
+
yard-lint lib/ --diff main
|
|
139
|
+
```
|
|
140
|
+
|
|
141
|
+
### Documentation Coverage Statistics
|
|
142
|
+
|
|
143
|
+
Monitor and enforce minimum documentation coverage thresholds:
|
|
144
|
+
|
|
145
|
+
```bash
|
|
146
|
+
# Show coverage statistics with --stats flag
|
|
147
|
+
yard-lint lib/ --stats
|
|
148
|
+
|
|
149
|
+
# Output:
|
|
150
|
+
# Documentation Coverage: 85.5%
|
|
151
|
+
# Total objects: 120
|
|
152
|
+
# Documented: 102
|
|
153
|
+
# Undocumented: 18
|
|
154
|
+
|
|
155
|
+
# Enforce minimum coverage threshold (fails if below)
|
|
156
|
+
yard-lint lib/ --min-coverage 80
|
|
157
|
+
|
|
158
|
+
# Use with diff mode to check coverage only for changed files
|
|
159
|
+
yard-lint lib/ --diff main --min-coverage 90
|
|
160
|
+
|
|
161
|
+
# Quiet mode shows only summary with coverage
|
|
162
|
+
yard-lint lib/ --quiet --min-coverage 80
|
|
163
|
+
```
|
|
164
|
+
|
|
165
|
+
**Configuration File:**
|
|
166
|
+
```yaml
|
|
167
|
+
# .yard-lint.yml
|
|
168
|
+
AllValidators:
|
|
169
|
+
# Fail if documentation coverage is below this percentage
|
|
170
|
+
MinCoverage: 80.0
|
|
171
|
+
```
|
|
172
|
+
|
|
173
|
+
**CI/CD Pipeline Example:**
|
|
174
|
+
```yaml
|
|
175
|
+
name: Documentation Quality
|
|
176
|
+
on: [pull_request]
|
|
177
|
+
jobs:
|
|
178
|
+
coverage:
|
|
179
|
+
runs-on: ubuntu-latest
|
|
180
|
+
steps:
|
|
181
|
+
- uses: actions/checkout@v4
|
|
182
|
+
with:
|
|
183
|
+
fetch-depth: 0
|
|
184
|
+
- name: Check documentation coverage for new code
|
|
185
|
+
run: |
|
|
186
|
+
bundle exec yard-lint \
|
|
187
|
+
lib/ \
|
|
188
|
+
--diff origin/${{ github.base_ref }} \
|
|
189
|
+
--min-coverage 90 \
|
|
190
|
+
--quiet
|
|
191
|
+
```
|
|
192
|
+
|
|
193
|
+
**Key Features:**
|
|
194
|
+
- Calculates percentage of documented classes, modules, and methods
|
|
195
|
+
- CLI `--min-coverage` flag overrides config file setting
|
|
196
|
+
- Exit code 1 if coverage is below threshold
|
|
197
|
+
- Works with diff mode to enforce coverage only on changed files
|
|
198
|
+
- Performance optimized with auto-cleanup temp directories for large codebases
|
|
199
|
+
|
|
92
200
|
## Configuration
|
|
93
201
|
|
|
94
202
|
YARD-Lint is configured via a `.yard-lint.yml` configuration file (similar to `.rubocop.yml`).
|
|
@@ -116,6 +224,13 @@ AllValidators:
|
|
|
116
224
|
# Exit code behavior (error, warning, convention, never)
|
|
117
225
|
FailOnSeverity: warning
|
|
118
226
|
|
|
227
|
+
# Diff mode settings
|
|
228
|
+
DiffMode:
|
|
229
|
+
# Default base ref for --diff (auto-detects main/master if not specified)
|
|
230
|
+
DefaultBaseRef: ~
|
|
231
|
+
# Include untracked files in diff mode (not yet implemented)
|
|
232
|
+
IncludeUntracked: false
|
|
233
|
+
|
|
119
234
|
# Individual validator configuration
|
|
120
235
|
Documentation/UndocumentedObjects:
|
|
121
236
|
Description: 'Checks for classes, modules, and methods without documentation.'
|
|
@@ -273,7 +388,7 @@ YARD-Lint will automatically search for `.yard-lint.yml` in the current director
|
|
|
273
388
|
You can specify a different config file:
|
|
274
389
|
|
|
275
390
|
```bash
|
|
276
|
-
yard-lint --config path/to/config.yml
|
|
391
|
+
yard-lint lib/ --config path/to/config.yml
|
|
277
392
|
```
|
|
278
393
|
|
|
279
394
|
#### Configuration Inheritance
|
data/bin/yard-lint
CHANGED
|
@@ -8,6 +8,7 @@ require 'yard-lint'
|
|
|
8
8
|
|
|
9
9
|
options = {}
|
|
10
10
|
config_file = nil
|
|
11
|
+
diff_mode = nil
|
|
11
12
|
|
|
12
13
|
OptionParser.new do |opts|
|
|
13
14
|
opts.banner = 'Usage: yard-lint [options] PATH'
|
|
@@ -28,10 +29,32 @@ OptionParser.new do |opts|
|
|
|
28
29
|
options[:stats] = true
|
|
29
30
|
end
|
|
30
31
|
|
|
32
|
+
opts.on('--min-coverage PERCENT', Float, 'Minimum documentation coverage required (0-100)') do |percent|
|
|
33
|
+
options[:min_coverage] = percent
|
|
34
|
+
end
|
|
35
|
+
|
|
31
36
|
opts.on('--[no-]progress', 'Show progress indicator (default: auto)') do |value|
|
|
32
37
|
options[:progress] = value
|
|
33
38
|
end
|
|
34
39
|
|
|
40
|
+
opts.separator ''
|
|
41
|
+
opts.separator 'Diff mode options (mutually exclusive):'
|
|
42
|
+
|
|
43
|
+
opts.on('--diff [REF]', 'Lint only files changed since REF (default: main/master auto-detected)') do |ref|
|
|
44
|
+
diff_mode = { mode: :ref, base_ref: ref }
|
|
45
|
+
end
|
|
46
|
+
|
|
47
|
+
opts.on('--staged', 'Lint only staged files (git index)') do
|
|
48
|
+
diff_mode = { mode: :staged }
|
|
49
|
+
end
|
|
50
|
+
|
|
51
|
+
opts.on('--changed', 'Lint only uncommitted files') do
|
|
52
|
+
diff_mode = { mode: :changed }
|
|
53
|
+
end
|
|
54
|
+
|
|
55
|
+
opts.separator ''
|
|
56
|
+
opts.separator 'Other options:'
|
|
57
|
+
|
|
35
58
|
opts.on('--init', 'Generate .yard-lint.yml config file with defaults') do
|
|
36
59
|
options[:init] = true
|
|
37
60
|
end
|
|
@@ -47,6 +70,13 @@ OptionParser.new do |opts|
|
|
|
47
70
|
|
|
48
71
|
opts.on('-h', '--help', 'Show this help') do
|
|
49
72
|
puts opts
|
|
73
|
+
puts
|
|
74
|
+
puts 'Examples:'
|
|
75
|
+
puts ' yard-lint lib/ # Lint all files in lib/'
|
|
76
|
+
puts ' yard-lint --diff main lib/ # Lint only files changed since main branch'
|
|
77
|
+
puts ' yard-lint --staged lib/ # Lint only staged files'
|
|
78
|
+
puts ' yard-lint --changed lib/ # Lint only uncommitted files'
|
|
79
|
+
puts ' yard-lint --format json lib/ # Output in JSON format'
|
|
50
80
|
exit
|
|
51
81
|
end
|
|
52
82
|
end.parse!
|
|
@@ -96,12 +126,28 @@ end
|
|
|
96
126
|
Yard::Lint::Validators::Base.reset_command_cache!
|
|
97
127
|
Yard::Lint::Validators::Base.clear_yard_database!
|
|
98
128
|
|
|
99
|
-
#
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
129
|
+
# Load config and apply CLI overrides
|
|
130
|
+
config = if config_file
|
|
131
|
+
Yard::Lint::Config.from_file(config_file)
|
|
132
|
+
else
|
|
133
|
+
Yard::Lint::Config.load || Yard::Lint::Config.new
|
|
134
|
+
end
|
|
135
|
+
|
|
136
|
+
# Apply CLI min_coverage override if provided
|
|
137
|
+
config.min_coverage = options[:min_coverage] if options[:min_coverage]
|
|
138
|
+
|
|
139
|
+
# Run the linter
|
|
140
|
+
begin
|
|
141
|
+
result = Yard::Lint.run(
|
|
142
|
+
path: path,
|
|
143
|
+
config: config,
|
|
144
|
+
progress: options[:progress],
|
|
145
|
+
diff: diff_mode
|
|
146
|
+
)
|
|
147
|
+
rescue Yard::Lint::Git::Error => e
|
|
148
|
+
puts "Git error: #{e.message}"
|
|
149
|
+
exit 1
|
|
150
|
+
end
|
|
105
151
|
|
|
106
152
|
# Format and display results
|
|
107
153
|
case options[:format]
|
|
@@ -113,14 +159,40 @@ when 'json'
|
|
|
113
159
|
})
|
|
114
160
|
exit result.exit_code
|
|
115
161
|
when 'text', nil
|
|
162
|
+
# Calculate coverage stats if requested or configured
|
|
163
|
+
coverage = result.documentation_coverage if options[:stats] || options[:min_coverage] || config.min_coverage
|
|
164
|
+
|
|
165
|
+
# Show coverage stats if available
|
|
166
|
+
if coverage && (options[:stats] || options[:quiet])
|
|
167
|
+
puts "\nDocumentation Coverage: #{coverage[:coverage].round(2)}%"
|
|
168
|
+
puts " Total objects: #{coverage[:total]}"
|
|
169
|
+
puts " Documented: #{coverage[:documented]}"
|
|
170
|
+
puts " Undocumented: #{coverage[:total] - coverage[:documented]}"
|
|
171
|
+
|
|
172
|
+
if config.min_coverage
|
|
173
|
+
if coverage[:coverage] >= config.min_coverage
|
|
174
|
+
puts " Status: ✓ Meets minimum (#{config.min_coverage}%)"
|
|
175
|
+
else
|
|
176
|
+
puts " Status: ✗ Below minimum (#{config.min_coverage}%)"
|
|
177
|
+
end
|
|
178
|
+
end
|
|
179
|
+
puts
|
|
180
|
+
end
|
|
181
|
+
|
|
116
182
|
if result.clean?
|
|
117
|
-
|
|
183
|
+
# Still check coverage requirement even if no offenses
|
|
184
|
+
if coverage && config.min_coverage && coverage[:coverage] < config.min_coverage
|
|
185
|
+
puts "Error: Documentation coverage #{coverage[:coverage].round(2)}% is below minimum #{config.min_coverage}%"
|
|
186
|
+
exit result.exit_code
|
|
187
|
+
end
|
|
188
|
+
|
|
189
|
+
puts 'No offenses found' unless options[:quiet]
|
|
118
190
|
exit 0
|
|
119
191
|
else
|
|
120
192
|
# Show statistics if requested or in quiet mode
|
|
121
193
|
if options[:stats] || options[:quiet]
|
|
122
194
|
stats = result.statistics
|
|
123
|
-
puts "
|
|
195
|
+
puts "#{result.count} offense(s) detected"
|
|
124
196
|
puts " Errors: #{stats[:error]}"
|
|
125
197
|
puts " Warnings: #{stats[:warning]}"
|
|
126
198
|
puts " Conventions: #{stats[:convention]}"
|
|
@@ -58,7 +58,10 @@ module Yard
|
|
|
58
58
|
# @param command_string [String] the command to execute
|
|
59
59
|
# @return [Hash] hash with stdout, stderr, exit_code keys
|
|
60
60
|
def execute_command(command_string)
|
|
61
|
-
|
|
61
|
+
# Set up environment to load IRB shim before YARD (Ruby 3.5+ compatibility)
|
|
62
|
+
env = build_environment_with_shim
|
|
63
|
+
|
|
64
|
+
stdout, stderr, status = Open3.capture3(env, command_string)
|
|
62
65
|
{
|
|
63
66
|
stdout: stdout,
|
|
64
67
|
stderr: stderr,
|
|
@@ -66,6 +69,19 @@ module Yard
|
|
|
66
69
|
}
|
|
67
70
|
end
|
|
68
71
|
|
|
72
|
+
# Build environment hash with RUBYOPT to load IRB shim
|
|
73
|
+
# This ensures the shim is loaded in subprocesses (like yard list commands)
|
|
74
|
+
# @return [Hash] environment variables for command execution
|
|
75
|
+
def build_environment_with_shim
|
|
76
|
+
shim_path = File.expand_path('ext/irb_notifier_shim.rb', __dir__)
|
|
77
|
+
rubyopt = "-r#{shim_path}"
|
|
78
|
+
|
|
79
|
+
# Preserve existing RUBYOPT if present
|
|
80
|
+
rubyopt = "#{ENV['RUBYOPT'].strip} #{rubyopt}" if ENV['RUBYOPT']
|
|
81
|
+
|
|
82
|
+
{ 'RUBYOPT' => rubyopt }
|
|
83
|
+
end
|
|
84
|
+
|
|
69
85
|
# Deep clone a hash to prevent modifications to cached data
|
|
70
86
|
# @param hash [Hash] the hash to clone
|
|
71
87
|
# @return [Hash] deep cloned hash
|
data/lib/yard/lint/config.rb
CHANGED
|
@@ -129,6 +129,19 @@ module Yard
|
|
|
129
129
|
all_validators['FailOnSeverity'] || 'warning'
|
|
130
130
|
end
|
|
131
131
|
|
|
132
|
+
# Diff mode default base ref (main or master)
|
|
133
|
+
# @return [String, nil] default base ref for diff mode
|
|
134
|
+
def diff_mode_default_base_ref
|
|
135
|
+
diff_config = all_validators['DiffMode'] || {}
|
|
136
|
+
diff_config['DefaultBaseRef']
|
|
137
|
+
end
|
|
138
|
+
|
|
139
|
+
# Minimum documentation coverage percentage required
|
|
140
|
+
# @return [Float, nil] minimum coverage percentage (0-100) or nil if not set
|
|
141
|
+
def min_coverage
|
|
142
|
+
all_validators['MinCoverage']
|
|
143
|
+
end
|
|
144
|
+
|
|
132
145
|
# Check if a validator is enabled
|
|
133
146
|
# @param validator_name [String] full validator name (e.g., 'Tags/Order')
|
|
134
147
|
# @return [Boolean] true if validator is enabled
|
|
@@ -192,6 +205,13 @@ module Yard
|
|
|
192
205
|
@raw_config['AllValidators']['FailOnSeverity'] = value
|
|
193
206
|
end
|
|
194
207
|
|
|
208
|
+
# Set minimum coverage percentage
|
|
209
|
+
# @param value [Float] minimum coverage percentage (0-100)
|
|
210
|
+
def min_coverage=(value)
|
|
211
|
+
@raw_config['AllValidators'] ||= {}
|
|
212
|
+
@raw_config['AllValidators']['MinCoverage'] = value
|
|
213
|
+
end
|
|
214
|
+
|
|
195
215
|
# Allow hash-like access for convenience
|
|
196
216
|
# @param key [Symbol, String] attribute name to access
|
|
197
217
|
# @return [Object, nil] attribute value or nil if not found
|
|
@@ -27,6 +27,15 @@ module Yard
|
|
|
27
27
|
# Exit code behavior (error, warning, convention, never)
|
|
28
28
|
FailOnSeverity: warning
|
|
29
29
|
|
|
30
|
+
# Minimum documentation coverage percentage (0-100)
|
|
31
|
+
# Fails if coverage is below this threshold
|
|
32
|
+
# MinCoverage: 80.0
|
|
33
|
+
|
|
34
|
+
# Diff mode settings
|
|
35
|
+
DiffMode:
|
|
36
|
+
# Default base ref for --diff (auto-detects main/master if not specified)
|
|
37
|
+
DefaultBaseRef: ~
|
|
38
|
+
|
|
30
39
|
# Documentation validators
|
|
31
40
|
Documentation/UndocumentedObjects:
|
|
32
41
|
Description: 'Checks for classes, modules, and methods without documentation.'
|
|
@@ -0,0 +1,95 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
# Shim for IRB::Notifier to avoid IRB dependency in Ruby 3.5+
|
|
4
|
+
#
|
|
5
|
+
# YARD's legacy parser vendors old IRB code that depends on IRB::Notifier.
|
|
6
|
+
# In Ruby 3.5+, IRB is no longer part of the default gems and must be explicitly installed.
|
|
7
|
+
# This shim provides just enough functionality to keep YARD's legacy parser working
|
|
8
|
+
# without requiring the full IRB gem as a dependency.
|
|
9
|
+
#
|
|
10
|
+
# The notifier is only used for debug output in YARD's legacy parser, which we don't need.
|
|
11
|
+
#
|
|
12
|
+
# IMPORTANT: This shim only loads if IRB::Notifier is not already defined.
|
|
13
|
+
# If IRB gem is present, we use the real implementation instead.
|
|
14
|
+
|
|
15
|
+
# Only load the shim if IRB::Notifier is not already defined
|
|
16
|
+
unless defined?(IRB::Notifier)
|
|
17
|
+
# Try to load the real IRB notifier first
|
|
18
|
+
# If it fails (IRB not installed), we'll provide our shim
|
|
19
|
+
begin
|
|
20
|
+
# Suppress warnings during require attempt (Ruby 3.5+ warns about missing default gems)
|
|
21
|
+
original_verbose = $VERBOSE
|
|
22
|
+
$VERBOSE = nil
|
|
23
|
+
require 'irb/notifier'
|
|
24
|
+
rescue LoadError
|
|
25
|
+
# IRB not available, use our shim
|
|
26
|
+
# Mark as loaded to prevent further require attempts
|
|
27
|
+
$LOADED_FEATURES << 'irb/notifier.rb'
|
|
28
|
+
|
|
29
|
+
module IRB
|
|
30
|
+
# Minimal Notifier implementation that does nothing
|
|
31
|
+
# YARD's legacy parser uses this for debug output which we can safely ignore
|
|
32
|
+
class Notifier
|
|
33
|
+
# No-op message level constant
|
|
34
|
+
D_NOMSG = 0
|
|
35
|
+
|
|
36
|
+
class << self
|
|
37
|
+
# Returns a no-op notifier
|
|
38
|
+
# @param _prefix [String] notification prefix (ignored)
|
|
39
|
+
# @return [NoOpNotifier] a notifier that does nothing
|
|
40
|
+
def def_notifier(_prefix)
|
|
41
|
+
NoOpNotifier.new
|
|
42
|
+
end
|
|
43
|
+
end
|
|
44
|
+
|
|
45
|
+
# A notifier that silently discards all output
|
|
46
|
+
class NoOpNotifier
|
|
47
|
+
attr_accessor :level
|
|
48
|
+
|
|
49
|
+
def initialize
|
|
50
|
+
@level = Notifier::D_NOMSG
|
|
51
|
+
end
|
|
52
|
+
|
|
53
|
+
# Returns a no-op notifier for any sub-level
|
|
54
|
+
# @param _level [Integer] notification level (ignored)
|
|
55
|
+
# @param _prefix [String] notification prefix (ignored)
|
|
56
|
+
# @return [NoOpNotifier] a notifier that does nothing
|
|
57
|
+
def def_notifier(_level, _prefix)
|
|
58
|
+
NoOpNotifier.new
|
|
59
|
+
end
|
|
60
|
+
|
|
61
|
+
# Silently ignore pretty-print calls
|
|
62
|
+
# @param _obj [Object] object to pretty-print (ignored)
|
|
63
|
+
# @return [nil]
|
|
64
|
+
def pp(_obj)
|
|
65
|
+
nil
|
|
66
|
+
end
|
|
67
|
+
|
|
68
|
+
# Silently ignore print calls
|
|
69
|
+
# @param _args [Array] print arguments (ignored)
|
|
70
|
+
# @return [nil]
|
|
71
|
+
def print(*_args)
|
|
72
|
+
nil
|
|
73
|
+
end
|
|
74
|
+
|
|
75
|
+
# Silently ignore puts calls
|
|
76
|
+
# @param _args [Array] puts arguments (ignored)
|
|
77
|
+
# @return [nil]
|
|
78
|
+
def puts(*_args)
|
|
79
|
+
nil
|
|
80
|
+
end
|
|
81
|
+
|
|
82
|
+
# Silently ignore printf calls
|
|
83
|
+
# @param _args [Array] printf arguments (ignored)
|
|
84
|
+
# @return [nil]
|
|
85
|
+
def printf(*_args)
|
|
86
|
+
nil
|
|
87
|
+
end
|
|
88
|
+
end
|
|
89
|
+
end
|
|
90
|
+
end
|
|
91
|
+
ensure
|
|
92
|
+
# Restore original verbosity setting
|
|
93
|
+
$VERBOSE = original_verbose
|
|
94
|
+
end
|
|
95
|
+
end
|
|
@@ -0,0 +1,125 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Yard
|
|
4
|
+
module Lint
|
|
5
|
+
# Git integration for diff mode functionality
|
|
6
|
+
module Git
|
|
7
|
+
# Custom error class for Git-related errors
|
|
8
|
+
class Error < StandardError; end
|
|
9
|
+
|
|
10
|
+
class << self
|
|
11
|
+
# Detect the default branch (main or master)
|
|
12
|
+
# @return [String] 'main', 'master', or nil if neither exists
|
|
13
|
+
def default_branch
|
|
14
|
+
# Try main first (modern default)
|
|
15
|
+
return 'main' if branch_exists?('main')
|
|
16
|
+
# Fall back to master (legacy default)
|
|
17
|
+
return 'master' if branch_exists?('master')
|
|
18
|
+
|
|
19
|
+
nil
|
|
20
|
+
end
|
|
21
|
+
|
|
22
|
+
# Check if a git ref exists
|
|
23
|
+
# @param ref [String] the git ref to check
|
|
24
|
+
# @return [Boolean] true if ref exists
|
|
25
|
+
def branch_exists?(ref)
|
|
26
|
+
_stdout, _stderr, status = Open3.capture3('git', 'rev-parse', '--verify', '--quiet', ref)
|
|
27
|
+
status.success?
|
|
28
|
+
end
|
|
29
|
+
|
|
30
|
+
# Get files changed since a base ref
|
|
31
|
+
# @param base_ref [String, nil] the base ref to compare against (nil for auto-detect)
|
|
32
|
+
# @param path [String] the path to filter files within
|
|
33
|
+
# @return [Array<String>] absolute paths to changed Ruby files
|
|
34
|
+
def changed_files(base_ref, path)
|
|
35
|
+
base_ref ||= default_branch
|
|
36
|
+
raise Error, 'Could not detect default branch (main/master)' unless base_ref
|
|
37
|
+
|
|
38
|
+
ensure_git_repository!
|
|
39
|
+
|
|
40
|
+
# Use three-dot diff to compare against merge base
|
|
41
|
+
stdout, stderr, status = Open3.capture3('git', 'diff', '--name-only', "#{base_ref}...HEAD")
|
|
42
|
+
|
|
43
|
+
unless status.success?
|
|
44
|
+
raise Error, "Git diff failed: #{stderr.strip}"
|
|
45
|
+
end
|
|
46
|
+
|
|
47
|
+
filter_ruby_files(stdout.split("\n"), path)
|
|
48
|
+
end
|
|
49
|
+
|
|
50
|
+
# Get staged files (files in git index)
|
|
51
|
+
# @param path [String] the path to filter files within
|
|
52
|
+
# @return [Array<String>] absolute paths to staged Ruby files
|
|
53
|
+
def staged_files(path)
|
|
54
|
+
ensure_git_repository!
|
|
55
|
+
|
|
56
|
+
# ACM filter: Added, Copied, Modified (exclude Deleted)
|
|
57
|
+
stdout, stderr, status = Open3.capture3(
|
|
58
|
+
'git', 'diff', '--cached', '--name-only', '--diff-filter=ACM'
|
|
59
|
+
)
|
|
60
|
+
|
|
61
|
+
unless status.success?
|
|
62
|
+
raise Error, "Git diff failed: #{stderr.strip}"
|
|
63
|
+
end
|
|
64
|
+
|
|
65
|
+
filter_ruby_files(stdout.split("\n"), path)
|
|
66
|
+
end
|
|
67
|
+
|
|
68
|
+
# Get uncommitted files (all changes in working directory)
|
|
69
|
+
# @param path [String] the path to filter files within
|
|
70
|
+
# @return [Array<String>] absolute paths to uncommitted Ruby files
|
|
71
|
+
def uncommitted_files(path)
|
|
72
|
+
ensure_git_repository!
|
|
73
|
+
|
|
74
|
+
# Get both staged and unstaged changes
|
|
75
|
+
stdout, stderr, status = Open3.capture3('git', 'diff', '--name-only', 'HEAD')
|
|
76
|
+
|
|
77
|
+
unless status.success?
|
|
78
|
+
raise Error, "Git diff failed: #{stderr.strip}"
|
|
79
|
+
end
|
|
80
|
+
|
|
81
|
+
filter_ruby_files(stdout.split("\n"), path)
|
|
82
|
+
end
|
|
83
|
+
|
|
84
|
+
private
|
|
85
|
+
|
|
86
|
+
# Ensure we're in a git repository
|
|
87
|
+
# @raise [Error] if not in a git repository
|
|
88
|
+
def ensure_git_repository!
|
|
89
|
+
_stdout, _stderr, status = Open3.capture3('git', 'rev-parse', '--git-dir')
|
|
90
|
+
|
|
91
|
+
return if status.success?
|
|
92
|
+
|
|
93
|
+
raise Error, 'Not a git repository'
|
|
94
|
+
end
|
|
95
|
+
|
|
96
|
+
# Filter for Ruby files within path and convert to absolute paths
|
|
97
|
+
# @param files [Array<String>] relative file paths from git
|
|
98
|
+
# @param path [String] the base path to filter within
|
|
99
|
+
# @return [Array<String>] absolute paths to Ruby files that exist
|
|
100
|
+
def filter_ruby_files(files, path)
|
|
101
|
+
base_path = File.expand_path(path)
|
|
102
|
+
|
|
103
|
+
files
|
|
104
|
+
.select { |f| f.end_with?('.rb') }
|
|
105
|
+
.map { |f| File.expand_path(f) }
|
|
106
|
+
.select { |f| File.exist?(f) } # Skip deleted files
|
|
107
|
+
.select { |f| file_within_path?(f, base_path) }
|
|
108
|
+
end
|
|
109
|
+
|
|
110
|
+
# Check if file is within the specified path
|
|
111
|
+
# @param file [String] absolute file path
|
|
112
|
+
# @param base_path [String] absolute base path
|
|
113
|
+
# @return [Boolean] true if file is within base_path
|
|
114
|
+
def file_within_path?(file, base_path)
|
|
115
|
+
# Handle both directory and file base_path
|
|
116
|
+
if File.directory?(base_path)
|
|
117
|
+
file.start_with?(base_path + '/')
|
|
118
|
+
else
|
|
119
|
+
file == base_path
|
|
120
|
+
end
|
|
121
|
+
end
|
|
122
|
+
end
|
|
123
|
+
end
|
|
124
|
+
end
|
|
125
|
+
end
|
|
@@ -13,14 +13,16 @@ module Yard
|
|
|
13
13
|
# Convention severity level constant
|
|
14
14
|
SEVERITY_CONVENTION = 'convention'
|
|
15
15
|
|
|
16
|
-
attr_reader :config
|
|
16
|
+
attr_reader :config, :files
|
|
17
17
|
|
|
18
18
|
# Initialize aggregate result with array of validator results
|
|
19
19
|
# @param results [Array<Results::Base>] array of validator result objects
|
|
20
20
|
# @param config [Config, nil] configuration object
|
|
21
|
-
|
|
21
|
+
# @param files [Array<String>, nil] array of files that were analyzed
|
|
22
|
+
def initialize(results, config = nil, files = nil)
|
|
22
23
|
@results = Array(results)
|
|
23
24
|
@config = config
|
|
25
|
+
@files = Array(files)
|
|
24
26
|
end
|
|
25
27
|
|
|
26
28
|
# Get all offenses from all validators
|
|
@@ -60,10 +62,28 @@ module Yard
|
|
|
60
62
|
stats
|
|
61
63
|
end
|
|
62
64
|
|
|
65
|
+
# Calculate documentation coverage statistics
|
|
66
|
+
# @return [Hash] coverage statistics with :total, :documented, :coverage keys
|
|
67
|
+
def documentation_coverage
|
|
68
|
+
return @documentation_coverage if defined?(@documentation_coverage)
|
|
69
|
+
|
|
70
|
+
return nil unless @config && !@files.empty?
|
|
71
|
+
|
|
72
|
+
calculator = StatsCalculator.new(@config, @files)
|
|
73
|
+
@documentation_coverage = calculator.calculate
|
|
74
|
+
end
|
|
75
|
+
|
|
63
76
|
# Determine exit code based on configured fail_on_severity
|
|
64
77
|
# Uses the config object stored during initialization
|
|
65
78
|
# @return [Integer] 0 for success, 1 for failure
|
|
66
79
|
def exit_code
|
|
80
|
+
# Check minimum coverage requirement first
|
|
81
|
+
if @config&.min_coverage &&
|
|
82
|
+
documentation_coverage &&
|
|
83
|
+
documentation_coverage[:coverage] < @config.min_coverage
|
|
84
|
+
return 1
|
|
85
|
+
end
|
|
86
|
+
|
|
67
87
|
return 0 if offenses.empty?
|
|
68
88
|
return 0 unless @config # No config means don't fail
|
|
69
89
|
|
data/lib/yard/lint/runner.rb
CHANGED
|
@@ -21,7 +21,7 @@ module Yard
|
|
|
21
21
|
def run
|
|
22
22
|
raw_results = run_validators
|
|
23
23
|
parsed_results = parse_results(raw_results)
|
|
24
|
-
build_result(parsed_results)
|
|
24
|
+
build_result(parsed_results, @selection)
|
|
25
25
|
end
|
|
26
26
|
|
|
27
27
|
private
|
|
@@ -116,9 +116,10 @@ module Yard
|
|
|
116
116
|
|
|
117
117
|
# Build final result object
|
|
118
118
|
# @param results [Array<Results::Base>] array of validator result objects
|
|
119
|
+
# @param files [Array<String>] array of files that were analyzed
|
|
119
120
|
# @return [Results::Aggregate] aggregate result object
|
|
120
|
-
def build_result(results)
|
|
121
|
-
Results::Aggregate.new(results, config)
|
|
121
|
+
def build_result(results, files)
|
|
122
|
+
Results::Aggregate.new(results, config, files)
|
|
122
123
|
end
|
|
123
124
|
end
|
|
124
125
|
end
|
|
@@ -0,0 +1,157 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Yard
|
|
4
|
+
module Lint
|
|
5
|
+
# Calculates documentation coverage statistics
|
|
6
|
+
# Runs YARD queries to count documented vs undocumented objects
|
|
7
|
+
class StatsCalculator
|
|
8
|
+
attr_reader :config, :files
|
|
9
|
+
|
|
10
|
+
# @param config [Yard::Lint::Config] configuration object
|
|
11
|
+
# @param files [Array<String>] files to analyze
|
|
12
|
+
def initialize(config, files)
|
|
13
|
+
@config = config
|
|
14
|
+
@files = Array(files).compact
|
|
15
|
+
end
|
|
16
|
+
|
|
17
|
+
# Calculate documentation coverage statistics
|
|
18
|
+
# @return [Hash] statistics with :total, :documented, :coverage keys
|
|
19
|
+
def calculate
|
|
20
|
+
return default_stats if files.empty?
|
|
21
|
+
|
|
22
|
+
raw_stats = run_yard_stats_query
|
|
23
|
+
return default_stats if raw_stats.empty?
|
|
24
|
+
|
|
25
|
+
parsed_stats = parse_stats_output(raw_stats)
|
|
26
|
+
filtered_stats = apply_exclusions(parsed_stats)
|
|
27
|
+
|
|
28
|
+
calculate_coverage_percentage(filtered_stats)
|
|
29
|
+
end
|
|
30
|
+
|
|
31
|
+
private
|
|
32
|
+
|
|
33
|
+
# Default stats for empty file lists
|
|
34
|
+
# @return [Hash]
|
|
35
|
+
def default_stats
|
|
36
|
+
{ total: 0, documented: 0, coverage: 100.0 }
|
|
37
|
+
end
|
|
38
|
+
|
|
39
|
+
# Run YARD query to get object documentation status
|
|
40
|
+
# @return [String] YARD query output
|
|
41
|
+
def run_yard_stats_query
|
|
42
|
+
# Create temp file with file list
|
|
43
|
+
Tempfile.create(['yard_stats', '.txt']) do |f|
|
|
44
|
+
files.each { |file| f.puts(Shellwords.escape(file)) }
|
|
45
|
+
f.flush
|
|
46
|
+
|
|
47
|
+
query = build_stats_query
|
|
48
|
+
|
|
49
|
+
# Use temp directory for YARD database (auto-cleanup)
|
|
50
|
+
Dir.mktmpdir("yard_stats_#{Process.pid}_") do |temp_dir|
|
|
51
|
+
cmd = build_yard_command(f.path, query, temp_dir)
|
|
52
|
+
|
|
53
|
+
stdout, _stderr, status = Open3.capture3(cmd)
|
|
54
|
+
|
|
55
|
+
# Return empty string if YARD command fails
|
|
56
|
+
return '' unless status.exitstatus.zero?
|
|
57
|
+
|
|
58
|
+
stdout
|
|
59
|
+
end
|
|
60
|
+
end
|
|
61
|
+
end
|
|
62
|
+
|
|
63
|
+
# Build the YARD query for stats collection
|
|
64
|
+
# @return [String] YARD query string
|
|
65
|
+
def build_stats_query
|
|
66
|
+
<<~QUERY.chomp
|
|
67
|
+
type = object.type.to_s; state = object.docstring.all.empty? ? "undoc" : "doc"; puts "\#{type}:\#{state}"
|
|
68
|
+
QUERY
|
|
69
|
+
end
|
|
70
|
+
|
|
71
|
+
# Build complete YARD command
|
|
72
|
+
# @param file_list_path [String] path to file with list of files
|
|
73
|
+
# @param query [String] YARD query to execute
|
|
74
|
+
# @param temp_dir [String] temporary directory for YARD database
|
|
75
|
+
# @return [String] complete command string
|
|
76
|
+
def build_yard_command(file_list_path, query, temp_dir)
|
|
77
|
+
<<~CMD.tr("\n", ' ').strip
|
|
78
|
+
cat #{Shellwords.escape(file_list_path)} | xargs yard list
|
|
79
|
+
--charset utf-8
|
|
80
|
+
--markup markdown
|
|
81
|
+
--no-progress
|
|
82
|
+
--query #{Shellwords.escape(query)}
|
|
83
|
+
-q
|
|
84
|
+
-b #{Shellwords.escape(temp_dir)}
|
|
85
|
+
CMD
|
|
86
|
+
end
|
|
87
|
+
|
|
88
|
+
# Parse YARD stats output
|
|
89
|
+
# Format: "type:state" (e.g., "method:doc", "class:undoc")
|
|
90
|
+
# @param output [String] YARD command output
|
|
91
|
+
# @return [Hash] counts by type and state
|
|
92
|
+
def parse_stats_output(output)
|
|
93
|
+
stats = Hash.new { |h, k| h[k] = { documented: 0, undocumented: 0 } }
|
|
94
|
+
|
|
95
|
+
output.each_line do |line|
|
|
96
|
+
line.strip!
|
|
97
|
+
next if line.empty?
|
|
98
|
+
|
|
99
|
+
type, state = line.split(':', 2)
|
|
100
|
+
next unless type && state
|
|
101
|
+
|
|
102
|
+
if state == 'doc'
|
|
103
|
+
stats[type][:documented] += 1
|
|
104
|
+
elsif state == 'undoc'
|
|
105
|
+
stats[type][:undocumented] += 1
|
|
106
|
+
end
|
|
107
|
+
end
|
|
108
|
+
|
|
109
|
+
stats
|
|
110
|
+
end
|
|
111
|
+
|
|
112
|
+
# Apply validator exclusions to stats
|
|
113
|
+
# Respects ExcludedMethods and other validator-specific exclusions
|
|
114
|
+
# @param stats [Hash] parsed stats
|
|
115
|
+
# @return [Hash] filtered stats
|
|
116
|
+
def apply_exclusions(stats)
|
|
117
|
+
# Get excluded methods from UndocumentedObjects validator config
|
|
118
|
+
excluded_methods = config.validator_config('Documentation/UndocumentedObjects', 'ExcludedMethods') || []
|
|
119
|
+
|
|
120
|
+
return stats if excluded_methods.empty?
|
|
121
|
+
|
|
122
|
+
# For now, we can't easily filter out specific methods without re-parsing
|
|
123
|
+
# This would require running YARD query with method names
|
|
124
|
+
# TODO: Implement precise method-level filtering if needed
|
|
125
|
+
|
|
126
|
+
stats
|
|
127
|
+
end
|
|
128
|
+
|
|
129
|
+
# Calculate coverage percentage from stats
|
|
130
|
+
# @param stats [Hash] filtered stats by type
|
|
131
|
+
# @return [Hash] final coverage statistics
|
|
132
|
+
def calculate_coverage_percentage(stats)
|
|
133
|
+
total_documented = 0
|
|
134
|
+
total_undocumented = 0
|
|
135
|
+
|
|
136
|
+
stats.each_value do |counts|
|
|
137
|
+
total_documented += counts[:documented]
|
|
138
|
+
total_undocumented += counts[:undocumented]
|
|
139
|
+
end
|
|
140
|
+
|
|
141
|
+
total_objects = total_documented + total_undocumented
|
|
142
|
+
|
|
143
|
+
coverage = if total_objects.zero?
|
|
144
|
+
100.0
|
|
145
|
+
else
|
|
146
|
+
(total_documented.to_f / total_objects * 100)
|
|
147
|
+
end
|
|
148
|
+
|
|
149
|
+
{
|
|
150
|
+
total: total_objects,
|
|
151
|
+
documented: total_documented,
|
|
152
|
+
coverage: coverage
|
|
153
|
+
}
|
|
154
|
+
end
|
|
155
|
+
end
|
|
156
|
+
end
|
|
157
|
+
end
|
data/lib/yard/lint/version.rb
CHANGED
data/lib/yard/lint.rb
CHANGED
|
@@ -19,10 +19,20 @@ module Yard
|
|
|
19
19
|
# @param config_file [String, nil] path to config file
|
|
20
20
|
# (auto-loads .yard-lint.yml if not specified)
|
|
21
21
|
# @param progress [Boolean] show progress indicator (default: true for TTY)
|
|
22
|
+
# @param diff [Hash, nil] diff mode options
|
|
23
|
+
# - :mode [Symbol] one of :ref, :staged, :changed
|
|
24
|
+
# - :base_ref [String, nil] base ref for :ref mode (auto-detects main/master if nil)
|
|
22
25
|
# @return [Yard::Lint::Result] result object with offenses
|
|
23
|
-
def run(path:, config: nil, config_file: nil, progress: nil)
|
|
26
|
+
def run(path:, config: nil, config_file: nil, progress: nil, diff: nil)
|
|
24
27
|
config ||= load_config(config_file)
|
|
25
|
-
|
|
28
|
+
|
|
29
|
+
# Determine files to lint based on diff mode or normal path expansion
|
|
30
|
+
files = if diff
|
|
31
|
+
get_diff_files(diff, path, config)
|
|
32
|
+
else
|
|
33
|
+
expand_path(path, config)
|
|
34
|
+
end
|
|
35
|
+
|
|
26
36
|
runner = Runner.new(files, config)
|
|
27
37
|
|
|
28
38
|
# Enable progress by default if output is a TTY
|
|
@@ -45,6 +55,32 @@ module Yard
|
|
|
45
55
|
end
|
|
46
56
|
end
|
|
47
57
|
|
|
58
|
+
# Get files from git diff based on diff mode
|
|
59
|
+
# @param diff [Hash] diff mode options
|
|
60
|
+
# @param path [String, Array<String>] path or array of paths to filter within
|
|
61
|
+
# @param config [Yard::Lint::Config] configuration object
|
|
62
|
+
# @return [Array<String>] array of absolute file paths
|
|
63
|
+
def get_diff_files(diff, path, config)
|
|
64
|
+
# Get changed files from git based on mode
|
|
65
|
+
git_files = case diff[:mode]
|
|
66
|
+
when :ref
|
|
67
|
+
Git.changed_files(diff[:base_ref], path)
|
|
68
|
+
when :staged
|
|
69
|
+
Git.staged_files(path)
|
|
70
|
+
when :changed
|
|
71
|
+
Git.uncommitted_files(path)
|
|
72
|
+
else
|
|
73
|
+
raise ArgumentError, "Unknown diff mode: #{diff[:mode]}"
|
|
74
|
+
end
|
|
75
|
+
|
|
76
|
+
# Apply exclusion patterns
|
|
77
|
+
git_files.reject do |file|
|
|
78
|
+
config.exclude.any? do |pattern|
|
|
79
|
+
File.fnmatch(pattern, file, File::FNM_PATHNAME | File::FNM_EXTGLOB)
|
|
80
|
+
end
|
|
81
|
+
end
|
|
82
|
+
end
|
|
83
|
+
|
|
48
84
|
# Expand path/glob patterns into an array of files
|
|
49
85
|
# @param path [String, Array<String>] path or array of paths
|
|
50
86
|
# @param config [Yard::Lint::Config] configuration object
|
data/lib/yard-lint.rb
CHANGED
|
@@ -1,10 +1,15 @@
|
|
|
1
1
|
# frozen_string_literal: true
|
|
2
2
|
|
|
3
|
+
# Load IRB notifier shim before YARD to avoid IRB dependency in Ruby 3.5+
|
|
4
|
+
# This must be loaded before any YARD code is required
|
|
5
|
+
require_relative 'yard/lint/ext/irb_notifier_shim'
|
|
6
|
+
|
|
3
7
|
require 'zeitwerk'
|
|
4
8
|
|
|
5
9
|
# Setup Zeitwerk loader for gem
|
|
6
10
|
loader = Zeitwerk::Loader.for_gem(warn_on_extra_files: false)
|
|
7
11
|
loader.ignore(__FILE__)
|
|
12
|
+
loader.ignore("#{__dir__}/yard/lint/ext")
|
|
8
13
|
loader.setup
|
|
9
14
|
|
|
10
15
|
# Manually load the main module since it contains class-level methods
|
metadata
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
|
2
2
|
name: yard-lint
|
|
3
3
|
version: !ruby/object:Gem::Version
|
|
4
|
-
version: 1.
|
|
4
|
+
version: 1.2.0
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- Maciej Mensfeld
|
|
@@ -9,20 +9,6 @@ bindir: bin
|
|
|
9
9
|
cert_chain: []
|
|
10
10
|
date: 1980-01-02 00:00:00.000000000 Z
|
|
11
11
|
dependencies:
|
|
12
|
-
- !ruby/object:Gem::Dependency
|
|
13
|
-
name: irb
|
|
14
|
-
requirement: !ruby/object:Gem::Requirement
|
|
15
|
-
requirements:
|
|
16
|
-
- - ">="
|
|
17
|
-
- !ruby/object:Gem::Version
|
|
18
|
-
version: '0'
|
|
19
|
-
type: :runtime
|
|
20
|
-
prerelease: false
|
|
21
|
-
version_requirements: !ruby/object:Gem::Requirement
|
|
22
|
-
requirements:
|
|
23
|
-
- - ">="
|
|
24
|
-
- !ruby/object:Gem::Version
|
|
25
|
-
version: '0'
|
|
26
12
|
- !ruby/object:Gem::Dependency
|
|
27
13
|
name: yard
|
|
28
14
|
requirement: !ruby/object:Gem::Requirement
|
|
@@ -77,7 +63,9 @@ files:
|
|
|
77
63
|
- lib/yard/lint/config_generator.rb
|
|
78
64
|
- lib/yard/lint/config_loader.rb
|
|
79
65
|
- lib/yard/lint/errors.rb
|
|
66
|
+
- lib/yard/lint/ext/irb_notifier_shim.rb
|
|
80
67
|
- lib/yard/lint/formatters/progress.rb
|
|
68
|
+
- lib/yard/lint/git.rb
|
|
81
69
|
- lib/yard/lint/parsers/base.rb
|
|
82
70
|
- lib/yard/lint/parsers/one_line_base.rb
|
|
83
71
|
- lib/yard/lint/parsers/two_line_base.rb
|
|
@@ -85,6 +73,7 @@ files:
|
|
|
85
73
|
- lib/yard/lint/results/aggregate.rb
|
|
86
74
|
- lib/yard/lint/results/base.rb
|
|
87
75
|
- lib/yard/lint/runner.rb
|
|
76
|
+
- lib/yard/lint/stats_calculator.rb
|
|
88
77
|
- lib/yard/lint/validators/base.rb
|
|
89
78
|
- lib/yard/lint/validators/config.rb
|
|
90
79
|
- lib/yard/lint/validators/documentation/markdown_syntax.rb
|