From 954cf313a36df950eaf1e02486c5e3d5c7449276 Mon Sep 17 00:00:00 2001 From: Ivan Valdes Date: Tue, 6 Aug 2024 14:40:28 -0700 Subject: [PATCH] Add Markdown diff linter Add Makefile target, and scripts to lint only the modified Markdown files, failing only if the violation is within the changed lines. Signed-off-by: Ivan Valdes --- .markdownlint-cli2.yaml | 2 + Makefile | 4 ++ scripts/markdown_diff_lint.sh | 98 +++++++++++++++++++++++++++++++++++ 3 files changed, 104 insertions(+) create mode 100644 .markdownlint-cli2.yaml create mode 100755 scripts/markdown_diff_lint.sh diff --git a/.markdownlint-cli2.yaml b/.markdownlint-cli2.yaml new file mode 100644 index 00000000..3e434e3d --- /dev/null +++ b/.markdownlint-cli2.yaml @@ -0,0 +1,2 @@ +config: + line-length: false # Allow lines longer than 80 characters. diff --git a/Makefile b/Makefile index 6725ce58..cd6d563d 100644 --- a/Makefile +++ b/Makefile @@ -27,3 +27,7 @@ ifndef LATEST_VERSION else ./scripts/update_release_version.sh "$(LATEST_VERSION)" endif + +.PHONY: markdown-diff-lint +markdown-diff-lint: + ./scripts/markdown_diff_lint.sh diff --git a/scripts/markdown_diff_lint.sh b/scripts/markdown_diff_lint.sh new file mode 100755 index 00000000..600782b8 --- /dev/null +++ b/scripts/markdown_diff_lint.sh @@ -0,0 +1,98 @@ +#!/usr/bin/env bash +# This script runs markdownlint-cli2 on changed files. +# Usage: ./markdown_lint.sh + +set -eo pipefail + +if ! command markdownlint-cli2 dummy.md &>/dev/null; then + echo "markdownlint-cli2 needs to be installed." + echo "Please refer to https://github.com/DavidAnson/markdownlint-cli2?tab=readme-ov-file#install for installation instructions." + exit 1 +fi + +COLOR_RED='\033[0;31m' +COLOR_BOLD='\033[1m' +COLOR_ORANGE='\033[0;33m' +COLOR_NONE='\033[0m' # No Color + +function log_error { + echo -n -e "${COLOR_BOLD}${COLOR_RED}$*${COLOR_NONE}\n" +} + +function log_warning { + echo -n -e "${COLOR_ORANGE}$*${COLOR_NONE}\n" +} + +if [ -z "${PULL_BASE_SHA}" ]; then + echo "Empty base reference (\$PULL_BASE_SHA), assuming: main" + PULL_BASE_SHA=main +fi + +if [ -z "${PULL_PULL_SHA}" ]; then + PULL_PULL_SHA="$(git rev-parse HEAD)" + echo "Empty pull reference (\$PULL_PULL_SHA), assuming: ${PULL_PULL_SHA}" +fi + +MD_LINT_URL_PREFIX="https://github.com/DavidAnson/markdownlint/blob/main/doc/" + +mapfile -t changed_files < <(git diff "${PULL_BASE_SHA}" --name-only) +declare -A files_with_failures start_ranges end_ranges + +for file in "${changed_files[@]}"; do + if ! [[ "$file" =~ .md$ ]]; then + continue + fi + + # Find start and end ranges from changed files. + start_ranges=() + end_ranges=() + # From https://github.com/paleite/eslint-plugin-diff/blob/46c5bcf296e9928db19333288457bf2805aad3b9/src/git.ts#L8-L27 + ranges=$(git diff "${PULL_BASE_SHA}" \ + --diff-algorithm=histogram \ + --diff-filter=ACM \ + --find-renames=100% \ + --no-ext-diff \ + --relative \ + --unified=0 -- "${file}" | \ + awk 'match($0, /^@@\s-[0-9,]+\s\+([0-9]+)(,([0-9]+))?/, m) { \ + print m[1] ":" m[1] + ((m[3] == "") ? "0" : m[3]) }') + i=0 + for range in ${ranges}; do + start_ranges["${i}"]=$(echo "${range}" | awk -F: '{print $1}') + end_ranges["${i}"]=$(echo "${range}" | awk -F: '{print $2}') + i=$((1 + i)) + done + if [ -z "${ranges}" ]; then + start_ranges[0]=0 + end_ranges[0]=0 + fi + + i=0 + markdownlint-cli2 "${file}" 2>/dev/null || true + while IFS= read -r line; do + line_number=$(echo "${line}" | awk -F: '{print $2}' | awk '{print $1}') + while [ "${i}" -lt "${#end_ranges[@]}" ] && [ "${line_number}" -gt "${end_ranges["${i}"]}" ]; do + i=$((1 + i)) + done + rule=$(echo "${line}" | awk 'match($2, /([^\/]+)/, m) {print tolower(m[1])}') + lint_error="${line} (${MD_LINT_URL_PREFIX}${rule}.md)" + + if [ "${i}" -lt "${#start_ranges[@]}" ] && [ "${line_number}" -ge "${start_ranges["${i}"]}" ] && [ "${line_number}" -le "${end_ranges["${i}"]}" ]; then + # Inside range with changes, raise an error. + log_error "${lint_error}" + files_with_failures["${file}"]=1 + else + # Outside of range, raise a warning. + log_warning "${lint_error}" + fi + done < <(markdownlint-cli2 "${file}" 2>&1 >/dev/null || true) +done + +echo "Finished linting" + +for file in "${!files_with_failures[@]}"; do + log_error "${file} has linting issues" +done +if [ "${#files_with_failures[@]}" -gt "0" ]; then + exit 1 +fi