Docs: consistency between man / --help

New functionality in hack/man-page-checker: start cross-
referencing the man page 'Synopsis' line against the
output of 'podman foo --help'. This is part 1, flag/option
consistency. Part 2 (arg consistency) is too big and will
have to wait for later.

flag/option consistency means: if 'podman foo --help'
includes the string '[flags]' in the Usage message,
make sure the man page includes '[*options*]' in its
Synopsis line, and vice-versa. This found several
inconsistencies, which I've fixed.

While doing this I realized that Cobra automatically
includes a 'Flags:' subsection in its --help output
for all subcommands that have defined flags. This
is great - it lets us cross-check against the
usage synopsis, and make sure that '[flags]' is
present or absent as needed, without fear of
human screwups. If a flag-less subcommand ever
gets extended with flags, but the developer forgets
to add '[flags]' and remove DisableFlagsInUseLine,
we now have a test that will catch that. (This,
too, caught two instances which I fixed).

I don't actually know if the new man-page-checker
functionality will work in CI: I vaguely recall that
it might run before 'make podman' does; and also
vaguely recall that some steps were taken to remedy
that.

Signed-off-by: Ed Santiago <santiago@redhat.com>
This commit is contained in:
Ed Santiago
2020-06-24 10:16:59 -06:00
parent 988fd27541
commit c6090c290e
26 changed files with 162 additions and 77 deletions

View File

