From b7a499afbc81a550bc7f305441b36e7be45a4dab Mon Sep 17 00:00:00 2001 From: Konstantin Yarovoy Date: Fri, 19 Apr 2024 07:58:11 +0000 Subject: [PATCH] docs: Add linter for the documentation 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 --- .github/workflows/actions.yml | 13 ++++ docs/mdl-TTDS-rules.rb | 111 ++++++++++++++++++++++++++++++++++ docs/mdl-style.rb | 5 ++ 3 files changed, 129 insertions(+) create mode 100644 docs/mdl-TTDS-rules.rb create mode 100644 docs/mdl-style.rb diff --git a/.github/workflows/actions.yml b/.github/workflows/actions.yml index 3806b72e4..115c20309 100644 --- a/.github/workflows/actions.yml +++ b/.github/workflows/actions.yml @@ -9,6 +9,19 @@ on: - '**' - '!**.md' 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 docs/mdl-style.rb -u docs/mdl-custom-rules.rb docs/TEST_DOCUMENTATION.md + tests: name: Fetch Matrix Tests runs-on: [ubuntu-latest] diff --git a/docs/mdl-TTDS-rules.rb b/docs/mdl-TTDS-rules.rb new file mode 100644 index 000000000..6bf4fe322 --- /dev/null +++ b/docs/mdl-TTDS-rules.rb @@ -0,0 +1,111 @@ +# 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 \ No newline at end of file diff --git a/docs/mdl-style.rb b/docs/mdl-style.rb new file mode 100644 index 000000000..a8b8f3373 --- /dev/null +++ b/docs/mdl-style.rb @@ -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 \ No newline at end of file