From 1259d1f4c1c636597f6dd848dcb287e0a5dd6fb6 Mon Sep 17 00:00:00 2001
From: Sorin Sbarnea <ssbarnea@redhat.com>
Date: Mon, 2 Dec 2024 09:33:30 +0000
Subject: [PATCH] Remove formatting options for listing rules

This change removes the --format option from the affecting the
output of --list-rules. That change is not marked as major because
because it only affect output that is not used in automations.

Related: #4396 AAP-36125
---
 src/ansiblelint/__main__.py      | 17 ++----
 src/ansiblelint/cli.py           |  8 ++-
 src/ansiblelint/generate_docs.py | 88 +++-----------------------------
 test/test_rules_collection.py    |  6 +--
 tools/generate_docs.py           |  2 +-
 5 files changed, 18 insertions(+), 103 deletions(-)

diff --git a/src/ansiblelint/__main__.py b/src/ansiblelint/__main__.py
index 7035d86b10..3b3e14eaba 100755
--- a/src/ansiblelint/__main__.py
+++ b/src/ansiblelint/__main__.py
@@ -34,6 +34,7 @@
 
 from ansible_compat.prerun import get_cache_dir
 from filelock import BaseFileLock, FileLock, Timeout
+from rich.markdown import Markdown
 from rich.markup import escape
 
 from ansiblelint.constants import RC, SKIP_SCHEMA_UPDATE
@@ -72,8 +73,6 @@
 if TYPE_CHECKING:
     # RulesCollection must be imported lazily or ansible gets imported too early.
 
-    from collections.abc import Callable
-
     from ansiblelint.rules import RulesCollection
     from ansiblelint.runner import LintResult
 
@@ -166,17 +165,11 @@ def initialize_options(arguments: list[str] | None = None) -> BaseFileLock | Non
 def _do_list(rules: RulesCollection) -> int:
     # On purpose lazy-imports to avoid pre-loading Ansible
     # pylint: disable=import-outside-toplevel
-    from ansiblelint.generate_docs import rules_as_md, rules_as_rich, rules_as_str
+    from ansiblelint.generate_docs import rules_as_str
 
     if options.list_rules:
-        _rule_format_map: dict[str, Callable[..., Any]] = {
-            "brief": rules_as_str,
-            "full": rules_as_rich,
-            "md": rules_as_md,
-        }
-
         console.print(
-            _rule_format_map.get(options.format, rules_as_str)(rules),
+            rules_as_str(rules),
             highlight=False,
         )
         return 0
@@ -339,9 +332,9 @@ def main(argv: list[str] | None = None) -> int:
     from ansiblelint.rules import RulesCollection
 
     if options.list_profiles:
-        from ansiblelint.generate_docs import profiles_as_rich
+        from ansiblelint.generate_docs import profiles_as_md
 
