-
Notifications
You must be signed in to change notification settings - Fork 42
Plugin Docs: Add filesystem validation rules #2476
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from all commits
d403980
9e0104a
584e3ba
f251849
6619d24
e788ec7
8f72da2
976a4e4
f45896f
4969615
eeb2c4c
bebf7f8
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -1,15 +1,23 @@ | ||
| import createDebug from 'debug'; | ||
| import { validate } from '../validation/engine.js'; | ||
| import { formatResult } from '../validation/format.js'; | ||
| import { allRules } from '../validation/rules/index.js'; | ||
|
|
||
| const debug = createDebug('plugin-docs-cli:validate'); | ||
|
|
||
| export async function validateCommand(docsPath: string): Promise<void> { | ||
| debug('Validating docs at: %s', docsPath); | ||
| export async function validateCommand( | ||
| docsPath: string, | ||
| options: { strict: boolean; json: boolean } = { strict: true, json: false } | ||
| ): Promise<void> { | ||
sunker marked this conversation as resolved.
Show resolved
Hide resolved
|
||
| debug('Validating docs at: %s (strict: %s, json: %s)', docsPath, options.strict, options.json); | ||
|
|
||
| const result = await validate({ docsPath }, allRules); | ||
| const result = await validate({ docsPath, strict: options.strict }, allRules); | ||
|
|
||
| console.log(JSON.stringify(result, null, 2)); | ||
| if (options.json) { | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. not for this PR but good to open an issue: make an option to output json to a file. sometimes is much easier to read an output file than stdout json output.
Collaborator
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. An alternative is to pipe the output to a file if that would be a good enough solution?
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Yes piping could work for now! Added an issue so we can track this: https://github.com/grafana/grafana-catalog-team/issues/782 |
||
| console.log(JSON.stringify(result, null, 2)); | ||
| } else { | ||
| console.log(formatResult(result)); | ||
| } | ||
|
|
||
| process.exitCode = result.valid ? 0 : 1; | ||
| } | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,81 @@ | ||
| import { describe, it, expect } from 'vitest'; | ||
| import { formatResult } from './format.js'; | ||
| import type { ValidationResult } from './types.js'; | ||
|
|
||
| describe('formatResult', () => { | ||
| it('should show success for valid result with no diagnostics', () => { | ||
| const result: ValidationResult = { valid: true, diagnostics: [] }; | ||
| const output = formatResult(result); | ||
| expect(output).toContain('✓'); | ||
| expect(output).toContain('valid'); | ||
| }); | ||
|
|
||
| it('should show error count and ✗ icon when errors exist', () => { | ||
| const result: ValidationResult = { | ||
| valid: false, | ||
| diagnostics: [{ rule: 'test', severity: 'error', title: 'Bad thing', detail: 'Fix it' }], | ||
| }; | ||
| const output = formatResult(result); | ||
| expect(output).toContain('✗'); | ||
| expect(output).toContain('1 error'); | ||
| expect(output).toContain('Bad thing'); | ||
| expect(output).toContain('Fix it'); | ||
| }); | ||
|
|
||
| it('should show ⚠ icon when only warnings exist', () => { | ||
| const result: ValidationResult = { | ||
| valid: true, | ||
| diagnostics: [{ rule: 'test', severity: 'warning', title: 'Heads up', detail: '' }], | ||
| }; | ||
| const output = formatResult(result); | ||
| expect(output).toContain('⚠'); | ||
| expect(output).toContain('1 warning'); | ||
| }); | ||
|
|
||
| it('should pluralize counts correctly', () => { | ||
| const result: ValidationResult = { | ||
| valid: false, | ||
| diagnostics: [ | ||
| { rule: 'a', severity: 'error', title: 'A', detail: '' }, | ||
| { rule: 'b', severity: 'error', title: 'B', detail: '' }, | ||
| { rule: 'c', severity: 'warning', title: 'C', detail: '' }, | ||
| ], | ||
| }; | ||
| const output = formatResult(result); | ||
| expect(output).toContain('2 errors'); | ||
| expect(output).toContain('1 warning'); | ||
| }); | ||
|
|
||
| it('should include file path when present', () => { | ||
| const result: ValidationResult = { | ||
| valid: false, | ||
| diagnostics: [{ rule: 'test', severity: 'error', file: 'docs/page.md', title: 'Problem', detail: '' }], | ||
| }; | ||
| const output = formatResult(result); | ||
| expect(output).toContain('docs/page.md'); | ||
| }); | ||
|
|
||
| it('should show file:line when line number is present', () => { | ||
| const result: ValidationResult = { | ||
| valid: false, | ||
| diagnostics: [{ rule: 'test', severity: 'error', file: 'page.md', line: 7, title: 'H1', detail: '' }], | ||
| }; | ||
| const output = formatResult(result); | ||
| expect(output).toContain('page.md:7'); | ||
| }); | ||
|
|
||
| it('should handle mixed severities', () => { | ||
| const result: ValidationResult = { | ||
| valid: false, | ||
| diagnostics: [ | ||
| { rule: 'a', severity: 'error', title: 'Error', detail: '' }, | ||
| { rule: 'b', severity: 'warning', title: 'Warning', detail: '' }, | ||
| { rule: 'c', severity: 'info', title: 'Info', detail: '' }, | ||
| ], | ||
| }; | ||
| const output = formatResult(result); | ||
| expect(output).toContain('1 error'); | ||
| expect(output).toContain('1 warning'); | ||
| expect(output).toContain('1 info'); | ||
| }); | ||
| }); |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,54 @@ | ||
| import type { Severity, ValidationResult } from './types.js'; | ||
|
|
||
| const SEVERITY_LABEL: Record<Severity, string> = { | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This works, but my intention with my previous comment and example was to convinced you to turn |
||
| error: 'error', | ||
| warning: 'warn ', | ||
| info: 'info ', | ||
| }; | ||
|
|
||
| /** | ||
| * Formats a validation result as human-readable text. | ||
| */ | ||
| export function formatResult(result: ValidationResult): string { | ||
| const lines: string[] = []; | ||
|
|
||
| if (result.diagnostics.length === 0) { | ||
| lines.push('✓ Documentation is valid'); | ||
| return lines.join('\n'); | ||
| } | ||
|
|
||
| const counts: Record<Severity, number> = { error: 0, warning: 0, info: 0 }; | ||
| for (const d of result.diagnostics) { | ||
| counts[d.severity]++; | ||
| } | ||
| const { error: errors, warning: warnings, info: infos } = counts; | ||
|
|
||
| // summary line | ||
| const parts: string[] = []; | ||
| if (errors > 0) { | ||
| parts.push(`${errors} error${errors !== 1 ? 's' : ''}`); | ||
| } | ||
| if (warnings > 0) { | ||
| parts.push(`${warnings} warning${warnings !== 1 ? 's' : ''}`); | ||
| } | ||
| if (infos > 0) { | ||
| parts.push(`${infos} info`); | ||
| } | ||
|
|
||
| const icon = errors > 0 ? '✗' : '⚠'; | ||
| lines.push(`${icon} Documentation has ${parts.join(' and ')}`); | ||
| lines.push(''); | ||
|
|
||
| for (const d of result.diagnostics) { | ||
| const label = SEVERITY_LABEL[d.severity] ?? d.severity; | ||
| const location = d.file ? (d.line ? ` ${d.file}:${d.line}` : ` ${d.file}`) : ''; | ||
| lines.push(` ${label}${location}`); | ||
| lines.push(` ${d.title}`); | ||
| if (d.detail) { | ||
| lines.push(` ${d.detail}`); | ||
| } | ||
| lines.push(''); | ||
| } | ||
|
|
||
| return lines.join('\n'); | ||
| } | ||
Uh oh!
There was an error while loading. Please reload this page.