aboutsummaryrefslogtreecommitdiff
path: root/hack/man-page-checker
blob: 45f9edbd1ba16a5e6a643aa579bda1f32842ef66 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
#!/usr/bin/env bash
#
# man-page-checker - validate and cross-reference man page names
#

verbose=
for i; do
    case "$i" in
        -v|--verbose)   verbose=verbose ;;
    esac
done


die() {
    echo "$(basename $0): $*" >&2
    exit 1
}

cd $(dirname $0)/../docs/source/markdown || die "Please run me from top-level libpod dir"

rc=0

for md in *.1.md;do
    # Read the first line after '# NAME' (or '## NAME'). (FIXME: # and ##
    #               are not the same; should we stick to one convention?)
    # There may be more than one name, e.g. podman-info.1.md has
    # podman-system-info then another line with podman-info. We
    # care only about the first.
    name=$(egrep -A1 '^#* NAME' $md|tail -1|awk '{print $1}' | tr -d \\\\)

    expect=$(basename $md .1.md)
    if [ "$name" != "$expect" ]; then
        echo
        printf "Inconsistent program NAME in %s:\n" $md
        printf "  NAME= %s  (expected: %s)\n" $name $expect
        rc=1
    fi
done

# Pass 2: compare descriptions.
#
# Make sure the descriptive text in podman-foo.1.md matches the one
# in the table in podman.1.md. podman-remote is not a podman subcommand,
# so it is excluded here.
for md in $(ls -1 *-*.1.md | grep -v remote);do
    desc=$(egrep -A1 '^#* NAME' $md|tail -1|sed -e 's/^podman[^ ]\+ - //')

    # podman.1.md has a two-column table; podman-*.1.md all have three.
    parent=$(echo $md | sed -e 's/^\(.*\)-.*$/\1.1.md/')
    if [[ $parent =~ "podman-auto" ]]; then
        # podman-auto-update.1.md is special cased as it's structure differs
        # from that of other man pages where main and sub-commands split by
        # dashes.
        parent="podman.1.md"
    fi
    x=3
    if expr -- "$parent" : ".*-" >/dev/null; then
        x=4
    fi

    # Find the descriptive text in the parent man page.
    # Strip off the final period; let's not warn about such minutia.
    parent_desc=$(grep $md $parent | awk -F'|' "{print \$$x}" | sed -e 's/^ \+//' -e 's/ \+$//' -e 's/\.$//')

    if [ "$desc" != "$parent_desc" ]; then
        echo
        printf "Inconsistent subcommand descriptions:\n"
        printf "  %-32s = '%s'\n" $md "$desc"
        printf "  %-32s = '%s'\n" $parent "$parent_desc"
        printf "Please ensure that the NAME section of $md\n"
        printf "matches the subcommand description in $parent\n"
        rc=1
    fi
done

