-
Notifications
You must be signed in to change notification settings - Fork 72
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
docs: Add linter for the documentation (#2089)
Add mdl as a linter for new cnf-testsuite documentation Add custom rules for keeping format of the documentation Add new git workflow with job lint to run mdl. Signed-off-by: Konstantin Yarovoy <[email protected]>
- Loading branch information
1 parent
2174f35
commit 8ef99b9
Showing
7 changed files
with
218 additions
and
11 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,23 @@ | ||
name: Test documentation lint | ||
on: | ||
push: | ||
paths: | ||
- 'docs/*' | ||
- 'doc-lint/*' | ||
pull_request: | ||
paths: | ||
- 'docs/*' | ||
- 'doc-lint/*' | ||
jobs: | ||
lint-docs: | ||
name: Lint Docs | ||
runs-on: ubuntu-latest | ||
container: | ||
image: ruby:latest | ||
steps: | ||
- name: Checkout code | ||
uses: actions/checkout@v3 | ||
- name: Install mdl | ||
run: gem install mdl | ||
- name: Run mdl | ||
run: mdl -s doc-lint/mdl-style.rb -u doc-lint/mdl-TTDS-rules.rb docs/TEST_DOCUMENTATION.md |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,4 +1,3 @@ | ||
/docs/ | ||
/lib/ | ||
/utils/tar/lib/ | ||
/utils/tar/bin/ | ||
|
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,5 @@ | ||
# Steps to run lint on TEST_DOCUMENTATION locally | ||
|
||
- Install ruby version that suits your system https://www.ruby-lang.org/en/documentation/installation/ | ||
- Install MDL with gem package manager `gem install mdl` or build it from source https://github.com/markdownlint/markdownlint | ||
- Run `mdl -s doc-lint/mdl-style.rb -u doc-lint/mdl-TTDS-rules.rb docs/TEST_DOCUMENTATION.md`, that should run Markdownlint with custom style and rules. |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,171 @@ | ||
# TTDS stands for Testsuite Test Documentation Style | ||
rule "TTDS001", "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 "TTDS002", "All categories and tests are present in TOC" do | ||
tags :documentation | ||
check do |doc| | ||
toc_regex = /## Table of Contents\n\n[\s\S]*?\n## /m | ||
doc_text = doc.lines.join("\n") | ||
toc_text = doc_text.scan(toc_regex).first | ||
parsed = doc.parsed | ||
violation_lines = [] | ||
if toc_text.nil? | ||
puts "Table of Contents not found" | ||
violation_lines << 1 | ||
end | ||
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 "TTDS003", "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 "TTDS004", "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 | ||
|
||
rule "TTDS005", "TOC should not contain non-existent tests" do | ||
tags :documentation | ||
check do |doc| | ||
toc_regex = /## Table of Contents\n\n[\s\S]*?\n## /m | ||
doc_text = doc.lines.join("\n") | ||
toc_text = doc_text.scan(toc_regex).first | ||
parsed = doc.parsed | ||
violation_lines = [] | ||
test_header_names = [] | ||
if toc_text.nil? | ||
puts "Table of Contents not found" | ||
violation_lines << 1 | ||
end | ||
parsed.root.children.each do |element| | ||
if element.type == :header | ||
text = element.options[:raw_text] | ||
line_number = element.options[:location] | ||
if element.options[:level] == 3 && text != "Usage" | ||
test_header_names << text.strip | ||
end | ||
end | ||
end | ||
toc_header_names = toc_text.scan(/\[\[([^\]]*)\]\]/).flatten | ||
toc_header_names.each do |toc_header_name| | ||
unless test_header_names.include?(toc_header_name) | ||
violation_lines << 1 | ||
end | ||
end | ||
violation_lines.empty? ? nil : violation_lines | ||
end | ||
end | ||
|
||
rule "TTDS006", "TOC should not contain 'dead' local links" do | ||
tags :documentation | ||
check do |doc| | ||
parsed = doc.parsed | ||
violation_lines = [] | ||
headers = Set[] | ||
parsed.root.children.each do |element| | ||
if element.type == :header | ||
headers.add(element.options[:raw_text].downcase.delete('^a-z0-9\- ').tr(' ', '-')) | ||
end | ||
end | ||
link_regex = /\[.*?\]\(#(.*?)\)/ | ||
doc.lines.each_with_index do |line, line_number| | ||
match = line.scan(link_regex).flatten | ||
if match | ||
match.each do |link| | ||
unless headers.include?(link) | ||
violation_lines << line_number + 1 | ||
puts "Dead link: #{link}" | ||
end | ||
end | ||
|
||
end | ||
end | ||
violation_lines.empty? ? nil : violation_lines | ||
end | ||
end |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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 |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters