man page crossrefs: add --filter autocompletes

For all commands with a --filter option, cross-reference
against man pages, and vice-versa.

I'm sorry. I know this script has gone off the deep end.

[NO NEW TESTS NEEDED] although actually I would like to test some broken completions

Signed-off-by: Ed Santiago <santiago@redhat.com>
This commit is contained in:
Ed Santiago
2023-09-12 19:10:10 -06:00
parent 5dc4370d91
commit deba3b80a1
11 changed files with 128 additions and 23 deletions

View File

@ -1436,6 +1436,7 @@ func AutocompleteEventFilter(cmd *cobra.Command, args []string, toComplete strin
"pod=": func(s string) ([]string, cobra.ShellCompDirective) { return getPods(cmd, s, completeDefault) }, "pod=": func(s string) ([]string, cobra.ShellCompDirective) { return getPods(cmd, s, completeDefault) },
"volume=": func(s string) ([]string, cobra.ShellCompDirective) { return getVolumes(cmd, s) }, "volume=": func(s string) ([]string, cobra.ShellCompDirective) { return getVolumes(cmd, s) },
"event=": event, "event=": event,
"label=": nil,
"type=": eventTypes, "type=": eventTypes,
} }
return completeKeyValues(toComplete, kv) return completeKeyValues(toComplete, kv)
@ -1644,6 +1645,7 @@ func AutocompletePodPsFilters(cmd *cobra.Command, args []string, toComplete stri
return []string{"stopped", "running", return []string{"stopped", "running",
"paused", "exited", "dead", "created", "degraded"}, cobra.ShellCompDirectiveNoFileComp "paused", "exited", "dead", "created", "degraded"}, cobra.ShellCompDirectiveNoFileComp
}, },
"until=": nil,
} }
return completeKeyValues(toComplete, kv) return completeKeyValues(toComplete, kv)
} }
@ -1652,12 +1654,19 @@ func AutocompletePodPsFilters(cmd *cobra.Command, args []string, toComplete stri
func AutocompleteImageFilters(cmd *cobra.Command, args []string, toComplete string) ([]string, cobra.ShellCompDirective) { func AutocompleteImageFilters(cmd *cobra.Command, args []string, toComplete string) ([]string, cobra.ShellCompDirective) {
getImg := func(s string) ([]string, cobra.ShellCompDirective) { return getImages(cmd, s) } getImg := func(s string) ([]string, cobra.ShellCompDirective) { return getImages(cmd, s) }
kv := keyValueCompletion{ kv := keyValueCompletion{
"before=": getImg, "after=": getImg,
"dangling=": getBoolCompletion, "before=": getImg,
"label=": nil, "containers=": getBoolCompletion,
"readonly=": getBoolCompletion, "dangling=": getBoolCompletion,
"reference=": nil, "digest=": nil,
"since=": getImg, "id=": getImg,
"intermediate=": getBoolCompletion,
"label=": nil,
"manifest=": getImg,
"readonly=": getBoolCompletion,
"reference=": nil,
"since=": getImg,
"until=": nil,
} }
return completeKeyValues(toComplete, kv) return completeKeyValues(toComplete, kv)
} }
@ -1674,6 +1683,7 @@ func AutocompletePruneFilters(cmd *cobra.Command, args []string, toComplete stri
// AutocompleteNetworkFilters - Autocomplete network ls --filter options. // AutocompleteNetworkFilters - Autocomplete network ls --filter options.
func AutocompleteNetworkFilters(cmd *cobra.Command, args []string, toComplete string) ([]string, cobra.ShellCompDirective) { func AutocompleteNetworkFilters(cmd *cobra.Command, args []string, toComplete string) ([]string, cobra.ShellCompDirective) {
kv := keyValueCompletion{ kv := keyValueCompletion{
"dangling=": getBoolCompletion,
"driver=": func(_ string) ([]string, cobra.ShellCompDirective) { "driver=": func(_ string) ([]string, cobra.ShellCompDirective) {
return []string{types.BridgeNetworkDriver, types.MacVLANNetworkDriver, types.IPVLANNetworkDriver}, cobra.ShellCompDirectiveNoFileComp return []string{types.BridgeNetworkDriver, types.MacVLANNetworkDriver, types.IPVLANNetworkDriver}, cobra.ShellCompDirectiveNoFileComp
}, },
@ -1690,13 +1700,17 @@ func AutocompleteVolumeFilters(cmd *cobra.Command, args []string, toComplete str
local := func(_ string) ([]string, cobra.ShellCompDirective) { local := func(_ string) ([]string, cobra.ShellCompDirective) {
return []string{"local"}, cobra.ShellCompDirectiveNoFileComp return []string{"local"}, cobra.ShellCompDirectiveNoFileComp
} }
getImg := func(s string) ([]string, cobra.ShellCompDirective) { return getImages(cmd, s) }
kv := keyValueCompletion{ kv := keyValueCompletion{
"after=": getImg,
"dangling=": getBoolCompletion, "dangling=": getBoolCompletion,
"driver=": local, "driver=": local,
"label=": nil, "label=": nil,
"name=": func(s string) ([]string, cobra.ShellCompDirective) { return getVolumes(cmd, s) }, "name=": func(s string) ([]string, cobra.ShellCompDirective) { return getVolumes(cmd, s) },
"opt=": nil, "opt=": nil,
"scope=": local, "scope=": local,
"since=": getImg,
"until=": nil,
} }
return completeKeyValues(toComplete, kv) return completeKeyValues(toComplete, kv)
} }

View File

@ -87,13 +87,16 @@ Setting `events_container_create_inspect_data=true` in containers.conf(5) instru
Filter events that are displayed. They must be in the format of "filter=value". The following Filter events that are displayed. They must be in the format of "filter=value". The following
filters are supported: filters are supported:
* container=name_or_id
* event=event_status (described above) | **Filter** | **Description** |
* image=name_or_id |------------|-------------------------------------|
* label=key=value | container | [Name or ID] Container's name or ID |
* pod=name_or_id | event | event_status (described above) |
* volume=name_or_id | image | [Name or ID] Image name or ID |
* type=event_type (described above) | label | [key=value] label |
| pod | [Name or ID] Pod name or ID |
| volume | [Name or ID] Volume name or ID |
| type | Event_type (described above) |
In the case where an ID is used, the ID may be in its full or shortened form. The "die" event is mapped to "died" for Docker compatibility. In the case where an ID is used, the ID may be in its full or shortened form. The "die" event is mapped to "died" for Docker compatibility.

View File

@ -42,6 +42,7 @@ Valid filters are listed below:
| health | [Status] healthy or unhealthy | | health | [Status] healthy or unhealthy |
| pod | [Pod] name or full or partial ID of pod | | pod | [Pod] name or full or partial ID of pod |
| network | [Network] name or full ID of network | | network | [Network] name or full ID of network |
| until | [DateTime] container created before the given duration or time. |
@@option latest @@option latest

View File

@ -60,6 +60,7 @@ Valid filters are listed below:
| health | [Status] healthy or unhealthy | | health | [Status] healthy or unhealthy |
| pod | [Pod] name or full or partial ID of pod | | pod | [Pod] name or full or partial ID of pod |
| network | [Network] name or full ID of network | | network | [Network] name or full ID of network |
| until | [DateTime] container created before the given duration or time. |
#### **--format**=*format* #### **--format**=*format*

View File

@ -45,6 +45,7 @@ Valid filters are listed below:
| health | [Status] healthy or unhealthy | | health | [Status] healthy or unhealthy |
| pod | [Pod] name or full or partial ID of pod | | pod | [Pod] name or full or partial ID of pod |
| network | [Network] name or full ID of network | | network | [Network] name or full ID of network |
| until | [DateTime] Containers created before the given duration or time. |
@@option latest @@option latest

View File

@ -49,6 +49,7 @@ Valid filters are listed below:
| health | [Status] healthy or unhealthy | | health | [Status] healthy or unhealthy |
| pod | [Pod] name or full or partial ID of pod | | pod | [Pod] name or full or partial ID of pod |
| network | [Network] name or full ID of network | | network | [Network] name or full ID of network |
| until | [DateTime] Containers created before the given duration or time. |
#### **--force**, **-f** #### **--force**, **-f**

View File

@ -50,6 +50,7 @@ Valid filters are listed below:
| health | [Status] healthy or unhealthy | | health | [Status] healthy or unhealthy |
| pod | [Pod] name or full or partial ID of pod | | pod | [Pod] name or full or partial ID of pod |
| network | [Network] name or full ID of network | | network | [Network] name or full ID of network |
| until | [DateTime] Containers created before the given duration or time. |
@@option interactive @@option interactive

View File

@ -48,6 +48,7 @@ Valid filters are listed below:
| health | [Status] healthy or unhealthy | | health | [Status] healthy or unhealthy |
| pod | [Pod] name or full or partial ID of pod | | pod | [Pod] name or full or partial ID of pod |
| network | [Network] name or full ID of network | | network | [Network] name or full ID of network |
| until | [DateTime] Containers created before the given duration or time. |
@@option ignore @@option ignore

View File

@ -42,6 +42,7 @@ Valid filters are listed below:
| health | [Status] healthy or unhealthy | | health | [Status] healthy or unhealthy |
| pod | [Pod] name or full or partial ID of pod | | pod | [Pod] name or full or partial ID of pod |
| network | [Network] name or full ID of network | | network | [Network] name or full ID of network |
| until | [DateTime] Containers created before the given duration or time. |
@@option latest @@option latest

View File

@ -23,11 +23,16 @@ The *filters* argument format is of `key=value`. If there is more than one *filt
Supported filters: Supported filters:
| Filter | Description | | Filter | Description |
| :----------------: | --------------------------------------------------------------------------- | |:-------------:|------------------------------------------------------------------------------------------------------------|
| *label* | Only remove volumes, with (or without, in the case of label!=[...] is used) the specified labels. | | *dangling* | [Bool] Only remove volumes not referenced by any containers |
| *until* | Only remove volumes created before given timestamp. | | *driver* | [String] Only remove volumes with the given driver |
| *after/since* | Filter by volumes created after the given VOLUME (name or tag) | | *label* | [String] Only remove volumes, with (or without, in the case of label!=[...] is used) the specified labels. |
| *name* | [String] Only remove volume with the given name |
| *opt* | [String] Only remove volumes created with the given options |
| *scope* | [String] Only remove volumes with the given scope |
| *until* | [DateTime] Only remove volumes created before given timestamp. |
| *after/since* | [Volume] Filter by volumes created after the given VOLUME (name or tag) |
The `label` *filter* accepts two formats. One is the `label`=*key* or `label`=*key*=*value*, which removes volumes with the specified labels. The other format is the `label!`=*key* or `label!`=*key*=*value*, which removes volumes without the specified labels. The `label` *filter* accepts two formats. One is the `label`=*key* or `label`=*key*=*value*, which removes volumes with the specified labels. The other format is the `label!`=*key* or `label!`=*key*=*value*, which removes volumes without the specified labels.

View File

@ -203,6 +203,13 @@ sub xref_by_help {
OPTION: OPTION:
for my $k (sort keys %$help) { for my $k (sort keys %$help) {
if (! ref($man)) {
# Super-unlikely but I've seen it
warn "$ME: 'podman @subcommand' is not documented in man pages!\n";
++$Errs;
next OPTION;
}
if (exists $man->{$k}) { if (exists $man->{$k}) {
if (ref $help->{$k}) { if (ref $help->{$k}) {
# This happens when 'podman foo --format' offers # This happens when 'podman foo --format' offers
@ -233,7 +240,11 @@ sub xref_by_help {
# Nope, it's not that case. # Nope, it's not that case.
my $man = $man->{_path} || 'man'; my $man = $man->{_path} || 'man';
warn "$ME: 'podman @subcommand --help' lists '$k', which is not in $man\n"; # The usual case is "podman ... --help"...
my $what = '--help';
# ...but for *options* (e.g. --filter), we're checking command completion
$what = '<TAB>' if $subcommand[-1] =~ /^--/;
warn "$ME: 'podman @subcommand $what' lists '$k', which is not in $man\n";
++$Errs; ++$Errs;
} }
} }
@ -296,7 +307,10 @@ sub xref_by_man {
# Special case for hidden or external commands # Special case for hidden or external commands
next if $Skip_Subcommand{$k}; next if $Skip_Subcommand{$k};
warn "$ME: 'podman @subcommand': $k in $man, but not --help\n"; # It's not always --help, sometimes we check <TAB> completion
my $what = '--help';
$what = 'command completion' if $subcommand[-1] =~ /^--/;
warn "$ME: 'podman @subcommand': '$k' in $man, but not in $what\n";
++$Errs; ++$Errs;
} }
} }
@ -420,7 +434,17 @@ sub podman_help {
++$Errs; ++$Errs;
} }
} }
} }
# Same thing, for --filter
elsif ($opt eq '--filter') {
my @completions = _completions(@_, '--filter=');
for my $c (@completions) {
if ($c =~ /^(\S+)=/) {
$help{$opt} = {} if ! ref($help{$opt});
$help{$opt}{$1} = 1;
}
}
}
} }
} }
close $fh close $fh
@ -447,9 +471,11 @@ sub podman_man {
my $previous_subcmd = ''; my $previous_subcmd = '';
my $previous_flag = ''; my $previous_flag = '';
my $previous_format = ''; my $previous_format = '';
my $previous_filter = '';
LINE:
while (my $line = <$fh>) { while (my $line = <$fh>) {
chomp $line; chomp $line;
next unless $line; # skip empty lines next LINE unless $line; # skip empty lines
# First line (page title) must match the command name. # First line (page title) must match the command name.
if ($line =~ /^%\s+/) { if ($line =~ /^%\s+/) {
@ -561,7 +587,7 @@ sub podman_man {
} }
# Options with no '=whatever' # Options with no '=whatever'
next if !$line; next LINE if !$line;
# Anything remaining *must* be of the form '=<possibilities>' # Anything remaining *must* be of the form '=<possibilities>'
if ($line !~ /^=/) { if ($line !~ /^=/) {
@ -639,6 +665,56 @@ sub podman_man {
$previous_format = $format; $previous_format = $format;
} }
} }
# Same as above, but with --filter
elsif ($previous_flag eq '--filter') {
if ($line =~ /^\|\s+(\S+)\s+\|/) {
my $filter = $1;
# Special-case transformation: some man pages use
# asterisks, "*foo*", some don't. Ignore asterisks.
$filter =~ tr/\*//d;
# (Garbage: these are just table column titles & dividers)
next LINE if $filter eq 'Filter';
next LINE if $filter =~ /---+/;
# Another special case: treat slash-separated options
# ("after/since") as identical, and require that each
# be documented.
for my $f (split '/', $filter) {
# Special case for negated options ("label!="): allow,
# but only immediately after the positive case.
if ($f =~ s/!$//) {
if ($f ne $previous_filter) {
warn "$ME: $subpath:$.: filter '$f!' only allowed immediately after its positive\n";
++$Errs;
}
next LINE;
}
if (! ref($man{$previous_flag})) {
$man{$previous_flag} = { _path => $subpath };
}
$man{$previous_flag}{$f} = 1;
}
# Sort order check, case-insensitive
# FIXME FIXME! Disabled for now because it would make
# this PR completely impossible to review (as opposed to
# only mostly-impossible)
#if (lc($filter) lt lc($previous_filter)) {
# warn "$ME: $subpath:$.: filter specifier '$filter' should precede '$previous_filter'\n";
# ++$Errs;
#}
# Dup check. Yes, it happens.
if (lc($filter) eq lc($previous_filter)) {
warn "$ME: $subpath:$.: filter specifier '$filter' is a dup\n";
++$Errs;
}
$previous_filter = $filter;
}
}
} }
# It's easy to make mistakes in the SEE ALSO elements. # It's easy to make mistakes in the SEE ALSO elements.