ci: validate "linkgit:" in documentation

It is easy to add incorrect "linkgit:<page>[<section>]" references to our documentation suite. Catch these common classes of errors: * Referring to Documentation/<page>.txt that does not exist. * Referring to a <page> outside the Git suite. In general, <page> must begin with "git". * Listing the manual <section> incorrectly. The first line of the Documentation/<page>.txt must end with "(<section>)". with a new script "ci/lint-gitlink", and drive it from "make check-docs". Signed-off-by: Junio C Hamano <gitster@pobox.com>

Junio C Hamano committed May 4, 2016 at 14:34 UTC ab81411cede9e5fe52b416c4df835e19f1048426
3 files changed +76
Documentation/Makefile
+4
@@ -204,6 +204,7 @@ ifndef V
204 QUIET_DBLATEX = @echo ' ' DBLATEX $@;
205 QUIET_XSLTPROC = @echo ' ' XSLTPROC $@;
206 QUIET_GEN = @echo ' ' GEN $@;
207 + QUIET_LINT = @echo ' ' LINT $@;
208 QUIET_STDERR = 2> /dev/null
209 QUIET_SUBDIR0 = +@subdir=
210 QUIET_SUBDIR1 = ;$(NO_SUBDIR) echo ' ' SUBDIR $$subdir; \
@@ -427,4 +428,7 @@ quick-install-html: require-htmlrepo
428 print-man1:
429 @for i in $(MAN1_TXT); do echo $$i; done
430
431 +lint-docs::
432 + $(QUIET_LINT)$(PERL_PATH) lint-gitlink.perl
433 +
434 .PHONY: FORCE
Documentation/lint-gitlink.perl new
+71
@@ -0,0 +1,71 @@
1 +#!/usr/bin/perl
2 +
3 +use File::Find;
4 +use Getopt::Long;
5 +
6 +my $basedir = ".";
7 +GetOptions("basedir=s" => \$basedir)
8 + or die("Cannot parse command line arguments\n");
9 +
10 +my $found_errors = 0;
11 +
12 +sub report {
13 + my ($where, $what, $error) = @_;
14 + print "$where: $error: $what\n";
15 + $found_errors = 1;
16 +}
17 +
18 +sub grab_section {
19 + my ($page) = @_;
20 + open my $fh, "<", "$basedir/$page.txt";
21 + my $firstline = <$fh>;
22 + chomp $firstline;
23 + close $fh;
24 + my ($section) = ($firstline =~ /.*\((\d)\)$/);
25 + return $section;
26 +}
27 +
28 +sub lint {
29 + my ($file) = @_;
30 + open my $fh, "<", $file
31 + or return;
32 + while (<$fh>) {
33 + my $where = "$file:$.";
34 + while (s/linkgit:((.*?)\[(\d)\])//) {
35 + my ($target, $page, $section) = ($1, $2, $3);
36 +
37 + # De-AsciiDoc
38 + $page =~ s/{litdd}/--/g;
39 +
40 + if ($page !~ /^git/) {
41 + report($where, $target, "nongit link");
42 + next;
43 + }
44 + if (! -f "$basedir/$page.txt") {
45 + report($where, $target, "no such source");
46 + next;
47 + }
48 + $real_section = grab_section($page);
49 + if ($real_section != $section) {
50 + report($where, $target,
51 + "wrong section (should be $real_section)");
52 + next;
53 + }
54 + }
55 + }
56 + close $fh;
57 +}
58 +
59 +sub lint_it {
60 + lint($File::Find::name) if -f && /\.txt$/;
61 +}
62 +
63 +if (!@ARGV) {
64 + find({ wanted => \&lint_it, no_chdir => 1 }, $basedir);
65 +} else {
66 + for (@ARGV) {
67 + lint($_);
68 + }
69 +}
70 +
71 +exit $found_errors;
Makefile
+1
@@ -2496,6 +2496,7 @@ ALL_COMMANDS += git-gui git-citool
2496
2497 .PHONY: check-docs
2498 check-docs::
2499 + $(MAKE) -C Documentation lint-docs
2500 @(for v in $(ALL_COMMANDS); \
2501 do \
2502 case "$$v" in \