Man page validation: part 2 of 2

This is the script that runs 'skopeo COMMAND --help' and
cross-checks that all the option flags are documented
in man pages, and vice-versa (all options listed in man
pages appear in COMMAND's --help message).

Copied from podman, with changes for skopeo-land (removing
the rst checks, and conforming to skopeo conventions).

Signed-off-by: Ed Santiago <santiago@redhat.com>
This commit is contained in:
Ed Santiago 2021-06-29 15:56:07 -06:00 committed by Chris Evich
parent b0ebbdd501
commit ae0595c56a
No known key found for this signature in database
GPG Key ID: 03EDC70FD578067F
15 changed files with 514 additions and 73 deletions

View File

@ -65,6 +65,15 @@ validate_task:
make validate-local make validate-local
make vendor && hack/tree_status.sh make vendor && hack/tree_status.sh
doccheck_task:
only_if: $CIRRUS_PR != ''
depends_on:
- validate
container: *build_container
script: |
"${GOSRC}/${SCRIPT_BASE}/runner.sh" setup
"${SKOPEO_PATH}/${SCRIPT_BASE}/runner.sh" build
"${SKOPEO_PATH}/${SCRIPT_BASE}/runner.sh" doccheck
osx_task: osx_task:
only_if: &not_docs $CIRRUS_CHANGE_TITLE !=~ '.*CI:DOCS.*' only_if: &not_docs $CIRRUS_CHANGE_TITLE !=~ '.*CI:DOCS.*'
@ -185,6 +194,7 @@ success_task:
# N/B: ALL tasks must be listed here, minus their '_task' suffix. # N/B: ALL tasks must be listed here, minus their '_task' suffix.
depends_on: depends_on:
- validate - validate
- doccheck
- osx - osx
- cross - cross
- test_skopeo - test_skopeo

View File

@ -207,12 +207,17 @@ validate: build-container
$(CONTAINER_RUN) make validate-local $(CONTAINER_RUN) make validate-local
# This target is only intended for development, e.g. executing it from an IDE. Use (make test) for CI or pre-release testing. # This target is only intended for development, e.g. executing it from an IDE. Use (make test) for CI or pre-release testing.
test-all-local: validate-local test-unit-local test-all-local: validate-local validate-docs test-unit-local
.PHONY: validate-local .PHONY: validate-local
validate-local: validate-local:
hack/make.sh validate-git-marks validate-gofmt validate-lint validate-vet hack/make.sh validate-git-marks validate-gofmt validate-lint validate-vet
# This invokes bin/skopeo, hence cannot be run as part of validate-local
.PHONY: validate-docs
validate-docs:
hack/man-page-checker hack/man-page-checker
hack/xref-helpmsgs-manpages
test-unit-local: test-unit-local:
$(GPGME_ENV) $(GO) test $(MOD_VENDOR) -tags "$(BUILDTAGS)" $$($(GO) list $(MOD_VENDOR) -tags "$(BUILDTAGS)" -e ./... | grep -v '^github\.com/containers/skopeo/\(integration\|vendor/.*\)$$') $(GPGME_ENV) $(GO) test $(MOD_VENDOR) -tags "$(BUILDTAGS)" $$($(GO) list $(MOD_VENDOR) -tags "$(BUILDTAGS)" -e ./... | grep -v '^github\.com/containers/skopeo/\(integration\|vendor/.*\)$$')

View File

@ -65,6 +65,10 @@ _run_validate() {
podmanmake validate-local BUILDTAGS="$BUILDTAGS" podmanmake validate-local BUILDTAGS="$BUILDTAGS"
} }
_run_doccheck() {
podmanmake validate-docs BUILDTAGS="$BUILDTAGS"
}
_run_unit() { _run_unit() {
podmanmake test-unit-local BUILDTAGS="$BUILDTAGS" podmanmake test-unit-local BUILDTAGS="$BUILDTAGS"
} }

View File

