diff --git a/changelogs/fragments/73602-ansible-doc-version-added.yml b/changelogs/fragments/73602-ansible-doc-version-added.yml new file mode 100644 index 00000000000..18eb028c2a7 --- /dev/null +++ b/changelogs/fragments/73602-ansible-doc-version-added.yml @@ -0,0 +1,2 @@ +minor_changes: +- "ansible-doc - show ``version_added`` for the plugin/module itself in text output, and improve ``version_added`` formatting (https://github.com/ansible/ansible/pull/73602)." \ No newline at end of file diff --git a/lib/ansible/cli/doc.py b/lib/ansible/cli/doc.py index 6bd287cc8e7..a215a383852 100644 --- a/lib/ansible/cli/doc.py +++ b/lib/ansible/cli/doc.py @@ -982,6 +982,17 @@ class DocCLI(CLI, RoleMixin): def _dump_yaml(struct, indent): return DocCLI.tty_ify('\n'.join([indent + line for line in yaml.dump(struct, default_flow_style=False, Dumper=AnsibleDumper).split('\n')])) + @staticmethod + def _format_version_added(version_added, version_added_collection=None): + if version_added_collection == 'ansible.builtin': + version_added_collection = 'ansible-core' + # In ansible-core, version_added can be 'historical' + if version_added == 'historical': + return 'historical' + if version_added_collection: + version_added = '%s of %s' % (version_added, version_added_collection) + return 'version %s' % (version_added, ) + @staticmethod def add_fields(text, fields, limit, opt_indent, return_values=False, base_indent=''): @@ -1057,9 +1068,8 @@ class DocCLI(CLI, RoleMixin): if conf: text.append(DocCLI._dump_yaml({'set_via': conf}, opt_indent)) - # Remove empty version_added_collection - if opt.get('version_added_collection') == '': - opt.pop('version_added_collection') + version_added = opt.pop('version_added', None) + version_added_collection = opt.pop('version_added_collection', None) for k in sorted(opt): if k.startswith('_'): @@ -1074,6 +1084,9 @@ class DocCLI(CLI, RoleMixin): else: text.append(DocCLI._dump_yaml({k: opt[k]}, opt_indent)) + if version_added: + text.append("%sadded in: %s\n" % (opt_indent, DocCLI._format_version_added(version_added, version_added_collection))) + for subkey, subdata in suboptions: text.append('') text.append("%s%s:\n" % (opt_indent, subkey.upper())) @@ -1167,6 +1180,11 @@ class DocCLI(CLI, RoleMixin): text.append("%s\n" % textwrap.fill(DocCLI.tty_ify(desc), limit, initial_indent=opt_indent, subsequent_indent=opt_indent)) + if 'version_added' in doc: + version_added = doc.pop('version_added') + version_added_collection = doc.pop('version_added_collection', None) + text.append("ADDED IN: %s\n" % DocCLI._format_version_added(version_added, version_added_collection)) + if doc.get('deprecated', False): text.append("DEPRECATED: \n") if isinstance(doc['deprecated'], dict): diff --git a/test/integration/targets/ansible-doc/fakemodule.output b/test/integration/targets/ansible-doc/fakemodule.output index 01070fd50b7..5548ad5ecef 100644 --- a/test/integration/targets/ansible-doc/fakemodule.output +++ b/test/integration/targets/ansible-doc/fakemodule.output @@ -2,6 +2,8 @@ this is a fake module +ADDED IN: version 1.0.0 of testns.testcol + OPTIONS (= is mandatory): - _notreal @@ -12,5 +14,3 @@ OPTIONS (= is mandatory): AUTHOR: me SHORT_DESCIPTION: fake module - -VERSION_ADDED_COLLECTION: testns.testcol diff --git a/test/integration/targets/ansible-doc/randommodule-text.output b/test/integration/targets/ansible-doc/randommodule-text.output new file mode 100644 index 00000000000..24327a59552 --- /dev/null +++ b/test/integration/targets/ansible-doc/randommodule-text.output @@ -0,0 +1,105 @@ +> TESTNS.TESTCOL.RANDOMMODULE (./collections/ansible_collections/testns/testcol/plugins/modules/randommodule.py) + + A random module. + +ADDED IN: version 1.0.0 of testns.testcol + +DEPRECATED: + + Reason: Test deprecation + Will be removed in: Ansible 3.0.0 + Alternatives: Use some other module + + +OPTIONS (= is mandatory): + +- sub + Suboptions. + [Default: (null)] + set_via: + env: + - deprecated: + alternative: none + removed_in: 2.0.0 + version: 2.0.0 + why: Test deprecation + name: TEST_ENV + + type: dict + + OPTIONS: + + - subtest2 + Another suboption. + [Default: (null)] + type: float + added in: version 1.1.0 + + + + SUBOPTIONS: + + - subtest + A suboption. + [Default: (null)] + type: int + added in: version 1.1.0 of testns.testcol + + +- test + Some text. + [Default: (null)] + type: str + added in: version 1.2.0 of testns.testcol + + +- testcol2option + An option taken from testcol2 + [Default: (null)] + type: str + added in: version 1.0.0 of testns.testcol2 + + +- testcol2option2 + Another option taken from testcol2 + [Default: (null)] + type: str + + +AUTHOR: Ansible Core Team + +EXAMPLES: + + + + +RETURN VALUES: +- a_first + A first result. + + returned: success + type: str + +- m_middle + This should be in the middle. + Has some more data + + returned: success and 1st of month + type: dict + + CONTAINS: + + - suboption + A suboption. + (Choices: ARF, BARN, c_without_capital_first_letter) + type: str + added in: version 1.4.0 of testns.testcol + + +- z_last + A last result. + + returned: success + type: str + added in: version 1.3.0 of testns.testcol + diff --git a/test/integration/targets/ansible-doc/runme.sh b/test/integration/targets/ansible-doc/runme.sh index 4f40d7c38d1..4a6f6820bcd 100755 --- a/test/integration/targets/ansible-doc/runme.sh +++ b/test/integration/targets/ansible-doc/runme.sh @@ -19,6 +19,11 @@ current_out="$(ansible-doc --playbook-dir ./ testns.testcol.fakemodule | sed '1 expected_out="$(sed '1 s/\(^> TESTNS\.TESTCOL\.FAKEMODULE\).*(.*)$/\1/' fakemodule.output)" test "$current_out" == "$expected_out" +# we use sed to strip the module path from the first line +current_out="$(ansible-doc --playbook-dir ./ testns.testcol.randommodule | sed '1 s/\(^> TESTNS\.TESTCOL\.RANDOMMODULE\).*(.*)$/\1/')" +expected_out="$(sed '1 s/\(^> TESTNS\.TESTCOL\.RANDOMMODULE\).*(.*)$/\1/' randommodule-text.output)" +test "$current_out" == "$expected_out" + # ensure we do work with valid collection name for list ansible-doc --list testns.testcol --playbook-dir ./ 2>&1 | grep -v "Invalid collection pattern"