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, )