# Helper function: compares man page synopsis vs --help usage message
function compare_usage() {
    local cmd="$1"
    local from_man="$2"

    # Sometimes in CI we run before podman gets built.
    test -x ../../../bin/podman || return

    # Run 'cmd --help', grab the line immediately after 'Usage:'
    local help_output=$(../../../bin/$cmd --help)
    local from_help=$(echo "$help_output" | grep -A1 '^Usage:' | tail -1)

    # strip off command name from both
    from_man=$(sed -e "s/\*\*$cmd\*\*[[:space:]]*//" <<<"$from_man")
    from_help=$(sed -e "s/^[[:space:]]*$cmd[[:space:]]*//" <<<"$from_help")

    # man page lists 'foo [*options*]', help msg shows 'foo [flags]'.
    # Make sure if one has it, the other does too.
    if expr "$from_man" : "\[\*options\*\]" >/dev/null; then
        if expr "$from_help" : "\[flags\]" >/dev/null; then
            :
        else
            echo "WARNING: $cmd: man page shows '[*options*]', help does not show [flags]"
            rc=1
       fi
    elif expr "$from_help" : "\[flags\]" >/dev/null; then
        echo "WARNING: $cmd: --help shows [flags], man page does not show [*options*]"
        rc=1
    fi

    # Strip off options and flags; start comparing arguments
    from_man=$(sed  -e 's/^\[\*options\*\][[:space:]]*//' <<<"$from_man")
    from_help=$(sed -e 's/^\[flags\][[:space:]]*//'      <<<"$from_help")

    # Args in man page are '*foo*', in --help are 'FOO'. Convert all to
    # UPCASE simply because it stands out better to the eye.
    from_man=$(sed -e 's/\*\([a-z-]\+\)\*/\U\1/g' <<<"$from_man")

    # FIXME: one of the common patterns is for --help to show 'POD [POD...]'
    # but man page show 'pod ...'. This conversion may help one day, but
    # not yet: there are too many inconsistencies such as '[pod ...]'
    # (brackets) and 'pod...' (no space between).
#    from_help=$(sed -e 's/\([A-Z]\+\)[[:space:]]\+\[\1[[:space:]]*\.\.\.\]/\1 .../' <<<"$from_help")

    # Compare man-page and --help usage strings. For now, do so only
    # when run with --verbose.
    if [[ "$from_man" != "$from_help" ]]; then
        if [ -n "$verbose" ]; then
            printf "%-25s man='%s' help='%s'\n" "$cmd:" "$from_man" "$from_help"
            # Yeah, we're not going to enable this as a blocker any time soon.
            # rc=1
        fi
    fi
}

# Pass 3: compare synopses.
#
# Make sure the SYNOPSIS line in podman-foo.1.md reads '**podman foo** ...'
for md in *.1.md;do
    # FIXME: several pages have a multi-line form of SYNOPSIS in which
    #        many or all flags are enumerated. Some of these are trivial
    #        and really should be made into one line (podman-container-exists,
    #        container-prune, others); some are more complicated and I
    #        would still like to see them one-lined (container-runlabel,
    #        image-trust) but I'm not 100% comfortable doing so myself.
    #        To view those:
    #           $ less $(for i in docs/*.1.md;do x=$(grep -A2 '^#* SYNOPSIS' $i|tail -1); if [ -n "$x" ]; then echo $i;fi;done)
    #
    synopsis=$(egrep -A1 '^#* SYNOPSIS' $md|tail -1)

    # Command name must be bracketed by double asterisks; options and
    # arguments are bracketed by single ones.
    #   E.g. '**podman volume inspect** [*options*] *volume*...'
    # Get the command name, and confirm that it matches the md file name.
    cmd=$(echo "$synopsis" | sed -e 's/\(.*\)\*\*.*/\1/' | tr -d \*)
    md_nodash=$(basename "$md" .1.md | tr '-' ' ')
    if [[ $md_nodash = 'podman auto update' ]]; then
        # special case: the command is "auto-update", with a hyphen
        md_nodash='podman auto-update'
    fi
    if [ "$cmd" != "$md_nodash" -a "$cmd" != "podman-remote" ]; then
        echo
        printf "Inconsistent program name in SYNOPSIS in %s:\n" $md
        printf "  SYNOPSIS = %s (expected: '%s')\n" "$cmd" "$md_nodash"
        rc=1
    fi

    # The convention is to use UPPER CASE in 'podman foo --help',
    # but *lower case bracketed by asterisks* in the man page
    if expr "$synopsis" : ".*[A-Z]" >/dev/null; then
        echo
        printf "Inconsistent capitalization in SYNOPSIS in %s\n" $md
        printf "  '%s' should not contain upper-case characters\n" "$synopsis"
        rc=1
    fi

    # (for debugging, and getting a sense of standard conventions)
    #printf "  %-32s ------ '%s'\n" $md "$synopsis"

    # If bin/podman is available, run "cmd --help" and compare Usage
    # messages. This is complicated, so do it in a helper function.
    compare_usage "$md_nodash" "$synopsis"
done


exit $rc