-        console.print(profiles_as_rich())
+        console.print(Markdown(profiles_as_md()))
         return 0
 
     app = get_app(
diff --git a/src/ansiblelint/cli.py b/src/ansiblelint/cli.py
index 8753fea273..379c446a04 100644
--- a/src/ansiblelint/cli.py
+++ b/src/ansiblelint/cli.py
@@ -255,7 +255,7 @@ def get_cli_parser() -> argparse.ArgumentParser:
         dest="list_profiles",
         default=False,
         action="store_true",
-        help="List all profiles, no formatting options available.",
+        help="List all profiles.",
     )
     listing_group.add_argument(
         "-L",
@@ -263,16 +263,14 @@ def get_cli_parser() -> argparse.ArgumentParser:
         dest="list_rules",
         default=False,
         action="store_true",
-        help="List all the rules. For listing rules only the following formats "
-        "for argument -f are supported: {brief, full, md} with 'brief' as default.",
+        help="List all the rules.",
     )
     listing_group.add_argument(
         "-T",
         "--list-tags",
         dest="list_tags",
         action="store_true",
-        help="List all the tags and the rules they cover. Increase the verbosity level "
-        "with `-v` to include 'opt-in' tag and its rules.",
+        help="List all the tags and the rules they cover.",
     )
     parser.add_argument(
         "-f",
diff --git a/src/ansiblelint/generate_docs.py b/src/ansiblelint/generate_docs.py
index a6f8a1d39d..5973f50dc0 100644
--- a/src/ansiblelint/generate_docs.py
+++ b/src/ansiblelint/generate_docs.py
@@ -1,95 +1,26 @@
 """Utils to generate rules documentation."""
 
-import logging
-from collections.abc import Iterable
-
-from rich import box
-from rich.console import RenderableType, group
-from rich.markdown import Markdown
-from rich.table import Table
-
 from ansiblelint.config import PROFILES
 from ansiblelint.constants import RULE_DOC_URL
 from ansiblelint.rules import RulesCollection, TransformMixin
 
-DOC_HEADER = """
-# Default Rules
-
-(lint_default_rules)=
-
-Below you can see the list of default rules Ansible Lint use to evaluate playbooks and roles:
-
-"""
 
-_logger = logging.getLogger(__name__)
-
-
-def rules_as_str(rules: RulesCollection) -> RenderableType:
+def rules_as_str(rules: RulesCollection) -> str:
     """Return rules as string."""
-    table = Table(show_header=False, header_style="title", box=box.SIMPLE)
+    result = ""
     for rule in rules.alphabetical():
         if issubclass(rule.__class__, TransformMixin):
             rule.tags.insert(0, "autofix")
-        tag = f"[dim] ({', '.join(rule.tags)})[/dim]" if rule.tags else ""
-        table.add_row(
-            f"[link={RULE_DOC_URL}{rule.id}/]{rule.id}[/link]",
-            rule.shortdesc + tag,
-        )
-    return table
+        tag = f"{','.join(rule.tags)}" if rule.tags else ""
+        result += f"- [repr.url][link={RULE_DOC_URL}{rule.id}/]{rule.id}[/link][/] {rule.shortdesc}\n[dim]  tags:{tag}[/dim]"
 
+        if rule.version_changed and rule.version_changed != "historic":
+            result += f"[dim] modified:{rule.version_changed}[/]"
 
-def rules_as_md(rules: RulesCollection) -> str:
-    """Return md documentation for a list of rules."""
-    result = DOC_HEADER
-
-    for rule in rules.alphabetical():
-        # because title == rule.id we get the desired labels for free
-        # and we do not have to insert `(target_header)=`
-        title = f"{rule.id}"
-
-        if rule.help:
-            if not rule.help.startswith(f"# {rule.id}"):  # pragma: no cover
-                msg = f"Rule {rule.__class__} markdown help does not start with `# {rule.id}` header.\n{rule.help}"
-                raise RuntimeError(msg)
-            result += f"\n\n{rule.help}"
-        else:
-            description = rule.description
-            if rule.link:
-                description += f" [more]({rule.link})"
-
-            result += f"\n\n## {title}\n\n**{rule.shortdesc}**\n\n{description}"
-
-        # Safety net for preventing us from adding autofix to rules and
-        # forgetting to mention it inside their documentation.
-        if "autofix" in rule.tags and "autofix" not in rule.description:
-            msg = f"Rule {rule.id} is invalid because it has 'autofix' tag but this ability is not documented in its description."
-            raise RuntimeError(msg)
-
+        result += " \n"
     return result
 
 
-@group()
-def rules_as_rich(rules: RulesCollection) -> Iterable[Table]:
-    """Print documentation for a list of rules, returns empty string."""
-    width = max(16, *[len(rule.id) for rule in rules])
-    for rule in rules.alphabetical():
-        table = Table(show_header=True, header_style="title", box=box.MINIMAL)
-        table.add_column(rule.id, style="dim", width=width)
-        table.add_column(Markdown(rule.shortdesc))
-
-        description = rule.help or rule.description
-        if rule.link:
-            description += f" [(more)]({rule.link})"
-        table.add_row("description", Markdown(description))
-        if rule.version_changed:
-            table.add_row("version_changed", rule.version_changed)
-        if rule.tags:
-            table.add_row("tags", ", ".join(rule.tags))
-        if rule.severity:
-            table.add_row("severity", rule.severity)
-        yield table
-
-
 def profiles_as_md(*, header: bool = False, docs_url: str = RULE_DOC_URL) -> str:
     """Return markdown representation of supported profiles."""
     result = ""
@@ -127,8 +58,3 @@ def profiles_as_md(*, header: bool = False, docs_url: str = RULE_DOC_URL) -> str
 
         result += "\n"
     return result
-
-
-def profiles_as_rich() -> Markdown:
-    """Return rich representation of supported profiles."""
-    return Markdown(profiles_as_md())
diff --git a/test/test_rules_collection.py b/test/test_rules_collection.py
index ba2ac5099f..fd053cff3d 100644
--- a/test/test_rules_collection.py
+++ b/test/test_rules_collection.py
@@ -140,21 +140,19 @@ def test_no_duplicate_rule_ids() -> None:
     assert not any(y > 1 for y in collections.Counter(rule_ids).values())
 
 
-def test_rich_rule_listing() -> None:
+def test_rule_listing() -> None:
     """Test that rich list format output is rendered as a table.
 
     This check also offers the contract of having rule id, short and long
     descriptions in the console output.
     """
     rules_path = Path("./test/rules/fixtures").resolve()
-    result = run_ansible_lint("-r", str(rules_path), "-f", "full", "-L")
+    result = run_ansible_lint("-r", str(rules_path), "-L")
     assert result.returncode == 0
 
     for rule in RulesCollection([rules_path]):
         assert rule.id in result.stdout
         assert rule.shortdesc in result.stdout
-        # description could wrap inside table, so we do not check full length
-        assert rule.description[:30] in result.stdout
 
 
 def test_rules_id_format(config_options: Options) -> None:
diff --git a/tools/generate_docs.py b/tools/generate_docs.py
index 5bbf160332..29e5066b53 100755
--- a/tools/generate_docs.py
+++ b/tools/generate_docs.py
@@ -12,7 +12,7 @@
 
 if __name__ == "__main__":
     subprocess.run(  # noqa: S603
-        ["ansible-lint", "-L", "--format", "md"],  # noqa: S607
+        ["ansible-lint", "--list-rules"],  # noqa: S607
         check=True,
         stdout=subprocess.DEVNULL,
     )