diff --git a/.github/workflows/lint-docs.yml b/.github/workflows/lint-docs.yml new file mode 100644 index 000000000..e52724398 --- /dev/null +++ b/.github/workflows/lint-docs.yml @@ -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 \ No newline at end of file diff --git a/docs/kramdown.rb b/docs/kramdown.rb new file mode 100644 index 000000000..bfd279f83 --- /dev/null +++ b/docs/kramdown.rb @@ -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 diff --git a/docs/mdl-custom-rules.rb b/docs/mdl-custom-rules.rb new file mode 100644 index 000000000..46ad4c21c --- /dev/null +++ b/docs/mdl-custom-rules.rb @@ -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 \ 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