@ -20,7 +20,11 @@ automatically inherit any parts of the source name.
## OPTIONS ## OPTIONS
**--all** **--additional-tag**=_strings_
Additional tags (supports docker-archive).
**--all**, **-a**
If _source-image_ refers to a list of images, instead of copying just the image which matches the current OS and If _source-image_ refers to a list of images, instead of copying just the image which matches the current OS and
architecture (subject to the use of the global --override-os, --override-arch and --override-variant options), attempt to copy all of architecture (subject to the use of the global --override-os, --override-arch and --override-variant options), attempt to copy all of
@ -42,57 +46,119 @@ Path of the authentication file for the source registry. Uses path given by `--a
Path of the authentication file for the destination registry. Uses path given by `--authfile`, if not provided. Path of the authentication file for the destination registry. Uses path given by `--authfile`, if not provided.
**--dest-shared-blob-dir** _directory_
Directory to use to share blobs across OCI repositories.
**--digestfile** _path_ **--digestfile** _path_
After copying the image, write the digest of the resulting image to the file. After copying the image, write the digest of the resulting image to the file.
**--format, -f** _manifest-type_ MANIFEST TYPE (oci, v2s1, or v2s2) to use in the destination (default is manifest type of source, with fallbacks) **--encrypt-layer** _ints_
**--quiet, -q** suppress output information when copying images *Experimental* the 0-indexed layer indices, with support for negative indexing (e.g. 0 is the first layer, -1 is the last layer)
**--remove-signatures** do not copy signatures, if any, from _source-image_. Necessary when copying a signed image to a destination which does not support signatures. **--format**, **-f** _manifest-type_
**--sign-by=**_key-id_ add a signature using that key ID for an image name corresponding to _destination-image_ MANIFEST TYPE (oci, v2s1, or v2s2) to use in the destination (default is manifest type of source, with fallbacks)
**--encryption-key** _protocol:keyfile_ specifies the encryption protocol, which can be JWE (RFC7516), PGP (RFC4880), and PKCS7 (RFC2315) and the key material required for image encryption. For instance, jwe:/path/to/key.pem or pgp:admin@example.com or pkcs7:/path/to/x509-file. **--help**, **-h**
**--decryption-key** _key[:passphrase]_ to be used for decryption of images. Key can point to keys and/or certificates. Decryption will be tried with all keys. If the key is protected by a passphrase, it is required to be passed in the argument and omitted otherwise. Print usage statement
**--src-creds** _username[:password]_ for accessing the source registry. **--quiet**, **-q**
**--dest-compress** _bool-value_ Compress tarball image layers when saving to directory using the 'dir' transport. (default is same compression type as source). Suppress output information when copying images.
**--dest-oci-accept-uncompressed-layers** _bool-value_ Allow uncompressed image layers when saving to an OCI image using the 'oci' transport. (default is to compress things that aren't compressed). **--remove-signatures**
**--dest-creds** _username[:password]_ for accessing the destination registry. Do not copy signatures, if any, from _source-image_. Necessary when copying a signed image to a destination which does not support signatures.
**--src-cert-dir** _path_ Use certificates at _path_ (*.crt, *.cert, *.key) to connect to the source registry or daemon. **--sign-by**=_key-id_
**--src-no-creds** _bool-value_ Access the registry anonymously. Add a signature using that key ID for an image name corresponding to _destination-image_
**--src-tls-verify** _bool-value_ Require HTTPS and verify certificates when talking to container source registry or daemon (defaults to true). **--src-shared-blob-dir** _directory_
**--dest-cert-dir** _path_ Use certificates at _path_ (*.crt, *.cert, *.key) to connect to the destination registry or daemon. Directory to use to share blobs across OCI repositories.
**--dest-no-creds** _bool-value_ Access the registry anonymously. **--encryption-key** _protocol:keyfile_
**--dest-tls-verify** _bool-value_ Require HTTPS and verify certificates when talking to container destination registry or daemon (defaults to true). Specifies the encryption protocol, which can be JWE (RFC7516), PGP (RFC4880), and PKCS7 (RFC2315) and the key material required for image encryption. For instance, jwe:/path/to/key.pem or pgp:admin@example.com or pkcs7:/path/to/x509-file.
**--src-daemon-host** _host_ Copy from docker daemon at _host_. If _host_ starts with `tcp://`, HTTPS is enabled by default. To use plain HTTP, use the form `http://` (default is `unix:///var/run/docker.sock`). **--decryption-key** _key[:passphrase]_
**--dest-daemon-host** _host_ Copy to docker daemon at _host_. If _host_ starts with `tcp://`, HTTPS is enabled by default. To use plain HTTP, use the form `http://` (default is `unix:///var/run/docker.sock`). Key to be used for decryption of images. Key can point to keys and/or certificates. Decryption will be tried with all keys. If the key is protected by a passphrase, it is required to be passed in the argument and omitted otherwise.
**--src-creds** _username[:password]_
Credentials for accessing the source registry.
**--dest-compress** _bool-value_
Compress tarball image layers when saving to directory using the 'dir' transport. (default is same compression type as source).
**--dest-oci-accept-uncompressed-layers** _bool-value_
Allow uncompressed image layers when saving to an OCI image using the 'oci' transport. (default is to compress things that aren't compressed).
**--dest-creds** _username[:password]_
Credentials for accessing the destination registry.
**--src-cert-dir** _path_
Use certificates at _path_ (*.crt, *.cert, *.key) to connect to the source registry or daemon.
**--src-no-creds** _bool-value_
Access the registry anonymously.
**--src-tls-verify** _bool-value_
Require HTTPS and verify certificates when talking to container source registry or daemon (defaults to true).
**--dest-cert-dir** _path_
Use certificates at _path_ (*.crt, *.cert, *.key) to connect to the destination registry or daemon.
**--dest-no-creds** _bool-value_
Access the registry anonymously.
**--dest-tls-verify** _bool-value_
Require HTTPS and verify certificates when talking to container destination registry or daemon (defaults to true).
**--src-daemon-host** _host_
Copy from docker daemon at _host_. If _host_ starts with `tcp://`, HTTPS is enabled by default. To use plain HTTP, use the form `http://` (default is `unix:///var/run/docker.sock`).
**--dest-daemon-host** _host_
Copy to docker daemon at _host_. If _host_ starts with `tcp://`, HTTPS is enabled by default. To use plain HTTP, use the form `http://` (default is `unix:///var/run/docker.sock`).
Existing signatures, if any, are preserved as well. Existing signatures, if any, are preserved as well.
**--dest-compress-format** _format_ Specifies the compression format to use. Supported values are: `gzip` and `zstd`. **--dest-compress-format** _format_
**--dest-compress-level** _format_ Specifies the compression level to use. The value is specific to the compression algorithm used, e.g. for zstd the accepted values are in the range 1-20 (inclusive), while for gzip it is 1-9 (inclusive). Specifies the compression format to use. Supported values are: `gzip` and `zstd`.
**--src-registry-token** _Bearer token_ for accessing the source registry. **--dest-compress-level** _format_
**--dest-registry-token** _Bearer token_ for accessing the destination registry. Specifies the compression level to use. The value is specific to the compression algorithm used, e.g. for zstd the accepted values are in the range 1-20 (inclusive), while for gzip it is 1-9 (inclusive).
**--retry-times** the number of times to retry, retry wait time will be exponentially increased based on the number of failed attempts. **--src-registry-token** _token_
Bearer token for accessing the source registry.
**--dest-registry-token** _token_
Bearer token for accessing the destination registry.
**--retry-times**
The number of times to retry. Retry wait time will be exponentially increased based on the number of failed attempts.
## EXAMPLES ## EXAMPLES

View File

@ -19,24 +19,46 @@ $ docker exec -it registry /usr/bin/registry garbage-collect /etc/docker-distrib
``` ```
## OPTIONS
**--authfile** _path_ **--authfile** _path_
Path of the authentication file. Default is ${XDG_RUNTIME\_DIR}/containers/auth.json, which is set using `skopeo login`. Path of the authentication file. Default is ${XDG_RUNTIME\_DIR}/containers/auth.json, which is set using `skopeo login`.
If the authorization state is not found there, $HOME/.docker/config.json is checked, which is set using `docker login`. If the authorization state is not found there, $HOME/.docker/config.json is checked, which is set using `docker login`.
**--creds** _username[:password]_ for accessing the registry. **--creds** _username[:password]_
**--cert-dir** _path_ Use certificates at _path_ (*.crt, *.cert, *.key) to connect to the registry. Credentials for accessing the registry.
**--tls-verify** _bool-value_ Require HTTPS and verify certificates when talking to container registries (defaults to true). **--cert-dir** _path_
**--no-creds** _bool-value_ Access the registry anonymously. Use certificates at _path_ (*.crt, *.cert, *.key) to connect to the registry.
**--daemon-host** _host_
Use docker daemon host at _host_ (`docker-daemon:` transport only)
**--help**, **-h**
Print usage statement
**--no-creds** _bool-value_
Access the registry anonymously.
Additionally, the registry must allow deletions by setting `REGISTRY_STORAGE_DELETE_ENABLED=true` for the registry daemon. Additionally, the registry must allow deletions by setting `REGISTRY_STORAGE_DELETE_ENABLED=true` for the registry daemon.
**--registry-token** _Bearer token_ for accessing the registry. **--registry-token** _token_
**--retry-times** the number of times to retry, retry wait time will be exponentially increased based on the number of failed attempts. Bearer token for accessing the registry.
**--retry-times**
The number of times to retry. Retry wait time will be exponentially increased based on the number of failed attempts.
**--shared-blob-dir** _directory_
Directory to use to share blobs across OCI repositories.
## EXAMPLES ## EXAMPLES

View File

@ -31,11 +31,19 @@ Output configuration in OCI format, default is to format in JSON format.
Username and password for accessing the registry. Username and password for accessing the registry.
**--daemon-host** _host_
Use docker daemon host at _host_ (`docker-daemon:` transport only)
**--format**, **-f**=*format* **--format**, **-f**=*format*
Format the output using the given Go template. Format the output using the given Go template.
The keys of the returned JSON can be used as the values for the --format flag (see examples below). The keys of the returned JSON can be used as the values for the --format flag (see examples below).
**--help**, **-h**
Print usage statement
**--no-creds** **--no-creds**
Access the registry anonymously. Access the registry anonymously.
@ -53,9 +61,9 @@ Registry token for accessing the registry.
The number of times to retry; retry wait time will be exponentially increased based on the number of failed attempts. The number of times to retry; retry wait time will be exponentially increased based on the number of failed attempts.
**--tls-verify** **--shared-blob-dir** _directory_
Require HTTPS and verify certificates when talking to container registries (defaults to true). Directory to use to share blobs across OCI repositories.
## EXAMPLES ## EXAMPLES

View File

@ -10,22 +10,34 @@ Return a list of tags from _repository-name_ in a registry.
_repository-name_ name of repository to retrieve tag listing from _repository-name_ name of repository to retrieve tag listing from
**--authfile** _path_ ## OPTIONS
Path of the authentication file. Default is ${XDG\_RUNTIME\_DIR}/containers/auth.json, which is set using `skopeo login`. **--authfile** _path_
Path of the authentication file. Default is ${XDG\_RUNTIME\_DIR}/containers/auth.json, which is set using `skopeo login`.
If the authorization state is not found there, $HOME/.docker/config.json is checked, which is set using `docker login`. If the authorization state is not found there, $HOME/.docker/config.json is checked, which is set using `docker login`.
**--creds** _username[:password]_ for accessing the registry. **--creds** _username[:password]_ for accessing the registry.
**--cert-dir** _path_ Use certificates at _path_ (\*.crt, \*.cert, \*.key) to connect to the registry. **--cert-dir** _path_
**--tls-verify** _bool-value_ Require HTTPS and verify certificates when talking to container registries (defaults to true). Use certificates at _path_ (\*.crt, \*.cert, \*.key) to connect to the registry.
**--no-creds** _bool-value_ Access the registry anonymously. **--help**, **-h**
**--registry-token** _Bearer token_ for accessing the registry. Print usage statement
**--retry-times** the number of times to retry, retry wait time will be exponentially increased based on the number of failed attempts. **--no-creds** _bool-value_
Access the registry anonymously.
**--registry-token** _Bearer token_
Bearer token for accessing the registry.
**--retry-times**
The number of times to retry. Retry wait time will be exponentially increased based on the number of failed attempts.
## REPOSITORY NAMES ## REPOSITORY NAMES

View File

@ -43,16 +43,14 @@ Return the logged-in user for the registry. Return error if no login is found.
Use certificates at *path* (\*.crt, \*.cert, \*.key) to connect to the registry. Use certificates at *path* (\*.crt, \*.cert, \*.key) to connect to the registry.
Default certificates directory is _/etc/containers/certs.d_. Default certificates directory is _/etc/containers/certs.d_.
**--tls-verify**=*true|false*
Require HTTPS and verify certificates when contacting registries (default: true). If explicitly set to true,
then TLS verification will be used. If set to false, then TLS verification will not be used. If not specified,
TLS verification will be used unless the target registry is listed as an insecure registry in registries.conf.
**--help**, **-h** **--help**, **-h**
Print usage statement Print usage statement
**--verbose**, **-v**
Write more detailed information to stdout
## EXAMPLES ## EXAMPLES
``` ```

View File

@ -10,6 +10,12 @@ skopeo\-manifest\-digest - Compute a manifest digest for a manifest-file and wri
Compute a manifest digest of _manifest-file_ and write it to standard output. Compute a manifest digest of _manifest-file_ and write it to standard output.
## OPTIONS
**--help**, **-h**
Print usage statement
## EXAMPLES ## EXAMPLES
```sh ```sh
@ -23,4 +29,3 @@ skopeo(1)
## AUTHORS ## AUTHORS
Antonio Murdaca <runcom@redhat.com>, Miloslav Trmac <mitr@redhat.com>, Jhon Honce <jhonce@redhat.com> Antonio Murdaca <runcom@redhat.com>, Miloslav Trmac <mitr@redhat.com>, Jhon Honce <jhonce@redhat.com>

View File

@ -15,7 +15,15 @@ This is primarily a debugging tool, useful for special cases, and usually should
_key-fingerprint_ Key identity to use for signing _key-fingerprint_ Key identity to use for signing
**--output**|**-o** output file ## OPTIONS
**--help**, **-h**
Print usage statement
**--output**, **-o** _output file_
Write signature to _output file_.
## EXAMPLES ## EXAMPLES

View File

@ -22,6 +22,12 @@ as per containers-policy.json(5).
**Note:** If you do use this, make sure that the image can not be changed at the source location between the times of its verification and use. **Note:** If you do use this, make sure that the image can not be changed at the source location between the times of its verification and use.
## OPTIONS
**--help**, **-h**
Print usage statement
## EXAMPLES ## EXAMPLES
```sh ```sh

View File

@ -32,7 +32,7 @@ When the `--scoped` option is specified, images are prefixed with the source ima
name can be stored at _destination_. name can be stored at _destination_.
## OPTIONS ## OPTIONS
**--all** **--all**, **-a**
If one of the images in __src__ refers to a list of images, instead of copying just the image which matches the current OS and If one of the images in __src__ refers to a list of images, instead of copying just the image which matches the current OS and
architecture (subject to the use of the global --override-os, --override-arch and --override-variant options), attempt to copy all of architecture (subject to the use of the global --override-os, --override-arch and --override-variant options), attempt to copy all of
the images in the list, and the list itself. the images in the list, and the list itself.
@ -50,17 +50,21 @@ Path of the authentication file for the source registry. Uses path given by `--a
Path of the authentication file for the destination registry. Uses path given by `--authfile`, if not provided. Path of the authentication file for the destination registry. Uses path given by `--authfile`, if not provided.
**--src** _transport_ Transport for the source repository. **--src**, **-s** _transport_ Transport for the source repository.
**--dest** _transport_ Destination transport. **--dest**, **-d** _transport_ Destination transport.
**--format, -f** _manifest-type_ Manifest Type (oci, v2s1, or v2s2) to use when syncing image(s) to a destination (default is manifest type of source, with fallbacks). **--format**, **-f** _manifest-type_ Manifest Type (oci, v2s1, or v2s2) to use when syncing image(s) to a destination (default is manifest type of source, with fallbacks).
**--help**, **-h**
Print usage statement.
**--scoped** Prefix images with the source image path, so that multiple images with the same name can be stored at _destination_. **--scoped** Prefix images with the source image path, so that multiple images with the same name can be stored at _destination_.
**--remove-signatures** Do not copy signatures, if any, from _source-image_. This is necessary when copying a signed image to a destination which does not support signatures. **--remove-signatures** Do not copy signatures, if any, from _source-image_. This is necessary when copying a signed image to a destination which does not support signatures.
**--sign-by=**_key-id_ Add a signature using that key ID for an image name corresponding to _destination-image_. **--sign-by**=_key-id_ Add a signature using that key ID for an image name corresponding to _destination-image_.
**--src-creds** _username[:password]_ for accessing the source registry. **--src-creds** _username[:password]_ for accessing the source registry.

View File

@ -51,27 +51,49 @@ See [containers-transports(5)](https://github.com/containers/image/blob/master/d
## OPTIONS ## OPTIONS
**--command-timeout** _duration_ Timeout for the command execution. **--command-timeout** _duration_
**--debug** enable debug output Timeout for the command execution.
**--help**|**-h** Show help **--debug**
**--insecure-policy** Adopt an insecure, permissive policy that allows anything. This obviates the need for a policy file. enable debug output
**--override-arch** _arch_ Use _arch_ instead of the architecture of the machine for choosing images. **--help**, **-h**
**--override-os** _OS_ Use _OS_ instead of the running OS for choosing images. Show help
**--override-variant** _VARIANT_ Use _VARIANT_ instead of the running architecture variant for choosing images. **--insecure-policy**
**--policy** _path-to-policy_ Path to a policy.json file to use for verifying signatures and deciding whether an image is trusted, overriding the default trust policy file. Adopt an insecure, permissive policy that allows anything. This obviates the need for a policy file.
**--registries.d** _dir_ use registry configuration files in _dir_ (e.g. for container signature storage), overriding the default path. **--override-arch** _arch_
**--tmpdir** _dir_ used to store temporary files. Defaults to /var/tmp. Use _arch_ instead of the architecture of the machine for choosing images.
**--version**|**-v** print the version number **--override-os** _os_
Use _OS_ instead of the running OS for choosing images.
**--override-variant** _variant_
Use _variant_ instead of the running architecture variant for choosing images.
**--policy** _path-to-policy_
Path to a policy.json file to use for verifying signatures and deciding whether an image is trusted, overriding the default trust policy file.
**--registries.d** _dir_
Use registry configuration files in _dir_ (e.g. for container signature storage), overriding the default path.
**--tmpdir** _dir_
Directory used to store temporary files. Defaults to /var/tmp.
**--version**, **-v**
Print the version number
## COMMANDS ## COMMANDS

View File

@ -6,9 +6,6 @@
# script that cross-checks that each option in skopeo foo --help is listed # script that cross-checks that each option in skopeo foo --help is listed
# in skopeo-foo.1.md and vice-versa; that one is xref-helpmsgs-manpages. # in skopeo-foo.1.md and vice-versa; that one is xref-helpmsgs-manpages.
# #
# IMPORTANT NOTE: this script runs on Macs, on which sed is an ancient
# non-gnu version. To make sed work on all platforms, we invoke with '-E'.
#
verbose= verbose=
for i; do for i; do
@ -70,9 +67,6 @@ function compare_usage() {
local cmd="$1" local cmd="$1"
local from_man="$2" local from_man="$2"
# Sometimes in CI we run before skopeo gets built.
test -x ../bin/skopeo || return
# Run 'cmd --help', grab the line immediately after 'Usage:' # Run 'cmd --help', grab the line immediately after 'Usage:'
local help_output=$(../bin/$cmd --help) local help_output=$(../bin/$cmd --help)
local from_help=$(echo "$help_output" | grep -A1 '^Usage:' | tail -1) local from_help=$(echo "$help_output" | grep -A1 '^Usage:' | tail -1)

277
hack/xref-helpmsgs-manpages Executable file
View File

@ -0,0 +1,277 @@
#!/usr/bin/perl
#
# xref-helpmsgs-manpages - cross-reference --help options against man pages
#
package LibPod::CI::XrefHelpmsgsManpages;
use v5.14;
use utf8;
use strict;
use warnings;
(our $ME = $0) =~ s|.*/||;
our $VERSION = '0.1';
# For debugging, show data structures using DumpTree($var)
#use Data::TreeDumper; $Data::TreeDumper::Displayaddress = 0;
# unbuffer output
$| = 1;
###############################################################################
# BEGIN user-customizable section
# Path to skopeo executable
my $Default_Skopeo = './bin/skopeo';
my $SKOPEO = $ENV{SKOPEO} || $Default_Skopeo;
# Path to all doc files (markdown)
my $Docs_Path = 'docs';
# Global error count
my $Errs = 0;
# END user-customizable section
###############################################################################
###############################################################################
# BEGIN boilerplate args checking, usage messages
sub usage {
print <<"END_USAGE";
Usage: $ME [OPTIONS]
$ME recursively runs 'skopeo --help' against
all subcommands; and recursively reads skopeo-*.1.md files
in $Docs_Path, then cross-references that each --help
option is listed in the appropriate man page and vice-versa.
$ME invokes '\$SKOPEO' (default: $Default_Skopeo).
Exit status is zero if no inconsistencies found, one otherwise
OPTIONS:
-v, --verbose show verbose progress indicators
-n, --dry-run make no actual changes
--help display this message
--version display program name and version
END_USAGE
exit;
}
# Command-line options. Note that this operates directly on @ARGV !
our $debug = 0;
our $verbose = 0;
sub handle_opts {
use Getopt::Long;
GetOptions(
'debug!' => \$debug,
'verbose|v' => \$verbose,
help => \&usage,
version => sub { print "$ME version $VERSION\n"; exit 0 },
) or die "Try `$ME --help' for help\n";
}
# END boilerplate args checking, usage messages
###############################################################################
############################## CODE BEGINS HERE ###############################
# The term is "modulino".
__PACKAGE__->main() unless caller();
# Main code.
sub main {
# Note that we operate directly on @ARGV, not on function parameters.
# This is deliberate: it's because Getopt::Long only operates on @ARGV
# and there's no clean way to make it use @_.
handle_opts(); # will set package globals
# Fetch command-line arguments. Barf if too many.
die "$ME: Too many arguments; try $ME --help\n" if @ARGV;
my $help = skopeo_help();
my $man = skopeo_man('skopeo');
xref_by_help($help, $man);
xref_by_man($help, $man);
exit !!$Errs;
}
###############################################################################
# BEGIN cross-referencing
##################
# xref_by_help # Find keys in '--help' but not in man
##################
sub xref_by_help {
my ($help, $man, @subcommand) = @_;
for my $k (sort keys %$help) {
if (exists $man->{$k}) {
if (ref $help->{$k}) {
xref_by_help($help->{$k}, $man->{$k}, @subcommand, $k);
}
# Otherwise, non-ref is leaf node such as a --option
}
else {
my $man = $man->{_path} || 'man';
warn "$ME: skopeo @subcommand --help lists $k, but $k not in $man\n";
++$Errs;
}
}
}
#################
# xref_by_man # Find keys in man pages but not in --help
#################
#
# In an ideal world we could share the functionality in one function; but
# there are just too many special cases in man pages.
#
sub xref_by_man {
my ($help, $man, @subcommand) = @_;
# FIXME: this generates way too much output
for my $k (grep { $_ ne '_path' } sort keys %$man) {
if (exists $help->{$k}) {
if (ref $man->{$k}) {
xref_by_man($help->{$k}, $man->{$k}, @subcommand, $k);
}
}
elsif ($k ne '--help' && $k ne '-h') {
my $man = $man->{_path} || 'man';
warn "$ME: skopeo @subcommand: $k in $man, but not --help\n";
++$Errs;
}
}
}
# END cross-referencing
###############################################################################
# BEGIN data gathering
#################
# skopeo_help # Parse output of 'skopeo [subcommand] --help'
#################
sub skopeo_help {
my %help;
open my $fh, '-|', $SKOPEO, @_, '--help'
or die "$ME: Cannot fork: $!\n";
my $section = '';
while (my $line = <$fh>) {
# Cobra is blessedly consistent in its output:
# Usage: ...
# Available Commands:
# ....
# Options:
# ....
#
# Start by identifying the section we're in...
if ($line =~ /^Available\s+(Commands):/) {
$section = lc $1;
}
elsif ($line =~ /^(Flags):/) {
$section = lc $1;
}
# ...then track commands and options. For subcommands, recurse.
elsif ($section eq 'commands') {
if ($line =~ /^\s{1,4}(\S+)\s/) {
my $subcommand = $1;
print "> skopeo @_ $subcommand\n" if $debug;
$help{$subcommand} = skopeo_help(@_, $subcommand)
unless $subcommand eq 'help'; # 'help' not in man
}
}
elsif ($section eq 'flags') {
# Handle '--foo' or '-f, --foo'
if ($line =~ /^\s{1,10}(--\S+)\s/) {
print "> skopeo @_ $1\n" if $debug;
$help{$1} = 1;
}
elsif ($line =~ /^\s{1,10}(-\S),\s+(--\S+)\s/) {
print "> skopeo @_ $1, $2\n" if $debug;
$help{$1} = $help{$2} = 1;
}
}
}
close $fh
or die "$ME: Error running 'skopeo @_ --help'\n";
return \%help;
}
################
# skopeo_man # Parse contents of skopeo-*.1.md
################
sub skopeo_man {
my $command = shift;
my $manpath = "$Docs_Path/$command.1.md";
print "** $manpath \n" if $debug;
my %man = (_path => $manpath);
open my $fh, '<', $manpath
or die "$ME: Cannot read $manpath: $!\n";
my $section = '';
my @most_recent_flags;
my $previous_subcmd = '';
while (my $line = <$fh>) {
chomp $line;
next unless $line; # skip empty lines
# .md files designate sections with leading double hash
if ($line =~ /^##\s*OPTIONS/) {
$section = 'flags';
}
elsif ($line =~ /^\#\#\s+(SUB)?COMMANDS/) {
$section = 'commands';
}
elsif ($line =~ /^\#\#[^#]/) {
$section = '';
}
# This will be a table containing subcommand names, links to man pages.
elsif ($section eq 'commands') {
# In skopeo.1.md
if ($line =~ /^\|\s*\[skopeo-(\S+?)\(\d\)\]/) {
# $1 will be changed by recursion _*BEFORE*_ left-hand assignment
my $subcmd = $1;
$man{$subcmd} = skopeo_man("skopeo-$1");
}
}
# Options should always be of the form '**-f**' or '**\-\-flag**',
# possibly separated by comma-space.
elsif ($section eq 'flags') {
# If option has long and short form, long must come first.
# This is a while-loop because there may be multiple long
# option names (not in skopeo ATM, but leave the possibility open)
while ($line =~ s/^\*\*(--[a-z0-9.-]+)\*\*(=\*[a-zA-Z0-9-]+\*)?(,\s+)?//g) {
$man{$1} = 1;
}
# Short form
if ($line =~ s/^\*\*(-[a-zA-Z0-9.])\*\*(=\*[a-zA-Z0-9-]+\*)?//g) {
$man{$1} = 1;
}
}
}
close $fh;
return \%man;
}
# END data gathering
###############################################################################
1;