check-links.sh 1.7 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768
  1. #!/bin/bash
  2. # Find all internal, cross-page markdown links and checks if the page exists.
  3. # Must be run at the root of the site.
  4. set -eu -o pipefail
  5. ignored_web_urls="
  6. /
  7. "
  8. function print_error {
  9. src_file="$1"
  10. web_url="$2"
  11. target_file="$3"
  12. msg="$4"
  13. echo
  14. echo "Error: $msg"
  15. echo "Source file: $src_file"
  16. echo "Web URL: $web_url"
  17. echo "Target file: $target_file"
  18. }
  19. # Checks if real file for MD-link input exists.
  20. function check_link {
  21. src_file="$1"
  22. md_link="$2"
  23. name=$(grep -Po '(?<=^\[)[^\[\]]*(?=\])' <<<$md_link || true)
  24. web_url=$(grep -Po '(?<=\()[^\(\)]*(?=\)$)' <<<$md_link || true)
  25. target_file=$(sed 's|/$|.md|' <<<$web_url)
  26. # Ignore external (fully-qualified) URLs.
  27. if grep -P '^https?://' <<<$web_url >/dev/null; then
  28. return
  29. fi
  30. # Ignore if not ending with "/".
  31. if ! grep -P '/$' <<<$web_url >/dev/null; then
  32. return
  33. fi
  34. # Ignore special targets.
  35. if grep -Fx "$web_url" <<<$ignored_web_urls >/dev/null; then
  36. return
  37. fi
  38. # Show error if using relative path.
  39. if grep -P '/$' <<<$target_file >/dev/null; then
  40. print_error "$src_file" "$web_url" "$target_file" "Relative paths not allowed."
  41. return
  42. fi
  43. # Show error if file does not exist.
  44. if [[ ! -f $target_file ]]; then
  45. print_error "$src_file" "$web_url" "$target_file" "Target file does not exist."
  46. return
  47. fi
  48. }
  49. # Find all markdown pages and check.
  50. for file in $(find "." -type f -name '*.md' | sed 's|^\./||' | LC_ALL=C sort -t. -k1,1); do
  51. # Extract MD-links and check them.
  52. md_link_regex='\[[^\[\]]*\]\([^\(\)]*\)'
  53. { grep -Po "$md_link_regex" "$file" || true; } | while read -r md_link; do
  54. check_link "$file" "$md_link"
  55. done
  56. done