51 lines
1.7 KiB
Bash
51 lines
1.7 KiB
Bash
#!/bin/sh
|
|
# Validate repository-relative Markdown links that name a path or file.
|
|
set -eu
|
|
|
|
ROOT=${RULES_ROOT:-$(cd "$(dirname "$0")/.." && pwd)}
|
|
|
|
if ! command -v perl >/dev/null 2>&1; then
|
|
echo "link gate: perl is required" >&2
|
|
exit 1
|
|
fi
|
|
|
|
find "$ROOT" -type f -name '*.md' -not -path '*/.git/*' -not -path '*/.build/*' -print0 \
|
|
| xargs -0 perl -MFile::Basename=dirname -MFile::Spec -e '
|
|
use strict;
|
|
use warnings;
|
|
my @broken;
|
|
|
|
sub check_target {
|
|
my ($file, $target) = @_;
|
|
$target =~ s/\s+"[^"]*"$//;
|
|
$target =~ s/^<|>$//g;
|
|
return if $target eq q{} || $target =~ m{^(?:#|/|[A-Za-z][A-Za-z0-9+.-]*:)};
|
|
$target =~ s/[#?].*$//;
|
|
$target =~ s/%20/ /g;
|
|
my $path = File::Spec->rel2abs($target, dirname($file));
|
|
push @broken, "$file -> $target" unless -e $path;
|
|
}
|
|
|
|
for my $file (@ARGV) {
|
|
open my $handle, "<", $file or die "$file: $!";
|
|
local $/;
|
|
my $text = <$handle>;
|
|
# Examples are not navigable links. Remove fenced and inline code before
|
|
# interpreting Markdown link syntax.
|
|
$text =~ s{^[ \t]*(```+|~~~+).*?^[ \t]*\1[ \t]*$}{}gms;
|
|
$text =~ s{(`+)[^\n]*?\1}{}g;
|
|
while ($text =~ /\[[^\]]*\]\(([^)]+)\)/g) {
|
|
check_target($file, $1);
|
|
}
|
|
while ($text =~ /^\s{0,3}\[[^\]]+\]:\s*(\S+)/gm) {
|
|
check_target($file, $1);
|
|
}
|
|
}
|
|
if (@broken) {
|
|
print "LINK FAIL (missing repository-relative targets):\n", join("\n", @broken), "\n";
|
|
exit 1;
|
|
}
|
|
'
|
|
|
|
echo "link gate clean"
|