ci: unify rust quality gate and add incremental docs/link checks
This commit is contained in:
parent
8a6273b988
commit
6528613c8d
12 changed files with 514 additions and 47 deletions
181
scripts/ci/docs_quality_gate.sh
Executable file
181
scripts/ci/docs_quality_gate.sh
Executable file
|
|
@ -0,0 +1,181 @@
|
|||
#!/usr/bin/env bash
|
||||
|
||||
set -euo pipefail
|
||||
|
||||
BASE_SHA="${BASE_SHA:-}"
|
||||
DOCS_FILES_RAW="${DOCS_FILES:-}"
|
||||
|
||||
if [ -z "$BASE_SHA" ] && git rev-parse --verify origin/main >/dev/null 2>&1; then
|
||||
BASE_SHA="$(git merge-base origin/main HEAD)"
|
||||
fi
|
||||
|
||||
if [ -z "$DOCS_FILES_RAW" ] && [ -n "$BASE_SHA" ] && git cat-file -e "$BASE_SHA^{commit}" 2>/dev/null; then
|
||||
DOCS_FILES_RAW="$(git diff --name-only "$BASE_SHA" HEAD | awk '
|
||||
/\.md$/ || /\.mdx$/ || $0 == "LICENSE" || $0 == ".github/pull_request_template.md" {
|
||||
print
|
||||
}
|
||||
')"
|
||||
fi
|
||||
|
||||
if [ -z "$DOCS_FILES_RAW" ]; then
|
||||
echo "No docs files detected; skipping docs quality gate."
|
||||
exit 0
|
||||
fi
|
||||
|
||||
if [ -z "$BASE_SHA" ] || ! git cat-file -e "$BASE_SHA^{commit}" 2>/dev/null; then
|
||||
echo "BASE_SHA is missing or invalid; falling back to full-file markdown lint."
|
||||
BASE_SHA=""
|
||||
fi
|
||||
|
||||
ALL_FILES=()
|
||||
while IFS= read -r file; do
|
||||
if [ -n "$file" ]; then
|
||||
ALL_FILES+=("$file")
|
||||
fi
|
||||
done < <(printf '%s\n' "$DOCS_FILES_RAW")
|
||||
|
||||
if [ "${#ALL_FILES[@]}" -eq 0 ]; then
|
||||
echo "No docs files detected after normalization; skipping docs quality gate."
|
||||
exit 0
|
||||
fi
|
||||
|
||||
EXISTING_FILES=()
|
||||
for file in "${ALL_FILES[@]}"; do
|
||||
if [ -f "$file" ]; then
|
||||
EXISTING_FILES+=("$file")
|
||||
fi
|
||||
done
|
||||
|
||||
if [ "${#EXISTING_FILES[@]}" -eq 0 ]; then
|
||||
echo "No existing docs files to lint; skipping docs quality gate."
|
||||
exit 0
|
||||
fi
|
||||
|
||||
if command -v npx >/dev/null 2>&1; then
|
||||
MD_CMD=(npx --yes markdownlint-cli2@0.20.0)
|
||||
elif command -v markdownlint-cli2 >/dev/null 2>&1; then
|
||||
MD_CMD=(markdownlint-cli2)
|
||||
else
|
||||
echo "markdownlint-cli2 is required (via npx or local binary)."
|
||||
exit 1
|
||||
fi
|
||||
|
||||
echo "Linting docs files: ${EXISTING_FILES[*]}"
|
||||
|
||||
LINT_OUTPUT_FILE="$(mktemp)"
|
||||
set +e
|
||||
"${MD_CMD[@]}" "${EXISTING_FILES[@]}" >"$LINT_OUTPUT_FILE" 2>&1
|
||||
LINT_EXIT=$?
|
||||
set -e
|
||||
|
||||
if [ "$LINT_EXIT" -eq 0 ]; then
|
||||
cat "$LINT_OUTPUT_FILE"
|
||||
rm -f "$LINT_OUTPUT_FILE"
|
||||
exit 0
|
||||
fi
|
||||
|
||||
if [ -z "$BASE_SHA" ]; then
|
||||
cat "$LINT_OUTPUT_FILE"
|
||||
rm -f "$LINT_OUTPUT_FILE"
|
||||
exit "$LINT_EXIT"
|
||||
fi
|
||||
|
||||
CHANGED_LINES_JSON_FILE="$(mktemp)"
|
||||
python3 - "$BASE_SHA" "${EXISTING_FILES[@]}" >"$CHANGED_LINES_JSON_FILE" <<'PY'
|
||||
import json
|
||||
import re
|
||||
import subprocess
|
||||
import sys
|
||||
|
||||
base = sys.argv[1]
|
||||
files = sys.argv[2:]
|
||||
|
||||
changed = {}
|
||||
hunk = re.compile(r"^@@ -\d+(?:,\d+)? \+(\d+)(?:,(\d+))? @@")
|
||||
|
||||
for path in files:
|
||||
proc = subprocess.run(
|
||||
["git", "diff", "--unified=0", base, "HEAD", "--", path],
|
||||
check=False,
|
||||
capture_output=True,
|
||||
text=True,
|
||||
)
|
||||
ranges = []
|
||||
for line in proc.stdout.splitlines():
|
||||
m = hunk.match(line)
|
||||
if not m:
|
||||
continue
|
||||
start = int(m.group(1))
|
||||
count = int(m.group(2) or "1")
|
||||
if count > 0:
|
||||
ranges.append([start, start + count - 1])
|
||||
changed[path] = ranges
|
||||
|
||||
print(json.dumps(changed))
|
||||
PY
|
||||
|
||||
FILTERED_OUTPUT_FILE="$(mktemp)"
|
||||
set +e
|
||||
python3 - "$LINT_OUTPUT_FILE" "$CHANGED_LINES_JSON_FILE" >"$FILTERED_OUTPUT_FILE" <<'PY'
|
||||
import json
|
||||
import re
|
||||
import sys
|
||||
|
||||
lint_file = sys.argv[1]
|
||||
changed_file = sys.argv[2]
|
||||
|
||||
with open(changed_file, "r", encoding="utf-8") as f:
|
||||
changed = json.load(f)
|
||||
|
||||
line_re = re.compile(r"^(.+?):(\d+)\s+error\s+(MD\d+(?:/[^\s]+)?)\s+(.*)$")
|
||||
|
||||
blocking = []
|
||||
baseline = []
|
||||
other_lines = []
|
||||
|
||||
with open(lint_file, "r", encoding="utf-8") as f:
|
||||
for raw_line in f:
|
||||
line = raw_line.rstrip("\n")
|
||||
m = line_re.match(line)
|
||||
if not m:
|
||||
other_lines.append(line)
|
||||
continue
|
||||
|
||||
path, line_no_s, rule, msg = m.groups()
|
||||
line_no = int(line_no_s)
|
||||
ranges = changed.get(path, [])
|
||||
|
||||
is_changed_line = any(start <= line_no <= end for start, end in ranges)
|
||||
entry = f"{path}:{line_no} {rule} {msg}"
|
||||
if is_changed_line:
|
||||
blocking.append(entry)
|
||||
else:
|
||||
baseline.append(entry)
|
||||
|
||||
if baseline:
|
||||
print("Existing markdown issues outside changed lines (non-blocking):")
|
||||
for entry in baseline:
|
||||
print(f" - {entry}")
|
||||
|
||||
if blocking:
|
||||
print("Markdown issues introduced on changed lines (blocking):")
|
||||
for entry in blocking:
|
||||
print(f" - {entry}")
|
||||
print(f"Blocking markdown issues: {len(blocking)}")
|
||||
sys.exit(1)
|
||||
|
||||
if baseline:
|
||||
print("No blocking markdown issues on changed lines.")
|
||||
sys.exit(0)
|
||||
|
||||
for line in other_lines:
|
||||
print(line)
|
||||
print("No blocking markdown issues on changed lines.")
|
||||
PY
|
||||
SCRIPT_EXIT=$?
|
||||
set -e
|
||||
|
||||
cat "$FILTERED_OUTPUT_FILE"
|
||||
|
||||
rm -f "$LINT_OUTPUT_FILE" "$CHANGED_LINES_JSON_FILE" "$FILTERED_OUTPUT_FILE"
|
||||
exit "$SCRIPT_EXIT"
|
||||
Loading…
Add table
Add a link
Reference in a new issue