Skip to content

Commit

Permalink
docs: Add linter for the documentation
Browse files Browse the repository at this point in the history
Add mdl as a linter for new cnf-testsuite documentation
Add custom rules for keeping format of the documentation
Add lint to git pipelines to run mdl.

Signed-off-by: Konstantin Yarovoy <[email protected]>
  • Loading branch information
Konstantin Yarovoy committed Apr 22, 2024
1 parent 8114e5d commit 5e6cacb
Show file tree
Hide file tree
Showing 4 changed files with 153 additions and 0 deletions.
27 changes: 27 additions & 0 deletions .github/workflows/lint-docs.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
name: Lint Docs

on:
push:
paths:
- 'docs/**'
pull_request:
paths:
- 'docs/**'

jobs:
lint-docs:
runs-on: ubuntu-latest
container:
image: ruby:latest

steps:
- name: Checkout code
uses: actions/checkout@v2

- name: Install mdl
run: |
gem install mdl
- name: Run mdl
run: |
mdl --style docs/mdl-style.rb --rules docs/mdl-custom-rules.rb docs/TEST_DOCUMENTATION.md
14 changes: 14 additions & 0 deletions docs/kramdown.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
require 'kramdown'

# Path to the Markdown file
markdown_file_path = 'docs/TEST_DOCUMENTATION.md'

# Read the Markdown file
markdown_content = File.read(markdown_file_path)
document = Kramdown::Document.new(markdown_content)

document.root.children.each do |element|
if element.type == :header && element.options[:level] == 4
puts element.options[:raw_text]
end
end
107 changes: 107 additions & 0 deletions docs/mdl-custom-rules.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,107 @@
rule "CTS001", "Testsuite documentation header format" do
tags :documentation
check do |doc|
parsed = doc.parsed
violation_lines = []
parsed.root.children.each do |element|
if element.type == :header
text = element.options[:raw_text]
line_number = element.options[:location]
case element.options[:level]
when 2
unless text == "Table of Contents" || text.start_with?("Category:")
violation_lines << line_number
end
when 4
allowed_texts = ["Overview", "Rationale", "Remediation", "Usage"]
unless allowed_texts.include?(text)
violation_lines << line_number
end
end
end
end
violation_lines.empty? ? nil : violation_lines
end
end

rule "CTS002", "All categories and tests are present in TOC" do
tags :documentation
check do |doc|
toc_regex = /## Table of Contents\n\n.*?\n\n## /m
doc_text = doc.lines.join("\n")
toc_text = doc_text.scan(toc_regex).first
parsed = doc.parsed
violation_lines = []

parsed.root.children.each do |element|
if element.type == :header
text = element.options[:raw_text]
line_number = element.options[:location]
case element.options[:level]
when 2
unless toc_text.include?(text)
violation_lines << line_number
end
when 3
unless text == "Usage" || toc_text.include?(text)
violation_lines << line_number
end
end
end
end
violation_lines.empty? ? nil : violation_lines
end
end

rule "CTS003", "Separators before tests and categories are present" do
tags :documentation
check do |doc|
parsed = doc.parsed
violation_lines = []
parsed.root.children.each do |element|
if element.type == :header
text = element.options[:raw_text]
line_number = element.options[:location]
case element.options[:level]
when 2, 3
unless text == "Table of Contents" || text == "Usage"
separator_line_number = line_number - 3
separator_line = doc.lines[separator_line_number.clamp(0, doc.lines.length - 1)]
unless separator_line.strip =~ /---/
violation_lines << line_number
end
end
end
end
end
violation_lines.empty? ? nil : violation_lines
end
end

rule "CTS004", "Tests should have all required sub-sections" do
tags :documentation
check do |doc|
parsed = doc.parsed
violation_lines = []
required_subsections = ["Overview", "Rationale", "Remediation", "Usage"]
current_test_header = nil
found_subsections = []
parsed.root.children.each do |element|
if element.type == :header
if element.options[:level] == 3 && element.options[:raw_text] != "Usage"
unless found_subsections.sort == required_subsections.sort || current_test_header.nil?
violation_lines << current_test_header.options[:location]
end
current_test_header = element
found_subsections = []
elsif element.options[:level] == 4
found_subsections << element.options[:raw_text]
end
end
end
unless found_subsections.sort == required_subsections.sort
violation_lines << current_test_header.options[:location]
end
violation_lines.empty? ? nil : violation_lines
end
end
5 changes: 5 additions & 0 deletions docs/mdl-style.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
all
exclude_rule 'MD013' # Line length
exclude_rule 'MD024' # Multiple headers with the same content
exclude_rule 'MD057' # Table has missing or invalid header separation
exclude_rule 'MD055' # Table row doesn't begin/end with pipes

0 comments on commit 5e6cacb

Please sign in to comment.