|
| 1 | +#!/usr/bin/env bash |
| 2 | +# Verify that code references in documentation match actual source code |
| 3 | +# This script extracts CODE_REFERENCE comments from markdown files and validates |
| 4 | +# that the referenced code still exists at the specified line numbers |
| 5 | + |
| 6 | +set -euo pipefail |
| 7 | + |
| 8 | +# Colors for output |
| 9 | +RED='\033[0;31m' |
| 10 | +GREEN='\033[0;32m' |
| 11 | +YELLOW='\033[1;33m' |
| 12 | +NC='\033[0m' # No Color |
| 13 | + |
| 14 | +REPO_ROOT="$(cd "$(dirname "${BASH_SOURCE[0]}")/../.." && pwd)" |
| 15 | +DOCS_DIR="${REPO_ROOT}/website/docs" |
| 16 | +EXIT_CODE=0 |
| 17 | +TOTAL_REFS=0 |
| 18 | +VALID_REFS=0 |
| 19 | +INVALID_REFS=0 |
| 20 | + |
| 21 | +echo "Verifying code references in documentation..." |
| 22 | +echo "Repository root: ${REPO_ROOT}" |
| 23 | +echo "" |
| 24 | + |
| 25 | +# Find all markdown files with CODE_REFERENCE comments |
| 26 | +while IFS= read -r doc_file; do |
| 27 | + # Extract CODE_REFERENCE comments from this file |
| 28 | + while IFS= read -r line_num; do |
| 29 | + # Get the actual line content |
| 30 | + comment_line=$(sed -n "${line_num}p" "$doc_file") |
| 31 | + |
| 32 | + # Extract file path and line range from comment |
| 33 | + # Format: <!-- CODE_REFERENCE: path/to/file.rs#L123-L456 --> |
| 34 | + if [[ $comment_line =~ CODE_REFERENCE:\ *([^#]+)#L([0-9]+)-L([0-9]+) ]]; then |
| 35 | + file_path="${BASH_REMATCH[1]}" |
| 36 | + start_line="${BASH_REMATCH[2]}" |
| 37 | + end_line="${BASH_REMATCH[3]}" |
| 38 | + |
| 39 | + # Trim whitespace from file_path |
| 40 | + file_path=$(echo "$file_path" | xargs) |
| 41 | + |
| 42 | + TOTAL_REFS=$((TOTAL_REFS + 1)) |
| 43 | + |
| 44 | + # Check if the source file exists |
| 45 | + source_file="${REPO_ROOT}/${file_path}" |
| 46 | + if [[ ! -f "$source_file" ]]; then |
| 47 | + echo -e "${RED}✗${NC} Invalid reference in ${doc_file}:${line_num}" |
| 48 | + echo " File not found: ${file_path}" |
| 49 | + INVALID_REFS=$((INVALID_REFS + 1)) |
| 50 | + EXIT_CODE=1 |
| 51 | + continue |
| 52 | + fi |
| 53 | + |
| 54 | + # Check if the line range is valid |
| 55 | + total_lines=$(wc -l < "$source_file") |
| 56 | + if [[ $end_line -gt $total_lines ]]; then |
| 57 | + echo -e "${RED}✗${NC} Invalid reference in ${doc_file}:${line_num}" |
| 58 | + echo " Line range L${start_line}-L${end_line} exceeds file length (${total_lines} lines)" |
| 59 | + echo " File: ${file_path}" |
| 60 | + INVALID_REFS=$((INVALID_REFS + 1)) |
| 61 | + EXIT_CODE=1 |
| 62 | + continue |
| 63 | + fi |
| 64 | + |
| 65 | + # Extract the actual code from source file |
| 66 | + actual_code=$(sed -n "${start_line},${end_line}p" "$source_file") |
| 67 | + |
| 68 | + # Find the corresponding code block in the markdown (should be right after the comment) |
| 69 | + # Look for ```rust reference block within next 5 lines |
| 70 | + code_block_start=$((line_num + 1)) |
| 71 | + code_block_end=$((line_num + 10)) |
| 72 | + |
| 73 | + # Extract GitHub URL from the reference block |
| 74 | + github_url=$(sed -n "${code_block_start},${code_block_end}p" "$doc_file" | grep "github.com" | head -1) |
| 75 | + |
| 76 | + if [[ -n "${github_url}" ]]; then |
| 77 | + # Verify the GitHub URL contains correct line range |
| 78 | + line_range_pattern="#L${start_line}-L${end_line}" |
| 79 | + if [[ "${github_url}" =~ ${line_range_pattern} ]]; then |
| 80 | + # Extract GitHub raw URL from the reference |
| 81 | + # Convert: https://github.com/o1-labs/mina-rust/blob/develop/path/to/file.rs#L10-L20 |
| 82 | + # To: https://raw.githubusercontent.com/o1-labs/mina-rust/develop/path/to/file.rs |
| 83 | + if [[ "${github_url}" =~ github\.com/([^/]+)/([^/]+)/blob/([^/]+)/([^#]+) ]]; then |
| 84 | + org="${BASH_REMATCH[1]}" |
| 85 | + repo="${BASH_REMATCH[2]}" |
| 86 | + branch="${BASH_REMATCH[3]}" |
| 87 | + gh_file_path="${BASH_REMATCH[4]}" |
| 88 | + |
| 89 | + raw_url="https://raw.githubusercontent.com/${org}/${repo}/${branch}/${gh_file_path}" |
| 90 | + |
| 91 | + # Fetch the code from GitHub |
| 92 | + github_code=$(curl -s "${raw_url}" | sed -n "${start_line},${end_line}p") |
| 93 | + |
| 94 | + # Compare local code with GitHub code |
| 95 | + if [[ "${actual_code}" == "${github_code}" ]]; then |
| 96 | + echo -e "${GREEN}✓${NC} Valid reference in ${doc_file}:${line_num}" |
| 97 | + echo " ${file_path}#L${start_line}-L${end_line}" |
| 98 | + echo " Local code matches GitHub (${branch})" |
| 99 | + VALID_REFS=$((VALID_REFS + 1)) |
| 100 | + else |
| 101 | + echo -e "${RED}✗${NC} Code mismatch in ${doc_file}:${line_num}" |
| 102 | + echo " ${file_path}#L${start_line}-L${end_line}" |
| 103 | + echo " Local code differs from GitHub (${branch})" |
| 104 | + echo " This may indicate uncommitted changes or branch divergence" |
| 105 | + INVALID_REFS=$((INVALID_REFS + 1)) |
| 106 | + EXIT_CODE=1 |
| 107 | + fi |
| 108 | + else |
| 109 | + echo -e "${YELLOW}⚠${NC} Could not parse GitHub URL in ${doc_file}:${line_num}" |
| 110 | + echo " URL: ${github_url}" |
| 111 | + INVALID_REFS=$((INVALID_REFS + 1)) |
| 112 | + EXIT_CODE=1 |
| 113 | + fi |
| 114 | + else |
| 115 | + echo -e "${YELLOW}⚠${NC} Mismatched line range in ${doc_file}:${line_num}" |
| 116 | + echo " CODE_REFERENCE comment specifies: L${start_line}-L${end_line}" |
| 117 | + echo " But GitHub URL has different line range" |
| 118 | + INVALID_REFS=$((INVALID_REFS + 1)) |
| 119 | + EXIT_CODE=1 |
| 120 | + fi |
| 121 | + else |
| 122 | + echo -e "${YELLOW}⚠${NC} No GitHub URL found for reference in ${doc_file}:${line_num}" |
| 123 | + echo " Expected rust reference block with GitHub URL" |
| 124 | + INVALID_REFS=$((INVALID_REFS + 1)) |
| 125 | + EXIT_CODE=1 |
| 126 | + fi |
| 127 | + fi |
| 128 | + done < <(grep -n "CODE_REFERENCE:" "$doc_file" | cut -d: -f1) |
| 129 | +done < <(find "$DOCS_DIR" -name "*.md" -o -name "*.mdx") |
| 130 | + |
| 131 | +echo "" |
| 132 | +echo "================================" |
| 133 | +echo "Code Reference Verification Summary" |
| 134 | +echo "================================" |
| 135 | +echo -e "Total references checked: ${TOTAL_REFS}" |
| 136 | +echo -e "${GREEN}Valid references: ${VALID_REFS}${NC}" |
| 137 | +if [[ $INVALID_REFS -gt 0 ]]; then |
| 138 | + echo -e "${RED}Invalid references: ${INVALID_REFS}${NC}" |
| 139 | +else |
| 140 | + echo -e "${GREEN}Invalid references: ${INVALID_REFS}${NC}" |
| 141 | +fi |
| 142 | +echo "" |
| 143 | + |
| 144 | +if [[ $EXIT_CODE -eq 0 ]]; then |
| 145 | + echo -e "${GREEN}✓ All code references are valid!${NC}" |
| 146 | +else |
| 147 | + echo -e "${RED}✗ Some code references are invalid. Please update the documentation.${NC}" |
| 148 | +fi |
| 149 | + |
| 150 | +exit $EXIT_CODE |
0 commit comments