#!/usr/bin/env bash # ============================================================================ # Example Validator # ============================================================================ # Purpose: Validate example quality and detect placeholder patterns # Version: 1.0.0 # Usage: ./example-validator.sh [--no-placeholders] [--recursive] [--json] # Returns: 0=success, 1=warning, JSON output to stdout if --json # ============================================================================ set -euo pipefail # Default values NO_PLACEHOLDERS=true RECURSIVE=true JSON_OUTPUT=false EXTENSIONS="md,txt,json,sh,py,js,ts,yaml,yml" # Parse arguments TARGET_PATH="${1:-.}" shift || true while [[ $# -gt 0 ]]; do case "$1" in --no-placeholders) NO_PLACEHOLDERS=true shift ;; --allow-placeholders) NO_PLACEHOLDERS=false shift ;; --recursive) RECURSIVE=true shift ;; --non-recursive) RECURSIVE=false shift ;; --json) JSON_OUTPUT=true shift ;; --extensions) EXTENSIONS="$2" shift 2 ;; *) shift ;; esac done # Initialize counters files_checked=0 example_count=0 placeholder_count=0 todo_count=0 declare -a issues=() declare -a files_with_issues=() # Build find command based on recursiveness if $RECURSIVE; then FIND_DEPTH="" else FIND_DEPTH="-maxdepth 1" fi # Build extension pattern ext_pattern="" IFS=',' read -ra EXT_ARRAY <<< "$EXTENSIONS" for ext in "${EXT_ARRAY[@]}"; do if [[ -z "$ext_pattern" ]]; then ext_pattern="-name '*.${ext}'" else ext_pattern="$ext_pattern -o -name '*.${ext}'" fi done # Find files to check mapfile -t files < <(eval "find '$TARGET_PATH' $FIND_DEPTH -type f \( $ext_pattern \) 2>/dev/null" || true) # Placeholder patterns to detect declare -a PLACEHOLDER_PATTERNS=( 'TODO[:\)]' 'FIXME[:\)]' 'XXX[:\)]' 'HACK[:\)]' 'placeholder' 'PLACEHOLDER' 'your-.*-here' '/dev/null || echo "0") # Ensure count is numeric and divide by 2 since each code block has opening and closing if [[ "$count" =~ ^[0-9]+$ ]]; then count=$((count / 2)) else count=0 fi echo "$count" } # Check each file for file in "${files[@]}"; do ((files_checked++)) || true # Count examples in markdown files if [[ "$file" =~ \.md$ ]]; then file_examples=$(count_code_examples "$file") ((example_count += file_examples)) || true fi file_issues=0 # Check for placeholder patterns for pattern in "${PLACEHOLDER_PATTERNS[@]}"; do while IFS=: read -r line_num line_content; do # Skip if it's an acceptable pattern if is_acceptable_pattern "$line_content"; then continue fi ((placeholder_count++)) || true ((file_issues++)) || true issue="$file:$line_num: Placeholder pattern detected" issues+=("$issue") # Track TODO/FIXME separately if echo "$pattern" | grep -qE 'TODO|FIXME|XXX'; then ((todo_count++)) || true fi done < <(grep -inE "$pattern" "$file" 2>/dev/null || true) done # Check for generic dummy values (only in non-test files) if [[ ! "$file" =~ test ]] && [[ ! "$file" =~ example ]] && [[ ! "$file" =~ spec ]]; then for pattern in "${GENERIC_PATTERNS[@]}"; do while IFS=: read -r line_num line_content; do # Skip code comments explaining these terms if echo "$line_content" | grep -qE '(#|//|/\*).*'"$pattern"; then continue fi # Skip if in an acceptable context if is_acceptable_pattern "$line_content"; then continue fi ((file_issues++)) || true issue="$file:$line_num: Generic placeholder value detected" issues+=("$issue") done < <(grep -inE "$pattern" "$file" 2>/dev/null || true) done fi # Track files with issues if ((file_issues > 0)); then files_with_issues+=("$file:$file_issues") fi done # Calculate quality score quality_score=100 ((quality_score -= placeholder_count * 10)) || true ((quality_score -= todo_count * 5)) || true if ((example_count < 2)); then ((quality_score -= 20)) || true fi # Ensure score doesn't go negative if ((quality_score < 0)); then quality_score=0 fi # Determine status status="pass" if ((quality_score < 60)); then status="fail" elif ((quality_score < 80)); then status="warning" fi # Output results if $JSON_OUTPUT; then # Build JSON output cat < 0)) || ((todo_count > 0)); then echo "Issues Detected:" echo " • Placeholder patterns: $placeholder_count" echo " • TODO/FIXME markers: $todo_count" echo " • Files with issues: ${#files_with_issues[@]}" echo "" if ((${#files_with_issues[@]} > 0)); then echo "Files with issues:" for file_info in "${files_with_issues[@]:0:5}"; do # Show first 5 file_path="${file_info%:*}" file_count="${file_info#*:}" echo " • $file_path ($file_count issues)" done if ((${#files_with_issues[@]} > 5)); then echo " ... and $((${#files_with_issues[@]} - 5)) more files" fi fi echo "" echo "Sample Issues:" for issue in "${issues[@]:0:5}"; do # Show first 5 echo " • $issue" done if ((${#issues[@]} > 5)); then echo " ... and $((${#issues[@]} - 5)) more issues" fi else echo "✓ No placeholder patterns detected" fi if ((example_count < 2)); then echo "" echo "⚠ Recommendation: Add more code examples (found: $example_count, recommended: 3+)" fi echo "" if [[ "$status" == "pass" ]]; then echo "Overall: ✓ PASS" elif [[ "$status" == "warning" ]]; then echo "Overall: ⚠ WARNINGS" else echo "Overall: ✗ FAIL" fi echo "" fi # Exit with appropriate code if [[ "$status" == "fail" ]]; then exit 1 elif [[ "$status" == "warning" ]]; then exit 0 # Warning is not a failure else exit 0 fi