@ -12,12 +12,13 @@ import (
var ( var (
healthcheckRunDescription = "run the health check of a container" healthcheckRunDescription = "run the health check of a container"
healthcheckrunCommand = &cobra.Command{ healthcheckrunCommand = &cobra.Command{
Use: "run [flags] CONTAINER", Use: "run CONTAINER",
Short: "run the health check of a container", Short: "run the health check of a container",
Long: healthcheckRunDescription, Long: healthcheckRunDescription,
Example: `podman healthcheck run mywebapp`, Example: `podman healthcheck run mywebapp`,
RunE: run, RunE: run,
Args: cobra.ExactArgs(1), Args: cobra.ExactArgs(1),
DisableFlagsInUseLine: true,
} }
) )

View File

@ -9,22 +9,24 @@ import (
var ( var (
tagDescription = "Adds one or more additional names to locally-stored image." tagDescription = "Adds one or more additional names to locally-stored image."
tagCommand = &cobra.Command{ tagCommand = &cobra.Command{
Use: "tag [flags] IMAGE TARGET_NAME [TARGET_NAME...]", Use: "tag IMAGE TARGET_NAME [TARGET_NAME...]",
Short: "Add an additional name to a local image", Short: "Add an additional name to a local image",
Long: tagDescription, Long: tagDescription,
RunE: tag, RunE: tag,
Args: cobra.MinimumNArgs(2), Args: cobra.MinimumNArgs(2),
DisableFlagsInUseLine: true,
Example: `podman tag 0e3bbc2 fedora:latest Example: `podman tag 0e3bbc2 fedora:latest
podman tag imageID:latest myNewImage:newTag podman tag imageID:latest myNewImage:newTag
podman tag httpd myregistryhost:5000/fedora/httpd:v2`, podman tag httpd myregistryhost:5000/fedora/httpd:v2`,
} }
imageTagCommand = &cobra.Command{ imageTagCommand = &cobra.Command{
Args: tagCommand.Args, Args: tagCommand.Args,
Use: tagCommand.Use, DisableFlagsInUseLine: true,
Short: tagCommand.Short, Use: tagCommand.Use,
Long: tagCommand.Long, Short: tagCommand.Short,
RunE: tagCommand.RunE, Long: tagCommand.Long,
RunE: tagCommand.RunE,
Example: `podman image tag 0e3bbc2 fedora:latest Example: `podman image tag 0e3bbc2 fedora:latest
podman image tag imageID:latest myNewImage:newTag podman image tag imageID:latest myNewImage:newTag
podman image tag httpd myregistryhost:5000/fedora/httpd:v2`, podman image tag httpd myregistryhost:5000/fedora/httpd:v2`,

View File

@ -8,22 +8,24 @@ import (
var ( var (
untagCommand = &cobra.Command{ untagCommand = &cobra.Command{
Use: "untag [flags] IMAGE [NAME...]", Use: "untag IMAGE [NAME...]",
Short: "Remove a name from a local image", Short: "Remove a name from a local image",
Long: "Removes one or more names from a locally-stored image.", Long: "Removes one or more names from a locally-stored image.",
RunE: untag, RunE: untag,
Args: cobra.MinimumNArgs(1), Args: cobra.MinimumNArgs(1),
DisableFlagsInUseLine: true,
Example: `podman untag 0e3bbc2 Example: `podman untag 0e3bbc2
podman untag imageID:latest otherImageName:latest podman untag imageID:latest otherImageName:latest
podman untag httpd myregistryhost:5000/fedora/httpd:v2`, podman untag httpd myregistryhost:5000/fedora/httpd:v2`,
} }
imageUntagCommand = &cobra.Command{ imageUntagCommand = &cobra.Command{
Args: untagCommand.Args, Args: untagCommand.Args,
Use: untagCommand.Use, DisableFlagsInUseLine: true,
Short: untagCommand.Short, Use: untagCommand.Use,
Long: untagCommand.Long, Short: untagCommand.Short,
RunE: untagCommand.RunE, Long: untagCommand.Long,
RunE: untagCommand.RunE,
Example: `podman image untag 0e3bbc2 Example: `podman image untag 0e3bbc2
podman image untag imageID:latest otherImageName:latest podman image untag imageID:latest otherImageName:latest
podman image untag httpd myregistryhost:5000/fedora/httpd:v2`, podman image untag httpd myregistryhost:5000/fedora/httpd:v2`,

View File

@ -12,12 +12,13 @@ import (
var ( var (
inspectCmd = &cobra.Command{ inspectCmd = &cobra.Command{
Use: "inspect [flags] IMAGE", Use: "inspect IMAGE",
Short: "Display the contents of a manifest list or image index", Short: "Display the contents of a manifest list or image index",
Long: "Display the contents of a manifest list or image index.", Long: "Display the contents of a manifest list or image index.",
RunE: inspect, RunE: inspect,
Example: "podman manifest inspect localhost/list", Example: "podman manifest inspect localhost/list",
Args: cobra.ExactArgs(1), Args: cobra.ExactArgs(1),
DisableFlagsInUseLine: true,
} }
) )

View File

@ -12,12 +12,13 @@ import (
var ( var (
removeCmd = &cobra.Command{ removeCmd = &cobra.Command{
Use: "remove [flags] LIST IMAGE", Use: "remove LIST IMAGE",
Short: "Remove an entry from a manifest list or image index", Short: "Remove an entry from a manifest list or image index",
Long: "Removes an image from a manifest list or image index.", Long: "Removes an image from a manifest list or image index.",
RunE: remove, RunE: remove,
Example: `podman manifest remove mylist:v1.11 sha256:15352d97781ffdf357bf3459c037be3efac4133dc9070c2dce7eca7c05c3e736`, Example: `podman manifest remove mylist:v1.11 sha256:15352d97781ffdf357bf3459c037be3efac4133dc9070c2dce7eca7c05c3e736`,
Args: cobra.ExactArgs(2), Args: cobra.ExactArgs(2),
DisableFlagsInUseLine: true,
} }
) )

View File

@ -22,11 +22,12 @@ var (
` `
renumberCommand = &cobra.Command{ renumberCommand = &cobra.Command{
Use: "renumber", Use: "renumber",
Args: validate.NoArgs, Args: validate.NoArgs,
Short: "Migrate lock numbers", DisableFlagsInUseLine: true,
Long: renumberDescription, Short: "Migrate lock numbers",
Run: renumber, Long: renumberDescription,
Run: renumber,
} }
) )

View File

@ -13,10 +13,11 @@ import (
var ( var (
unshareDescription = "Runs a command in a modified user namespace." unshareDescription = "Runs a command in a modified user namespace."
unshareCommand = &cobra.Command{ unshareCommand = &cobra.Command{
Use: "unshare [flags] [COMMAND [ARG ...]]", Use: "unshare [COMMAND [ARG ...]]",
Short: "Run a command in a modified user namespace", DisableFlagsInUseLine: true,
Long: unshareDescription, Short: "Run a command in a modified user namespace",
RunE: unshare, Long: unshareDescription,
RunE: unshare,
Example: `podman unshare id Example: `podman unshare id
podman unshare cat /proc/self/uid_map, podman unshare cat /proc/self/uid_map,
podman unshare podman-script.sh`, podman unshare podman-script.sh`,

View File

@ -4,7 +4,7 @@
podman-auto-update - Auto update containers according to their auto-update policy podman-auto-update - Auto update containers according to their auto-update policy
## SYNOPSIS ## SYNOPSIS
**podman auto-update** **podman auto-update** [*options*]
## DESCRIPTION ## DESCRIPTION
`podman auto-update` looks up containers with a specified "io.containers.autoupdate" label (i.e., the auto-update policy). `podman auto-update` looks up containers with a specified "io.containers.autoupdate" label (i.e., the auto-update policy).

View File

@ -4,7 +4,7 @@
podman-container-exists - Check if a container exists in local storage podman-container-exists - Check if a container exists in local storage
## SYNOPSIS ## SYNOPSIS
**podman container exists** [*options*] *container* **podman container exists** *container*
## DESCRIPTION ## DESCRIPTION
**podman container exists** checks if a container exists in local storage. The **ID** or **Name** **podman container exists** checks if a container exists in local storage. The **ID** or **Name**

View File

@ -4,7 +4,7 @@
podman\-healthcheck\-run - Run a container healthcheck podman\-healthcheck\-run - Run a container healthcheck
## SYNOPSIS ## SYNOPSIS
**podman healthcheck run** [*options*] *container* **podman healthcheck run** *container*
## DESCRIPTION ## DESCRIPTION

View File

@ -4,7 +4,7 @@
podman-image-exists - Check if an image exists in local storage podman-image-exists - Check if an image exists in local storage
## SYNOPSIS ## SYNOPSIS
**podman image exists** [*options*] *image* **podman image exists** *image*
## DESCRIPTION ## DESCRIPTION
**podman image exists** checks if an image exists in local storage. The **ID** or **Name** **podman image exists** checks if an image exists in local storage. The **ID** or **Name**

View File

@ -4,7 +4,7 @@
podman\-manifest\-add - Add an image to a manifest list or image index podman\-manifest\-add - Add an image to a manifest list or image index
## SYNOPSIS ## SYNOPSIS
**podman manifest add** *listnameorindexname* *imagename* **podman manifest add** [*options*] *listnameorindexname* *imagename*
## DESCRIPTION ## DESCRIPTION

View File

@ -4,7 +4,7 @@
podman\-manifest\-annotate - Add or update information about an entry in a manifest list or image index podman\-manifest\-annotate - Add or update information about an entry in a manifest list or image index
## SYNOPSIS ## SYNOPSIS
**podman manifest annotate** [options...] *listnameorindexname* *imagemanifestdigest* **podman manifest annotate** [*options*] *listnameorindexname* *imagemanifestdigest*
## DESCRIPTION ## DESCRIPTION

View File

@ -4,7 +4,7 @@
podman\-manifest\-push - Push a manifest list or image index to a registry podman\-manifest\-push - Push a manifest list or image index to a registry
## SYNOPSIS ## SYNOPSIS
**podman manifest push** [options...] *listnameorindexname* *transport:details* **podman manifest push** [*options*] *listnameorindexname* *transport:details*
## DESCRIPTION ## DESCRIPTION
Pushes a manifest list or image index to a registry. Pushes a manifest list or image index to a registry.

View File

@ -4,9 +4,9 @@
podman\-mount - Mount a working container's root filesystem podman\-mount - Mount a working container's root filesystem
## SYNOPSIS ## SYNOPSIS
**podman mount** [*container* ...] **podman mount** [*options*] [*container* ...]
**podman container mount** [*container* ...] **podman container mount** [*options*] [*container* ...]
## DESCRIPTION ## DESCRIPTION
Mounts the specified containers' root file system in a location which can be Mounts the specified containers' root file system in a location which can be

View File

@ -4,7 +4,7 @@
podman\-network\-inspect - Displays the raw CNI network configuration for one or more networks podman\-network\-inspect - Displays the raw CNI network configuration for one or more networks
## SYNOPSIS ## SYNOPSIS
**podman network inspect** [*network* ...] **podman network inspect** [*options*] [*network* ...]
## DESCRIPTION ## DESCRIPTION
Display the raw (JSON format) network configuration. This command is not available for rootless users. Display the raw (JSON format) network configuration. This command is not available for rootless users.

View File

@ -4,7 +4,7 @@
podman\-network\-rm - Remove one or more CNI networks podman\-network\-rm - Remove one or more CNI networks
## SYNOPSIS ## SYNOPSIS
**podman network rm** [*network...*] **podman network rm** [*options*] [*network...*]
## DESCRIPTION ## DESCRIPTION
Delete one or more Podman networks. Delete one or more Podman networks.

View File

@ -4,7 +4,7 @@
podman-pod-prune - Remove all stopped pods and their containers podman-pod-prune - Remove all stopped pods and their containers
## SYNOPSIS ## SYNOPSIS
**podman pod prune** **podman pod prune** [*options*]
## DESCRIPTION ## DESCRIPTION
**podman pod prune** removes all stopped pods and their containers from local storage. **podman pod prune** removes all stopped pods and their containers from local storage.

View File

@ -4,9 +4,9 @@
podman\-rmi - Removes one or more locally stored images podman\-rmi - Removes one or more locally stored images
## SYNOPSIS ## SYNOPSIS
**podman rmi** *image* [...] **podman rmi** [*options*] *image* [...]
**podman image rm** *image* [...] **podman image rm** [*options*] *image* [...]
## DESCRIPTION ## DESCRIPTION
Removes one or more locally stored images. Removes one or more locally stored images.

View File

@ -4,7 +4,7 @@
podman\-system\-migrate - Migrate existing containers to a new podman version podman\-system\-migrate - Migrate existing containers to a new podman version
## SYNOPSIS ## SYNOPSIS
**podman system migrate** **podman system migrate** [*options*]
## DESCRIPTION ## DESCRIPTION
**podman system migrate** migrates containers to the latest podman version. **podman system migrate** migrates containers to the latest podman version.

View File

@ -4,7 +4,7 @@
podman\-system\-reset - Reset storage back to initial state podman\-system\-reset - Reset storage back to initial state
## SYNOPSIS ## SYNOPSIS
**podman system reset** **podman system reset** [*options*]
## DESCRIPTION ## DESCRIPTION
**podman system reset** removes all pods, containers, images and volumes. **podman system reset** removes all pods, containers, images and volumes.

View File

@ -4,13 +4,13 @@
podman\-umount - Unmount a working container's root filesystem podman\-umount - Unmount a working container's root filesystem
## SYNOPSIS ## SYNOPSIS
**podman umount** *container* [...] **podman umount** [*options*] *container* [...]
**podman container umount** *container* [...] **podman container umount** [*options*] *container* [...]
**podman container unmount** *container* [...] **podman container unmount** [*options*] *container* [...]
**podman unmount** *container* [...] **podman unmount** [*options*] *container* [...]
## DESCRIPTION ## DESCRIPTION
Unmounts the specified containers' root file system, if no other processes Unmounts the specified containers' root file system, if no other processes

View File

@ -4,7 +4,7 @@
podman\-unshare - Run a command inside of a modified user namespace podman\-unshare - Run a command inside of a modified user namespace
## SYNOPSIS ## SYNOPSIS
**podman unshare** [*options*] [*--*] [*command*] **podman unshare** [*--*] [*command*]
## DESCRIPTION ## DESCRIPTION
Launches a process (by default, *$SHELL*) in a new user namespace. The user Launches a process (by default, *$SHELL*) in a new user namespace. The user

View File

@ -4,9 +4,9 @@
podman\-untag - Removes one or more names from a locally-stored image podman\-untag - Removes one or more names from a locally-stored image
## SYNOPSIS ## SYNOPSIS
**podman untag** [*options*] *image* [*name*[:*tag*]...] **podman untag** *image* [*name*[:*tag*]...]
**podman image untag** [*options*] *image* [*name*[:*tag*]...] **podman image untag** *image* [*name*[:*tag*]...]
## DESCRIPTION ## DESCRIPTION
Remove one or more names from an image in the local storage. The image can be referred to by ID or reference. If a no name is specified, all names are removed the image. If a specified name is a short name and does not include a registry `localhost/` will be prefixed (e.g., `fedora` -> `localhost/fedora`). If a specified name does not include a tag `:latest` will be appended (e.g., `localhost/fedora` -> `localhost/fedora:latest`). Remove one or more names from an image in the local storage. The image can be referred to by ID or reference. If a no name is specified, all names are removed the image. If a specified name is a short name and does not include a registry `localhost/` will be prefixed (e.g., `fedora` -> `localhost/fedora`). If a specified name does not include a tag `:latest` will be appended (e.g., `localhost/fedora` -> `localhost/fedora:latest`).

View File

@ -3,6 +3,14 @@
# man-page-checker - validate and cross-reference man page names # man-page-checker - validate and cross-reference man page names
# #
verbose=
for i; do
case "$i" in
-v|--verbose) verbose=verbose ;;
esac
done
die() { die() {
echo "$(basename $0): $*" >&2 echo "$(basename $0): $*" >&2
exit 1 exit 1
@ -65,6 +73,61 @@ for md in $(ls -1 *-*.1.md | grep -v remote);do
fi fi
done 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. # Pass 3: compare synopses.
# #
# Make sure the SYNOPSIS line in podman-foo.1.md reads '**podman foo** ...' # Make sure the SYNOPSIS line in podman-foo.1.md reads '**podman foo** ...'
@ -87,9 +150,7 @@ for md in *.1.md;do
cmd=$(echo "$synopsis" | sed -e 's/\(.*\)\*\*.*/\1/' | tr -d \*) cmd=$(echo "$synopsis" | sed -e 's/\(.*\)\*\*.*/\1/' | tr -d \*)
md_nodash=$(basename "$md" .1.md | tr '-' ' ') md_nodash=$(basename "$md" .1.md | tr '-' ' ')
if [[ $md_nodash = 'podman auto update' ]]; then if [[ $md_nodash = 'podman auto update' ]]; then
# podman-auto-update.1.md is special cased as it's structure differs # special case: the command is "auto-update", with a hyphen
# from that of other man pages where main and sub-commands split by
# dashes.
md_nodash='podman auto-update' md_nodash='podman auto-update'
fi fi
if [ "$cmd" != "$md_nodash" -a "$cmd" != "podman-remote" ]; then if [ "$cmd" != "$md_nodash" -a "$cmd" != "podman-remote" ]; then
@ -111,8 +172,9 @@ for md in *.1.md;do
# (for debugging, and getting a sense of standard conventions) # (for debugging, and getting a sense of standard conventions)
#printf " %-32s ------ '%s'\n" $md "$synopsis" #printf " %-32s ------ '%s'\n" $md "$synopsis"
# FIXME: some day: run ./bin/podman "args", extract Usage, # If bin/podman is available, run "cmd --help" and compare Usage
# strip off [flags] and [options], then compare arguments # messages. This is complicated, so do it in a helper function.
compare_usage "$md_nodash" "$synopsis"
done done

View File

@ -34,13 +34,16 @@ function check_help() {
dprint "$command_string --help" dprint "$command_string --help"
run_podman "$@" $cmd --help run_podman "$@" $cmd --help
local full_help="$output"
# The line immediately after 'Usage:' gives us a 1-line synopsis # The line immediately after 'Usage:' gives us a 1-line synopsis
usage=$(echo "$output" | grep -A1 '^Usage:' | tail -1) usage=$(echo "$full_help" | grep -A1 '^Usage:' | tail -1)
[ -n "$usage" ] || die "podman $cmd: no Usage message found" [ -n "$usage" ] || die "podman $cmd: no Usage message found"
# e.g. 'podman ps' should not show 'podman container ps' in usage # e.g. 'podman ps' should not show 'podman container ps' in usage
is "$usage" " $command_string .*" "Usage string matches command" # Trailing space in usage handles 'podman system renumber' which
# has no ' [flags]'
is "$usage " " $command_string .*" "Usage string matches command"
# If usage ends in '[command]', recurse into subcommands # If usage ends in '[command]', recurse into subcommands
if expr "$usage" : '.*\[command\]$' >/dev/null; then if expr "$usage" : '.*\[command\]$' >/dev/null; then
@ -59,6 +62,17 @@ function check_help() {
die "'flags' must precede arguments in usage: $usage" die "'flags' must precede arguments in usage: $usage"
fi fi
# Cross-check: if usage includes '[flags]', there must be a
# longer 'Flags:' section in the full --help output; vice-versa,
# if 'Flags:' is in full output, usage line must have '[flags]'.
if expr "$usage" : '.*\[flag' >/dev/null; then
if ! expr "$full_help" : ".*Flags:" >/dev/null; then
die "$command_string: Usage includes '[flags]' but has no 'Flags:' subsection"
fi
elif expr "$full_help" : ".*Flags:" >/dev/null; then
die "$command_string: --help has 'Flags:' section but no '[flags]' in synopsis"
fi
# If usage lists no arguments (strings in ALL CAPS), confirm # If usage lists no arguments (strings in ALL CAPS), confirm
# by running with 'invalid-arg' and expecting failure. # by running with 'invalid-arg' and expecting failure.
if ! expr "$usage" : '.*[A-Z]' >/dev/null; then if ! expr "$usage" : '.*[A-Z]' >/dev/null; then