expand doc snippets to lookups and other plugins (#74403)
Co-authored-by: Sviatoslav Sydorenko <wk.cvs.github@sydorenko.org.ua> Co-authored-by: Toshio Kuratomi <a.badger@gmail.com>
This commit is contained in:
parent
004c33d9c5
commit
6d2398db28
2 changed files with 105 additions and 29 deletions
2
changelogs/fragments/snippets.yml
Normal file
2
changelogs/fragments/snippets.yml
Normal file
|
@ -0,0 +1,2 @@
|
||||||
|
minor_changes:
|
||||||
|
- ansbile-doc now also shows snippets for inventory and lookup, adding to existing modules.
|
|
@ -47,6 +47,7 @@ display = Display()
|
||||||
TARGET_OPTIONS = C.DOCUMENTABLE_PLUGINS + ('role', 'keyword',)
|
TARGET_OPTIONS = C.DOCUMENTABLE_PLUGINS + ('role', 'keyword',)
|
||||||
PB_OBJECTS = ['Play', 'Role', 'Block', 'Task']
|
PB_OBJECTS = ['Play', 'Role', 'Block', 'Task']
|
||||||
PB_LOADED = {}
|
PB_LOADED = {}
|
||||||
|
SNIPPETS = ['inventory', 'lookup', 'module']
|
||||||
|
|
||||||
|
|
||||||
def jdump(text):
|
def jdump(text):
|
||||||
|
@ -400,32 +401,42 @@ class DocCLI(CLI, RoleMixin):
|
||||||
opt_help.add_module_options(self.parser)
|
opt_help.add_module_options(self.parser)
|
||||||
opt_help.add_basedir_options(self.parser)
|
opt_help.add_basedir_options(self.parser)
|
||||||
|
|
||||||
|
# targets
|
||||||
self.parser.add_argument('args', nargs='*', help='Plugin', metavar='plugin')
|
self.parser.add_argument('args', nargs='*', help='Plugin', metavar='plugin')
|
||||||
|
|
||||||
self.parser.add_argument("-t", "--type", action="store", default='module', dest='type',
|
self.parser.add_argument("-t", "--type", action="store", default='module', dest='type',
|
||||||
help='Choose which plugin type (defaults to "module"). '
|
help='Choose which plugin type (defaults to "module"). '
|
||||||
'Available plugin types are : {0}'.format(TARGET_OPTIONS),
|
'Available plugin types are : {0}'.format(TARGET_OPTIONS),
|
||||||
choices=TARGET_OPTIONS)
|
choices=TARGET_OPTIONS)
|
||||||
|
|
||||||
|
# formatting
|
||||||
self.parser.add_argument("-j", "--json", action="store_true", default=False, dest='json_format',
|
self.parser.add_argument("-j", "--json", action="store_true", default=False, dest='json_format',
|
||||||
help='Change output into json format.')
|
help='Change output into json format.')
|
||||||
|
|
||||||
|
# TODO: warn if not used with -t roles
|
||||||
# role-specific options
|
# role-specific options
|
||||||
self.parser.add_argument("-r", "--roles-path", dest='roles_path', default=C.DEFAULT_ROLES_PATH,
|
self.parser.add_argument("-r", "--roles-path", dest='roles_path', default=C.DEFAULT_ROLES_PATH,
|
||||||
type=opt_help.unfrack_path(pathsep=True),
|
type=opt_help.unfrack_path(pathsep=True),
|
||||||
action=opt_help.PrependListAction,
|
action=opt_help.PrependListAction,
|
||||||
help='The path to the directory containing your roles.')
|
help='The path to the directory containing your roles.')
|
||||||
|
|
||||||
|
# modifiers
|
||||||
exclusive = self.parser.add_mutually_exclusive_group()
|
exclusive = self.parser.add_mutually_exclusive_group()
|
||||||
|
# TODO: warn if not used with -t roles
|
||||||
|
exclusive.add_argument("-e", "--entry-point", dest="entry_point",
|
||||||
|
help="Select the entry point for role(s).")
|
||||||
|
|
||||||
|
# TODO: warn with --json as it is incompatible
|
||||||
|
exclusive.add_argument("-s", "--snippet", action="store_true", default=False, dest='show_snippet',
|
||||||
|
help='Show playbook snippet for these plugin types: %s' % ', '.join(SNIPPETS))
|
||||||
|
|
||||||
|
# TODO: warn when arg/plugin is passed
|
||||||
exclusive.add_argument("-F", "--list_files", action="store_true", default=False, dest="list_files",
|
exclusive.add_argument("-F", "--list_files", action="store_true", default=False, dest="list_files",
|
||||||
help='Show plugin names and their source files without summaries (implies --list). %s' % coll_filter)
|
help='Show plugin names and their source files without summaries (implies --list). %s' % coll_filter)
|
||||||
exclusive.add_argument("-l", "--list", action="store_true", default=False, dest='list_dir',
|
exclusive.add_argument("-l", "--list", action="store_true", default=False, dest='list_dir',
|
||||||
help='List available plugins. %s' % coll_filter)
|
help='List available plugins. %s' % coll_filter)
|
||||||
exclusive.add_argument("-s", "--snippet", action="store_true", default=False, dest='show_snippet',
|
|
||||||
help='Show playbook snippet for specified plugin(s)')
|
|
||||||
exclusive.add_argument("--metadata-dump", action="store_true", default=False, dest='dump',
|
exclusive.add_argument("--metadata-dump", action="store_true", default=False, dest='dump',
|
||||||
help='**For internal testing only** Dump json metadata for all plugins.')
|
help='**For internal testing only** Dump json metadata for all plugins.')
|
||||||
exclusive.add_argument("-e", "--entry-point", dest="entry_point",
|
|
||||||
help="Select the entry point for role(s).")
|
|
||||||
|
|
||||||
def post_process_args(self, options):
|
def post_process_args(self, options):
|
||||||
options = super(DocCLI, self).post_process_args(options)
|
options = super(DocCLI, self).post_process_args(options)
|
||||||
|
@ -785,7 +796,6 @@ class DocCLI(CLI, RoleMixin):
|
||||||
result = loader.find_plugin_with_context(plugin, mod_type='.py', ignore_deprecated=True, check_aliases=True)
|
result = loader.find_plugin_with_context(plugin, mod_type='.py', ignore_deprecated=True, check_aliases=True)
|
||||||
if not result.resolved:
|
if not result.resolved:
|
||||||
raise PluginNotFound('%s was not found in %s' % (plugin, search_paths))
|
raise PluginNotFound('%s was not found in %s' % (plugin, search_paths))
|
||||||
plugin_name = result.plugin_resolved_name
|
|
||||||
filename = result.plugin_resolved_path
|
filename = result.plugin_resolved_path
|
||||||
collection_name = result.plugin_resolved_collection
|
collection_name = result.plugin_resolved_collection
|
||||||
|
|
||||||
|
@ -829,8 +839,13 @@ class DocCLI(CLI, RoleMixin):
|
||||||
doc['returndocs'] = returndocs
|
doc['returndocs'] = returndocs
|
||||||
doc['metadata'] = metadata
|
doc['metadata'] = metadata
|
||||||
|
|
||||||
if context.CLIARGS['show_snippet'] and plugin_type == 'module':
|
if context.CLIARGS['show_snippet']:
|
||||||
text = DocCLI.get_snippet_text(doc)
|
if plugin_type not in SNIPPETS:
|
||||||
|
raise AnsibleError("Snippets are only available for the following plugin types: %s" % ', '.join(SNIPPETS))
|
||||||
|
if plugin_type == 'inventory' and plugin in ('yaml', 'toml'):
|
||||||
|
# not usable as other inventory plugins
|
||||||
|
del doc['options']
|
||||||
|
text = DocCLI.get_snippet_text(doc, plugin_type)
|
||||||
else:
|
else:
|
||||||
try:
|
try:
|
||||||
text = DocCLI.get_man_text(doc, collection_name, plugin_type)
|
text = DocCLI.get_man_text(doc, collection_name, plugin_type)
|
||||||
|
@ -948,32 +963,16 @@ class DocCLI(CLI, RoleMixin):
|
||||||
return os.pathsep.join(ret)
|
return os.pathsep.join(ret)
|
||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def get_snippet_text(doc):
|
def get_snippet_text(doc, ptype='module'):
|
||||||
|
''' return heavily commented plugin use to insert into play '''
|
||||||
text = []
|
text = []
|
||||||
desc = DocCLI.tty_ify(doc['short_description'])
|
|
||||||
text.append("- name: %s" % (desc))
|
|
||||||
text.append(" %s:" % (doc['module']))
|
|
||||||
pad = 31
|
|
||||||
subdent = " " * pad
|
|
||||||
limit = display.columns - pad
|
|
||||||
|
|
||||||
for o in sorted(doc['options'].keys()):
|
if ptype == 'lookup':
|
||||||
opt = doc['options'][o]
|
_do_lookup_snippet(text, doc)
|
||||||
if isinstance(opt['description'], string_types):
|
elif 'options' in doc:
|
||||||
desc = DocCLI.tty_ify(opt['description'])
|
_do_yaml_snippet(text, doc)
|
||||||
else:
|
|
||||||
desc = DocCLI.tty_ify(" ".join(opt['description']))
|
|
||||||
|
|
||||||
required = opt.get('required', False)
|
|
||||||
if not isinstance(required, bool):
|
|
||||||
raise("Incorrect value for 'Required', a boolean is needed.: %s" % required)
|
|
||||||
if required:
|
|
||||||
desc = "(required) %s" % desc
|
|
||||||
o = '%s:' % o
|
|
||||||
text.append(" %-20s # %s" % (o, textwrap.fill(desc, limit, subsequent_indent=subdent)))
|
|
||||||
text.append('')
|
text.append('')
|
||||||
|
|
||||||
return "\n".join(text)
|
return "\n".join(text)
|
||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
|
@ -1260,3 +1259,78 @@ class DocCLI(CLI, RoleMixin):
|
||||||
DocCLI.add_fields(text, doc.pop('returndocs'), limit, opt_indent, return_values=True)
|
DocCLI.add_fields(text, doc.pop('returndocs'), limit, opt_indent, return_values=True)
|
||||||
|
|
||||||
return "\n".join(text)
|
return "\n".join(text)
|
||||||
|
|
||||||
|
|
||||||
|
def _do_yaml_snippet(text, doc):
|
||||||
|
|
||||||
|
mdesc = DocCLI.tty_ify(doc['short_description'])
|
||||||
|
module = doc.get('module')
|
||||||
|
if module:
|
||||||
|
# this is actually a usable task!
|
||||||
|
text.append("- name: %s" % (mdesc))
|
||||||
|
text.append(" %s:" % (module))
|
||||||
|
else:
|
||||||
|
# just a comment, hopefully useful yaml file
|
||||||
|
text.append("# %s:" % doc.get('plugin', doc.get('name')))
|
||||||
|
|
||||||
|
pad = 29
|
||||||
|
subdent = '# '.rjust(pad + 2)
|
||||||
|
limit = display.columns - pad
|
||||||
|
|
||||||
|
for o in sorted(doc['options'].keys()):
|
||||||
|
opt = doc['options'][o]
|
||||||
|
if isinstance(opt['description'], string_types):
|
||||||
|
desc = DocCLI.tty_ify(opt['description'])
|
||||||
|
else:
|
||||||
|
desc = DocCLI.tty_ify(" ".join(opt['description']))
|
||||||
|
|
||||||
|
required = opt.get('required', False)
|
||||||
|
if not isinstance(required, bool):
|
||||||
|
raise("Incorrect value for 'Required', a boolean is needed.: %s" % required)
|
||||||
|
|
||||||
|
o = '%s:' % o
|
||||||
|
if module:
|
||||||
|
if required:
|
||||||
|
desc = "(required) %s" % desc
|
||||||
|
text.append(" %-20s # %s" % (o, textwrap.fill(desc, limit, subsequent_indent=subdent)))
|
||||||
|
else:
|
||||||
|
if required:
|
||||||
|
default = '(required)'
|
||||||
|
else:
|
||||||
|
default = opt.get('default', 'None')
|
||||||
|
|
||||||
|
text.append("%s %-9s # %s" % (o, default, textwrap.fill(desc, limit, subsequent_indent=subdent, max_lines=3)))
|
||||||
|
|
||||||
|
|
||||||
|
def _do_lookup_snippet(text, doc):
|
||||||
|
|
||||||
|
snippet = "lookup('%s', " % doc.get('plugin', doc.get('name'))
|
||||||
|
comment = []
|
||||||
|
for o in sorted(doc['options'].keys()):
|
||||||
|
|
||||||
|
opt = doc['options'][o]
|
||||||
|
comment.append('# %s(%s): %s' % (o, opt.get('type', 'string'), opt.get('description', '')))
|
||||||
|
if o in ('_terms', '_raw', '_list'):
|
||||||
|
# these are 'list of arguments'
|
||||||
|
snippet += '< %s >' % (o)
|
||||||
|
continue
|
||||||
|
|
||||||
|
required = opt.get('required', False)
|
||||||
|
if not isinstance(required, bool):
|
||||||
|
raise("Incorrect value for 'Required', a boolean is needed.: %s" % required)
|
||||||
|
|
||||||
|
if required:
|
||||||
|
default = '<REQUIRED>'
|
||||||
|
else:
|
||||||
|
default = opt.get('default', 'None')
|
||||||
|
|
||||||
|
if opt.get('type') in ('string', 'str'):
|
||||||
|
snippet += ", %s='%s'" % (o, default)
|
||||||
|
else:
|
||||||
|
snippet += ', %s=%s' % (o, default)
|
||||||
|
|
||||||
|
snippet += ")"
|
||||||
|
if comment:
|
||||||
|
text.extend(comment)
|
||||||
|
text.append('')
|
||||||
|
text.append(snippet)
|
||||||
|
|
Loading…
Reference in a new issue