Problem trying to use ansible-docs on Debian Bookworm

Hi @kpfleming thanks for your work on this tool.

I'm trying to use ansible-docs but haven't had much luck, when in a role directory am I right in understanding that the minimal command that can be used is as follows?

ansible-docs . markdown

Or have I misunderstood something?

The result I get is:

Traceback (most recent call last):
  File "/home/chris/.local/bin/ansible-docs", line 8, in <module>
    sys.exit(app())
             ^^^^^
  File "/home/chris/.local/lib/python3.11/site-packages/typer/main.py", line 214, in __call__
    return get_command(self)(*args, **kwargs)
           ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/usr/lib/python3/dist-packages/click/core.py", line 1130, in __call__
    return self.main(*args, **kwargs)
           ^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/usr/lib/python3/dist-packages/click/core.py", line 1055, in main
    rv = self.invoke(ctx)
         ^^^^^^^^^^^^^^^^
  File "/usr/lib/python3/dist-packages/click/core.py", line 1657, in invoke
    return _process_result(sub_ctx.command.invoke(sub_ctx))
                           ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/usr/lib/python3/dist-packages/click/core.py", line 1404, in invoke
    return ctx.invoke(self.callback, **ctx.params)
           ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/usr/lib/python3/dist-packages/click/core.py", line 760, in invoke
    return __callback(*args, **kwargs)
           ^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/home/chris/.local/lib/python3.11/site-packages/typer/main.py", line 532, in wrapper
    return callback(**use_params)  # type: ignore
           ^^^^^^^^^^^^^^^^^^^^^^
  File "/home/chris/.local/lib/python3.11/site-packages/ansible_docs/cli.py", line 44, in markdown
    content = render_content(ctx, "markdown.j2")
              ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/home/chris/.local/lib/python3.11/site-packages/ansible_docs/cli.py", line 166, in render_content
    content = env.get_template(content_template).render(
              ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/home/chris/.local/lib/python3.11/site-packages/jinja2/environment.py", line 1010, in get_template
    return self._load_template(name, globals)
           ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/home/chris/.local/lib/python3.11/site-packages/jinja2/environment.py", line 969, in _load_template
    template = self.loader.load(self, name, self.make_globals(globals))
               ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/home/chris/.local/lib/python3.11/site-packages/jinja2/loaders.py", line 126, in load
    source, filename, uptodate = self.get_source(environment, name)
                                 ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/home/chris/.local/lib/python3.11/site-packages/jinja2/loaders.py", line 218, in get_source
    raise TemplateNotFound(template)
jinja2.exceptions.TemplateNotFound: markdown.j2

I running Debian Bookworm with Python 3.11.1.

This is how I initially installed it:

pip install --user git+https://gitlab.com/kankare/ansible-docs 
Collecting git+https://gitlab.com/kankare/ansible-docs
  Cloning https://gitlab.com/kankare/ansible-docs to /tmp/pip-req-build-5qx99akz
  Running command git clone --filter=blob:none --quiet https://gitlab.com/kankare/ansible-docs /tmp/pip-req-build-5qx99akz
  warning: redirecting to https://gitlab.com/kankare/ansible-docs.git/
  Resolved https://gitlab.com/kankare/ansible-docs to commit cd2b2d8f2275d8ea8a52dc3e53d79420c6c5b889      /home/chris/webarch/m
  Installing build dependencies ... done
  Getting requirements to build wheel ... done       "stat": {"a
  Preparing metadata (pyproject.toml) ... done       utes": ["ex
Collecting typer<0.5.0,>=0.4.1
  Downloading typer-0.4.2-py3-none-any.whl (27 kB)   a623821e636
Collecting Jinja2<4.0.0,>=3.1.2
  Using cached Jinja2-3.1.2-py3-none-any.whl (133 kB) "executabl
Requirement already satisfied: PyYAML<7.0,>=6.0 in /usr/lib/python3/dist-packages (from ansible-docs==0.1.0) (6.0)                     ctory present] *********"
Requirement already satisfied: click<9.0.0,>=8.1.3 in /usr/lib/python3/dist-packages (from ansible-docs==0.1.0) (8.1.3)                ] => {"changed": false, e
Requirement already satisfied: MarkupSafe>=2.0 in /usr/lib/python3/dist-packages (from Jinja2<4.0.0,>=3.1.2->ansible-docs==0.1.0) (2.1.2)
Building wheels for collected packages: ansible-docs
  Building wheel for ansible-docs (pyproject.toml) ... done
  Created wheel for ansible-docs: filename=ansible_docs-0.1.0-py3-none-any.whl size=4148 sha256=2edb2e647ac37b439eeb126bde8911095b7de0a2877535fef363acf517870605
  Stored in directory: /tmp/pip-ephem-wheel-cache-fdiao98d/wheels/69/e1/11/40635c6ba913937d4c2dfc620807fde4e7ac008492850f2408
Successfully built ansible-docs
Installing collected packages: typer, Jinja2, ansible-docs
Successfully installed Jinja2-3.1.2 ansible-docs-0.1.0 typer-0.4.2

I then uninstalled the user typer package and installed the system one:

pip3 uninstall typer
Found existing installation: typer 0.4.2
Uninstalling typer-0.4.2:
  Would remove:
    /home/chris/.local/lib/python3.11/site-packages/typer-0.4.2.dist-info/*
    /home/chris/.local/lib/python3.11/site-packages/typer/*
Proceed (Y/n)? y
  Successfully uninstalled typer-0.4.2

sudo apt install python3-typer

Now I have this colourfull result:

ansible-docs . markdown
╭─────────────────────────────── Traceback (most recent call last) ────────────────────────────────╮
│ /home/chris/.local/lib/python3.11/site-packages/ansible_docs/cli.py:44 in markdown               │
│                                                                                                  │
│    41 │   """                                                                                    │
│    42 │                                                                                          │
│    43 │   # FIXME: ctx should not be passed to these non-command()'s                             │
│ ❱  44 │   content = render_content(ctx, "markdown.j2")                                           │
│    45 │   write(ctx, content)                                                                    │
│    46                                                                                            │
│    47                                                                                            │
│                                                                                                  │
│ ╭────────────────────── locals ───────────────────────╮                                          │
│ │ ctx = <click.core.Context object at 0x7f05196edd90> │                                          │
│ ╰─────────────────────────────────────────────────────╯                                          │
│                                                                                                  │
│ /home/chris/.local/lib/python3.11/site-packages/ansible_docs/cli.py:166 in render_content        │
│                                                                                                  │
│   163 │   loader = jinja2.FileSystemLoader(templates)                                            │
│   164 │                                                                                          │
│   165 │   env = jinja2.Environment(loader=loader)                                                │
│ ❱ 166 │   content = env.get_template(content_template).render(                                   │
│   167 │   │   role=ctx.obj["config"]["role"],                                                    │
│   168 │   │   metadata=ctx.obj["data"]["metadata"],                                              │
│   169 │   │   argument_specs=ctx.obj["data"]["argument_specs"],                                  │
│                                                                                                  │
│ ╭───────────────────────────────────────── locals ──────────────────────────────────────────╮    │
│ │ content_template = 'markdown.j2'                                                          │    │
│ │              ctx = <click.core.Context object at 0x7f05196edd90>                          │    │
│ │              env = <jinja2.environment.Environment object at 0x7f0519b24b10>              │    │
│ │           loader = <jinja2.loaders.FileSystemLoader object at 0x7f051970fe90>             │    │
│ │        templates = PosixPath('/home/chris/.local/lib/python3.11/site-packages/templates') │    │
│ ╰───────────────────────────────────────────────────────────────────────────────────────────╯    │
│                                                                                                  │
│ /home/chris/.local/lib/python3.11/site-packages/jinja2/environment.py:1010 in get_template       │
│                                                                                                  │
│   1007 │   │   if parent is not None:                                                            │
│   1008 │   │   │   name = self.join_path(name, parent)                                           │
│   1009 │   │                                                                                     │
│ ❱ 1010 │   │   return self._load_template(name, globals)                                         │
│   1011 │                                                                                         │
│   1012 │   @internalcode                                                                         │
│   1013 │   def select_template(                                                                  │
│                                                                                                  │
│ ╭────────────────────────────── locals ───────────────────────────────╮                          │
│ │ globals = None                                                      │                          │
│ │    name = 'markdown.j2'                                             │                          │
│ │  parent = None                                                      │                          │
│ │    self = <jinja2.environment.Environment object at 0x7f0519b24b10> │                          │
│ ╰─────────────────────────────────────────────────────────────────────╯                          │
│                                                                                                  │
│ /home/chris/.local/lib/python3.11/site-packages/jinja2/environment.py:969 in _load_template      │
│                                                                                                  │
│    966 │   │   │   │                                                                             │
│    967 │   │   │   │   return template                                                           │
│    968 │   │                                                                                     │
│ ❱  969 │   │   template = self.loader.load(self, name, self.make_globals(globals))               │
│    970 │   │                                                                                     │
│    971 │   │   if self.cache is not None:                                                        │
│    972 │   │   │   self.cache[cache_key] = template                                              │
│                                                                                                  │
│ ╭─────────────────────────────────────────── locals ───────────────────────────────────────────╮ │
│ │ cache_key = (                                                                                │ │
│ │             │   <weakref at 0x7f0519707dd0; to 'FileSystemLoader' at 0x7f051970fe90>,        │ │
│ │             │   'markdown.j2'                                                                │ │
│ │             )                                                                                │ │
│ │   globals = None                                                                             │ │
│ │      name = 'markdown.j2'                                                                    │ │
│ │      self = <jinja2.environment.Environment object at 0x7f0519b24b10>                        │ │
│ │  template = None                                                                             │ │
│ ╰──────────────────────────────────────────────────────────────────────────────────────────────╯ │
│                                                                                                  │
│ /home/chris/.local/lib/python3.11/site-packages/jinja2/loaders.py:126 in load                    │
│                                                                                                  │
│   123 │   │                                                                                      │
│   124 │   │   # first we try to get the source for this template together                        │
│   125 │   │   # with the filename and the uptodate function.                                     │
│ ❱ 126 │   │   source, filename, uptodate = self.get_source(environment, name)                    │
│   127 │   │                                                                                      │
│   128 │   │   # try to load the code from the bytecode cache if there is a                       │
│   129 │   │   # bytecode cache configured.                                                       │
│                                                                                                  │
│ ╭─────────────────────────────────────────── locals ───────────────────────────────────────────╮ │
│ │        code = None                                                                           │ │
│ │ environment = <jinja2.environment.Environment object at 0x7f0519b24b10>                      │ │
│ │     globals = ChainMap({}, {'range': <class 'range'>, 'dict': <class 'dict'>, 'lipsum':      │ │
│ │               <function generate_lorem_ipsum at 0x7f051a326480>, 'cycler': <class            │ │
│ │               'jinja2.utils.Cycler'>, 'joiner': <class 'jinja2.utils.Joiner'>, 'namespace':  │ │
│ │               <class 'jinja2.utils.Namespace'>})                                             │ │
│ │        name = 'markdown.j2'                                                                  │ │
│ │        self = <jinja2.loaders.FileSystemLoader object at 0x7f051970fe90>                     │ │
│ ╰──────────────────────────────────────────────────────────────────────────────────────────────╯ │
│                                                                                                  │
│ /home/chris/.local/lib/python3.11/site-packages/jinja2/loaders.py:218 in get_source              │
│                                                                                                  │
│   215 │   │   │                                                                                  │
│   216 │   │   │   # Use normpath to convert Windows altsep to sep.                               │
│   217 │   │   │   return contents, os.path.normpath(filename), uptodate                          │
│ ❱ 218 │   │   raise TemplateNotFound(template)                                                   │
│   219 │                                                                                          │
│   220 │   def list_templates(self) -> t.List[str]:                                               │
│   221 │   │   found = set()                                                                      │
│                                                                                                  │
│ ╭─────────────────────────────────────── locals ────────────────────────────────────────╮        │
│ │ environment = <jinja2.environment.Environment object at 0x7f0519b24b10>               │        │
│ │           f = None                                                                    │        │
│ │    filename = '/home/chris/.local/lib/python3.11/site-packages/templates/markdown.j2' │        │
│ │      pieces = ['markdown.j2']                                                         │        │
│ │  searchpath = '/home/chris/.local/lib/python3.11/site-packages/templates'             │        │
│ │        self = <jinja2.loaders.FileSystemLoader object at 0x7f051970fe90>              │        │
│ │    template = 'markdown.j2'                                                           │        │
│ ╰───────────────────────────────────────────────────────────────────────────────────────╯        │
╰──────────────────────────────────────────────────────────────────────────────────────────────────╯
TemplateNotFound: markdown.j2

So I tried manually downloading markdown.j2:

mkdir /home/chris/.local/lib/python3.11/site-packages/templates
cd /home/chris/.local/lib/python3.11/site-packages/templates 
wget https://gitlab.com/kpfleming/ansible-role-docs/-/raw/main/ansible_role_docs/templates/markdown.j2

And now I have this result:

ansible-docs . markdown
╭─────────────────────────────── Traceback (most recent call last) ────────────────────────────────╮
│ /home/chris/.local/lib/python3.11/site-packages/ansible_docs/cli.py:44 in markdown               │
│                                                                                                  │
│    41 │   """                                                                                    │
│    42 │                                                                                          │
│    43 │   # FIXME: ctx should not be passed to these non-command()'s                             │
│ ❱  44 │   content = render_content(ctx, "markdown.j2")                                           │
│    45 │   write(ctx, content)                                                                    │
│    46                                                                                            │
│    47                                                                                            │
│                                                                                                  │
│ ╭────────────────────── locals ───────────────────────╮                                          │
│ │ ctx = <click.core.Context object at 0x7fd5efe4abd0> │                                          │
│ ╰─────────────────────────────────────────────────────╯                                          │
│                                                                                                  │
│ /home/chris/.local/lib/python3.11/site-packages/ansible_docs/cli.py:166 in render_content        │
│                                                                                                  │
│   163 │   loader = jinja2.FileSystemLoader(templates)                                            │
│   164 │                                                                                          │
│   165 │   env = jinja2.Environment(loader=loader)                                                │
│ ❱ 166 │   content = env.get_template(content_template).render(                                   │
│   167 │   │   role=ctx.obj["config"]["role"],                                                    │
│   168 │   │   metadata=ctx.obj["data"]["metadata"],                                              │
│   169 │   │   argument_specs=ctx.obj["data"]["argument_specs"],                                  │
│                                                                                                  │
│ ╭───────────────────────────────────────── locals ──────────────────────────────────────────╮    │
│ │ content_template = 'markdown.j2'                                                          │    │
│ │              ctx = <click.core.Context object at 0x7fd5efe4abd0>                          │    │
│ │              env = <jinja2.environment.Environment object at 0x7fd5f008e190>              │    │
│ │           loader = <jinja2.loaders.FileSystemLoader object at 0x7fd5efe8fa90>             │    │
│ │        templates = PosixPath('/home/chris/.local/lib/python3.11/site-packages/templates') │    │
│ ╰───────────────────────────────────────────────────────────────────────────────────────────╯    │
│                                                                                                  │
│ /home/chris/.local/lib/python3.11/site-packages/jinja2/environment.py:1301 in render             │
│                                                                                                  │
│   1298 │   │   try:                                                                              │
│   1299 │   │   │   return self.environment.concat(self.root_render_func(ctx))  # type: ignore    │
│   1300 │   │   except Exception:                                                                 │
│ ❱ 1301 │   │   │   self.environment.handle_exception()                                           │
│   1302 │                                                                                         │
│   1303 │   async def render_async(self, *args: t.Any, **kwargs: t.Any) -> str:                   │
│   1304 │   │   """This works similar to :meth:`render` but returns a coroutine                   │
│                                                                                                  │
│ ╭─────────────────────────────────────────── locals ───────────────────────────────────────────╮ │
│ │   args = ()                                                                                  │ │
│ │    ctx = <Context {'range': <class 'range'>, 'dict': <class 'dict'>, 'lipsum': <function     │ │
│ │          generate_lorem_ipsum at 0x7fd5f0552480>, 'cycler': <class 'jinja2.utils.Cycler'>,   │ │
│ │          'joiner': <class 'jinja2.utils.Joiner'>, 'namespace': <class                        │ │
│ │          'jinja2.utils.Namespace'>, 'role': 'php', 'metadata': {'galaxy_info': {'role_name': │ │
│ │          'php', 'author': 'Chris Croome', 'namespace': 'chriscroome', 'description':         │ │
│ │          'Ansible role for installing and configuring PHP on Debian Bullseye and Buster',    │ │
│ │          'company': 'Webarchitects Co-operative', 'license': 'GNU General Public License     │ │
│ │          v3.0 (GPLv3)', 'min_ansible_version': '2.13', 'platforms': [{'name': 'Debian',      │ │
│ │          'versions': ['bullseye', 'buster']}], 'galaxy_tags': ['debian', 'php', 'sury']},    │ │
│ │          'dependencies': []}, 'argument_specs': {'main': {'author': 'Chris Croome',          │ │
│ │          'description': 'Ansible role for installing and configuring PHP on Debian',         │ │
│ │          'short_description': 'The main entry point for the PHP role.', 'options': {'php':   │ │
│ │          {'type': 'bool', 'required': True, 'description': 'Run the tasks in this role.'},   │ │
│ │          'php_config': {'type': 'list', 'required': False, 'description': 'Optional PHP      │ │
│ │          configuration.', 'elements': 'dict', 'options': {'files': {'type': 'list',          │ │
│ │          'required': False, 'description': 'A list PHP file paths and their configuration.', │ │
│ │          'elements': 'dict', 'options': {'conf': {'type': 'dict', 'required': False,         │ │
│ │          'description': 'A YAML dictionary representing the sections, variables and values   │ │
│ │          of the PHP configuration file.'}, 'name': {'type': 'str', 'required': False,        │ │
│ │          'description': 'An optional name for the PHP configuration file.'}, 'path':         │ │
│ │          {'type': 'str', 'required': True, 'description': 'The path of the PHP configuration │ │
│ │          file.'}, 'state': {'type': 'str', 'required': True, 'description': 'The state of    │ │
│ │          the PHP configuration file.', 'choices': ['absent', 'edited', 'present',            │ │
│ │          'templated']}}}, 'name': {'type': 'str', 'required': False, 'description': 'A YAML  │ │
│ │          dictionary representing the variables for the PHP configuration file.'}, 'path':    │ │
│ │          {'type': 'str', 'required': True, 'description': 'The PHP version number.'},        │ │
│ │          'state': {'type': 'str', 'required': True, 'description': 'The state of the         │ │
│ │          configuration files for the version of PHP.', 'choices': ['absent', 'present']}}},  │ │
│ │          'php_sury': {'type': 'bool', 'required': False, 'description': 'Configure the Sury  │ │
│ │          PHP deb package repo.'}, 'php_versions': {'type': 'list', 'required': False,        │ │
│ │          'description': 'A list of PHP versions and their packages and state.', 'elements':  │ │
│ │          'dict', 'options': {'name': {'type': 'str', 'required': False, 'description': 'A    │ │
│ │          optional name for the PHP version packages.'}, 'pkg_absent': {'type': 'list',       │ │
│ │          'required': False, 'description': 'Packages that should be absent for this PHP      │ │
│ │          version.', 'elements': 'str'}, 'pkg_present': {'type': 'list', 'required': False,   │ │
│ │          'description': 'Packages that should be present for this PHP version.', 'elements': │ │
│ │          'str'}, 'state': {'type': 'str', 'required': True, 'description': 'The state of the │ │
│ │          packages for the PHP version.', 'choices': ['absent', 'present']}, 'version':       │ │
│ │          {'type': 'str', 'required': True, 'description': 'The PHP version number.'}}}}}}}   │ │
│ │          of 'markdown.j2'>                                                                   │ │
│ │ kwargs = {                                                                                   │ │
│ │          │   'role': 'php',                                                                  │ │
│ │          │   'metadata': {                                                                   │ │
│ │          │   │   'galaxy_info': {                                                            │ │
│ │          │   │   │   'role_name': 'php',                                                     │ │
│ │          │   │   │   'author': 'Chris Croome',                                               │ │
│ │          │   │   │   'namespace': 'chriscroome',                                             │ │
│ │          │   │   │   'description': 'Ansible role for installing and configuring PHP on      │ │
│ │          Debian Bullseye and Buster',                                                        │ │
│ │          │   │   │   'company': 'Webarchitects Co-operative',                                │ │
│ │          │   │   │   'license': 'GNU General Public License v3.0 (GPLv3)',                   │ │
│ │          │   │   │   'min_ansible_version': '2.13',                                          │ │
│ │          │   │   │   'platforms': [{'name': 'Debian', 'versions': ['bullseye', 'buster']}],  │ │
│ │          │   │   │   'galaxy_tags': ['debian', 'php', 'sury']                                │ │
│ │          │   │   },                                                                          │ │
│ │          │   │   'dependencies': []                                                          │ │
│ │          │   },                                                                              │ │
│ │          │   'argument_specs': {                                                             │ │
│ │          │   │   'main': {                                                                   │ │
│ │          │   │   │   'author': 'Chris Croome',                                               │ │
│ │          │   │   │   'description': 'Ansible role for installing and configuring PHP on      │ │
│ │          Debian',                                                                            │ │
│ │          │   │   │   'short_description': 'The main entry point for the PHP role.',          │ │
│ │          │   │   │   'options': {                                                            │ │
│ │          │   │   │   │   'php': {                                                            │ │
│ │          │   │   │   │   │   'type': 'bool',                                                 │ │
│ │          │   │   │   │   │   'required': True,                                               │ │
│ │          │   │   │   │   │   'description': 'Run the tasks in this role.'                    │ │
│ │          │   │   │   │   },                                                                  │ │
│ │          │   │   │   │   'php_config': {                                                     │ │
│ │          │   │   │   │   │   'type': 'list',                                                 │ │
│ │          │   │   │   │   │   'required': False,                                              │ │
│ │          │   │   │   │   │   'description': 'Optional PHP configuration.',                   │ │
│ │          │   │   │   │   │   'elements': 'dict',                                             │ │
│ │          │   │   │   │   │   'options': {                                                    │ │
│ │          │   │   │   │   │   │   'files': {                                                  │ │
│ │          │   │   │   │   │   │   │   'type': 'list',                                         │ │
│ │          │   │   │   │   │   │   │   'required': False,                                      │ │
│ │          │   │   │   │   │   │   │   'description': 'A list PHP file paths and their         │ │
│ │          configuration.',                                                                    │ │
│ │          │   │   │   │   │   │   │   'elements': 'dict',                                     │ │
│ │          │   │   │   │   │   │   │   'options': {                                            │ │
│ │          │   │   │   │   │   │   │   │   'conf': {                                           │ │
│ │          │   │   │   │   │   │   │   │   │   'type': 'dict',                                 │ │
│ │          │   │   │   │   │   │   │   │   │   'required': False,                              │ │
│ │          │   │   │   │   │   │   │   │   │   'description': 'A YAML dictionary representing  │ │
│ │          the sections, variables and values of the PHP con'+16                               │ │
│ │          │   │   │   │   │   │   │   │   },                                                  │ │
│ │          │   │   │   │   │   │   │   │   'name': {                                           │ │
│ │          │   │   │   │   │   │   │   │   │   'type': 'str',                                  │ │
│ │          │   │   │   │   │   │   │   │   │   'required': False,                              │ │
│ │          │   │   │   │   │   │   │   │   │   'description': 'An optional name for the PHP    │ │
│ │          configuration file.'                                                                │ │
│ │          │   │   │   │   │   │   │   │   },                                                  │ │
│ │          │   │   │   │   │   │   │   │   'path': {                                           │ │
│ │          │   │   │   │   │   │   │   │   │   'type': 'str',                                  │ │
│ │          │   │   │   │   │   │   │   │   │   'required': True,                               │ │
│ │          │   │   │   │   │   │   │   │   │   'description': 'The path of the PHP             │ │
│ │          configuration file.'                                                                │ │
│ │          │   │   │   │   │   │   │   │   },                                                  │ │
│ │          │   │   │   │   │   │   │   │   'state': {                                          │ │
│ │          │   │   │   │   │   │   │   │   │   'type': 'str',                                  │ │
│ │          │   │   │   │   │   │   │   │   │   'required': True,                               │ │
│ │          │   │   │   │   │   │   │   │   │   'description': 'The state of the PHP            │ │
│ │          configuration file.',                                                               │ │
│ │          │   │   │   │   │   │   │   │   │   'choices': [                                    │ │
│ │          │   │   │   │   │   │   │   │   │   │   'absent',                                   │ │
│ │          │   │   │   │   │   │   │   │   │   │   'edited',                                   │ │
│ │          │   │   │   │   │   │   │   │   │   │   'present',                                  │ │
│ │          │   │   │   │   │   │   │   │   │   │   'templated'                                 │ │
│ │          │   │   │   │   │   │   │   │   │   ]                                               │ │
│ │          │   │   │   │   │   │   │   │   }                                                   │ │
│ │          │   │   │   │   │   │   │   }                                                       │ │
│ │          │   │   │   │   │   │   },                                                          │ │
│ │          │   │   │   │   │   │   'name': {                                                   │ │
│ │          │   │   │   │   │   │   │   'type': 'str',                                          │ │
│ │          │   │   │   │   │   │   │   'required': False,                                      │ │
│ │          │   │   │   │   │   │   │   'description': 'A YAML dictionary representing the      │ │
│ │          variables for the PHP configuration file.'                                          │ │
│ │          │   │   │   │   │   │   },                                                          │ │
│ │          │   │   │   │   │   │   'path': {                                                   │ │
│ │          │   │   │   │   │   │   │   'type': 'str',                                          │ │
│ │          │   │   │   │   │   │   │   'required': True,                                       │ │
│ │          │   │   │   │   │   │   │   'description': 'The PHP version number.'                │ │
│ │          │   │   │   │   │   │   },                                                          │ │
│ │          │   │   │   │   │   │   'state': {                                                  │ │
│ │          │   │   │   │   │   │   │   'type': 'str',                                          │ │
│ │          │   │   │   │   │   │   │   'required': True,                                       │ │
│ │          │   │   │   │   │   │   │   'description': 'The state of the configuration files    │ │
│ │          for the version of PHP.',                                                           │ │
│ │          │   │   │   │   │   │   │   'choices': ['absent', 'present']                        │ │
│ │          │   │   │   │   │   │   }                                                           │ │
│ │          │   │   │   │   │   }                                                               │ │
│ │          │   │   │   │   },                                                                  │ │
│ │          │   │   │   │   'php_sury': {                                                       │ │
│ │          │   │   │   │   │   'type': 'bool',                                                 │ │
│ │          │   │   │   │   │   'required': False,                                              │ │
│ │          │   │   │   │   │   'description': 'Configure the Sury PHP deb package repo.'       │ │
│ │          │   │   │   │   },                                                                  │ │
│ │          │   │   │   │   'php_versions': {                                                   │ │
│ │          │   │   │   │   │   'type': 'list',                                                 │ │
│ │          │   │   │   │   │   'required': False,                                              │ │
│ │          │   │   │   │   │   'description': 'A list of PHP versions and their packages and   │ │
│ │          state.',                                                                            │ │
│ │          │   │   │   │   │   'elements': 'dict',                                             │ │
│ │          │   │   │   │   │   'options': {                                                    │ │
│ │          │   │   │   │   │   │   'name': {                                                   │ │
│ │          │   │   │   │   │   │   │   'type': 'str',                                          │ │
│ │          │   │   │   │   │   │   │   'required': False,                                      │ │
│ │          │   │   │   │   │   │   │   'description': 'A optional name for the PHP version     │ │
│ │          packages.'                                                                          │ │
│ │          │   │   │   │   │   │   },                                                          │ │
│ │          │   │   │   │   │   │   'pkg_absent': {                                             │ │
│ │          │   │   │   │   │   │   │   'type': 'list',                                         │ │
│ │          │   │   │   │   │   │   │   'required': False,                                      │ │
│ │          │   │   │   │   │   │   │   'description': 'Packages that should be absent for this │ │
│ │          PHP version.',                                                                      │ │
│ │          │   │   │   │   │   │   │   'elements': 'str'                                       │ │
│ │          │   │   │   │   │   │   },                                                          │ │
│ │          │   │   │   │   │   │   'pkg_present': {                                            │ │
│ │          │   │   │   │   │   │   │   'type': 'list',                                         │ │
│ │          │   │   │   │   │   │   │   'required': False,                                      │ │
│ │          │   │   │   │   │   │   │   'description': 'Packages that should be present for     │ │
│ │          this PHP version.',                                                                 │ │
│ │          │   │   │   │   │   │   │   'elements': 'str'                                       │ │
│ │          │   │   │   │   │   │   },                                                          │ │
│ │          │   │   │   │   │   │   'state': {                                                  │ │
│ │          │   │   │   │   │   │   │   'type': 'str',                                          │ │
│ │          │   │   │   │   │   │   │   'required': True,                                       │ │
│ │          │   │   │   │   │   │   │   'description': 'The state of the packages for the PHP   │ │
│ │          version.',                                                                          │ │
│ │          │   │   │   │   │   │   │   'choices': ['absent', 'present']                        │ │
│ │          │   │   │   │   │   │   },                                                          │ │
│ │          │   │   │   │   │   │   'version': {                                                │ │
│ │          │   │   │   │   │   │   │   'type': 'str',                                          │ │
│ │          │   │   │   │   │   │   │   'required': True,                                       │ │
│ │          │   │   │   │   │   │   │   'description': 'The PHP version number.'                │ │
│ │          │   │   │   │   │   │   }                                                           │ │
│ │          │   │   │   │   │   }                                                               │ │
│ │          │   │   │   │   }                                                                   │ │
│ │          │   │   │   }                                                                       │ │
│ │          │   │   }                                                                           │ │
│ │          │   }                                                                               │ │
│ │          }                                                                                   │ │
│ │   self = <Template 'markdown.j2'>                                                            │ │
│ ╰──────────────────────────────────────────────────────────────────────────────────────────────╯ │
│                                                                                                  │
│ /home/chris/.local/lib/python3.11/site-packages/jinja2/environment.py:936 in handle_exception    │
│                                                                                                  │
│    933 │   │   """                                                                               │
│    934 │   │   from .debug import rewrite_traceback_stack                                        │
│    935 │   │                                                                                     │
│ ❱  936 │   │   raise rewrite_traceback_stack(source=source)                                      │
│    937 │                                                                                         │
│    938 │   def join_path(self, template: str, parent: str) -> str:                               │
│    939 │   │   """Join a template with the parent.  By default all the lookups are               │
│                                                                                                  │
│ ╭────────────────────────────────────── locals ───────────────────────────────────────╮          │
│ │ rewrite_traceback_stack = <function rewrite_traceback_stack at 0x7fd5efe91300>      │          │
│ │                    self = <jinja2.environment.Environment object at 0x7fd5f008e190> │          │
│ │                  source = None                                                      │          │
│ ╰─────────────────────────────────────────────────────────────────────────────────────╯          │
│                                                                                                  │
│ /home/chris/.local/lib/python3.11/site-packages/templates/markdown.j2:22 in top-level template   │
│ code                                                                                             │
│                                                                                                  │
│   19 ## Role Arguments                                                                           │
│   20 ---                                                                                         │
│   21 {% for entrypoint in argument_specs.keys() %}                                               │
│ ❱ 22 {%- set path, options=entrypoint_options[entrypoint][0] -%}                                 │
│   23 ### Entrypoint: {{ entrypoint }}                                                            │
│   24 ---                                                                                         │
│   25 {{ argument_specs[entrypoint].short_description }}                                          │
│                                                                                                  │
│ ╭─────────────────────────────────────────── locals ───────────────────────────────────────────╮ │
│ │ argument_specs = {                                                                           │ │
│ │                  │   'main': {                                                               │ │
│ │                  │   │   'author': 'Chris Croome',                                           │ │
│ │                  │   │   'description': 'Ansible role for installing and configuring PHP on  │ │
│ │                  Debian',                                                                    │ │
│ │                  │   │   'short_description': 'The main entry point for the PHP role.',      │ │
│ │                  │   │   'options': {                                                        │ │
│ │                  │   │   │   'php': {                                                        │ │
│ │                  │   │   │   │   'type': 'bool',                                             │ │
│ │                  │   │   │   │   'required': True,                                           │ │
│ │                  │   │   │   │   'description': 'Run the tasks in this role.'                │ │
│ │                  │   │   │   },                                                              │ │
│ │                  │   │   │   'php_config': {                                                 │ │
│ │                  │   │   │   │   'type': 'list',                                             │ │
│ │                  │   │   │   │   'required': False,                                          │ │
│ │                  │   │   │   │   'description': 'Optional PHP configuration.',               │ │
│ │                  │   │   │   │   'elements': 'dict',                                         │ │
│ │                  │   │   │   │   'options': {                                                │ │
│ │                  │   │   │   │   │   'files': {                                              │ │
│ │                  │   │   │   │   │   │   'type': 'list',                                     │ │
│ │                  │   │   │   │   │   │   'required': False,                                  │ │
│ │                  │   │   │   │   │   │   'description': 'A list PHP file paths and their     │ │
│ │                  configuration.',                                                            │ │
│ │                  │   │   │   │   │   │   'elements': 'dict',                                 │ │
│ │                  │   │   │   │   │   │   'options': {                                        │ │
│ │                  │   │   │   │   │   │   │   'conf': {                                       │ │
│ │                  │   │   │   │   │   │   │   │   'type': 'dict',                             │ │
│ │                  │   │   │   │   │   │   │   │   'required': False,                          │ │
│ │                  │   │   │   │   │   │   │   │   'description': 'A YAML dictionary           │ │
│ │                  representing the sections, variables and values of the PHP con'+16          │ │
│ │                  │   │   │   │   │   │   │   },                                              │ │
│ │                  │   │   │   │   │   │   │   'name': {                                       │ │
│ │                  │   │   │   │   │   │   │   │   'type': 'str',                              │ │
│ │                  │   │   │   │   │   │   │   │   'required': False,                          │ │
│ │                  │   │   │   │   │   │   │   │   'description': 'An optional name for the    │ │
│ │                  PHP configuration file.'                                                    │ │
│ │                  │   │   │   │   │   │   │   },                                              │ │
│ │                  │   │   │   │   │   │   │   'path': {                                       │ │
│ │                  │   │   │   │   │   │   │   │   'type': 'str',                              │ │
│ │                  │   │   │   │   │   │   │   │   'required': True,                           │ │
│ │                  │   │   │   │   │   │   │   │   'description': 'The path of the PHP         │ │
│ │                  configuration file.'                                                        │ │
│ │                  │   │   │   │   │   │   │   },                                              │ │
│ │                  │   │   │   │   │   │   │   'state': {                                      │ │
│ │                  │   │   │   │   │   │   │   │   'type': 'str',                              │ │
│ │                  │   │   │   │   │   │   │   │   'required': True,                           │ │
│ │                  │   │   │   │   │   │   │   │   'description': 'The state of the PHP        │ │
│ │                  configuration file.',                                                       │ │
│ │                  │   │   │   │   │   │   │   │   'choices': [                                │ │
│ │                  │   │   │   │   │   │   │   │   │   'absent',                               │ │
│ │                  │   │   │   │   │   │   │   │   │   'edited',                               │ │
│ │                  │   │   │   │   │   │   │   │   │   'present',                              │ │
│ │                  │   │   │   │   │   │   │   │   │   'templated'                             │ │
│ │                  │   │   │   │   │   │   │   │   ]                                           │ │
│ │                  │   │   │   │   │   │   │   }                                               │ │
│ │                  │   │   │   │   │   │   }                                                   │ │
│ │                  │   │   │   │   │   },                                                      │ │
│ │                  │   │   │   │   │   'name': {                                               │ │
│ │                  │   │   │   │   │   │   'type': 'str',                                      │ │
│ │                  │   │   │   │   │   │   'required': False,                                  │ │
│ │                  │   │   │   │   │   │   'description': 'A YAML dictionary representing the  │ │
│ │                  variables for the PHP configuration file.'                                  │ │
│ │                  │   │   │   │   │   },                                                      │ │
│ │                  │   │   │   │   │   'path': {                                               │ │
│ │                  │   │   │   │   │   │   'type': 'str',                                      │ │
│ │                  │   │   │   │   │   │   'required': True,                                   │ │
│ │                  │   │   │   │   │   │   'description': 'The PHP version number.'            │ │
│ │                  │   │   │   │   │   },                                                      │ │
│ │                  │   │   │   │   │   'state': {                                              │ │
│ │                  │   │   │   │   │   │   'type': 'str',                                      │ │
│ │                  │   │   │   │   │   │   'required': True,                                   │ │
│ │                  │   │   │   │   │   │   'description': 'The state of the configuration      │ │
│ │                  files for the version of PHP.',                                             │ │
│ │                  │   │   │   │   │   │   'choices': ['absent', 'present']                    │ │
│ │                  │   │   │   │   │   }                                                       │ │
│ │                  │   │   │   │   }                                                           │ │
│ │                  │   │   │   },                                                              │ │
│ │                  │   │   │   'php_sury': {                                                   │ │
│ │                  │   │   │   │   'type': 'bool',                                             │ │
│ │                  │   │   │   │   'required': False,                                          │ │
│ │                  │   │   │   │   'description': 'Configure the Sury PHP deb package repo.'   │ │
│ │                  │   │   │   },                                                              │ │
│ │                  │   │   │   'php_versions': {                                               │ │
│ │                  │   │   │   │   'type': 'list',                                             │ │
│ │                  │   │   │   │   'required': False,                                          │ │
│ │                  │   │   │   │   'description': 'A list of PHP versions and their packages   │ │
│ │                  and state.',                                                                │ │
│ │                  │   │   │   │   'elements': 'dict',                                         │ │
│ │                  │   │   │   │   'options': {                                                │ │
│ │                  │   │   │   │   │   'name': {                                               │ │
│ │                  │   │   │   │   │   │   'type': 'str',                                      │ │
│ │                  │   │   │   │   │   │   'required': False,                                  │ │
│ │                  │   │   │   │   │   │   'description': 'A optional name for the PHP version │ │
│ │                  packages.'                                                                  │ │
│ │                  │   │   │   │   │   },                                                      │ │
│ │                  │   │   │   │   │   'pkg_absent': {                                         │ │
│ │                  │   │   │   │   │   │   'type': 'list',                                     │ │
│ │                  │   │   │   │   │   │   'required': False,                                  │ │
│ │                  │   │   │   │   │   │   'description': 'Packages that should be absent for  │ │
│ │                  this PHP version.',                                                         │ │
│ │                  │   │   │   │   │   │   'elements': 'str'                                   │ │
│ │                  │   │   │   │   │   },                                                      │ │
│ │                  │   │   │   │   │   'pkg_present': {                                        │ │
│ │                  │   │   │   │   │   │   'type': 'list',                                     │ │
│ │                  │   │   │   │   │   │   'required': False,                                  │ │
│ │                  │   │   │   │   │   │   'description': 'Packages that should be present for │ │
│ │                  this PHP version.',                                                         │ │
│ │                  │   │   │   │   │   │   'elements': 'str'                                   │ │
│ │                  │   │   │   │   │   },                                                      │ │
│ │                  │   │   │   │   │   'state': {                                              │ │
│ │                  │   │   │   │   │   │   'type': 'str',                                      │ │
│ │                  │   │   │   │   │   │   'required': True,                                   │ │
│ │                  │   │   │   │   │   │   'description': 'The state of the packages for the   │ │
│ │                  PHP version.',                                                              │ │
│ │                  │   │   │   │   │   │   'choices': ['absent', 'present']                    │ │
│ │                  │   │   │   │   │   },                                                      │ │
│ │                  │   │   │   │   │   'version': {                                            │ │
│ │                  │   │   │   │   │   │   'type': 'str',                                      │ │
│ │                  │   │   │   │   │   │   'required': True,                                   │ │
│ │                  │   │   │   │   │   │   'description': 'The PHP version number.'            │ │
│ │                  │   │   │   │   │   }                                                       │ │
│ │                  │   │   │   │   }                                                           │ │
│ │                  │   │   │   }                                                               │ │
│ │                  │   │   }                                                                   │ │
│ │                  │   }                                                                       │ │
│ │                  }                                                                           │ │
│ │         cycler = <class 'jinja2.utils.Cycler'>                                               │ │
│ │           dict = <class 'dict'>                                                              │ │
│ │     entrypoint = 'main'                                                                      │ │
│ │         joiner = <class 'jinja2.utils.Joiner'>                                               │ │
│ │         lipsum = <function generate_lorem_ipsum at 0x7fd5f0552480>                           │ │
│ │       metadata = {                                                                           │ │
│ │                  │   'galaxy_info': {                                                        │ │
│ │                  │   │   'role_name': 'php',                                                 │ │
│ │                  │   │   'author': 'Chris Croome',                                           │ │
│ │                  │   │   'namespace': 'chriscroome',                                         │ │
│ │                  │   │   'description': 'Ansible role for installing and configuring PHP on  │ │
│ │                  Debian Bullseye and Buster',                                                │ │
│ │                  │   │   'company': 'Webarchitects Co-operative',                            │ │
│ │                  │   │   'license': 'GNU General Public License v3.0 (GPLv3)',               │ │
│ │                  │   │   'min_ansible_version': '2.13',                                      │ │
│ │                  │   │   'platforms': [                                                      │ │
│ │                  │   │   │   {'name': 'Debian', 'versions': ['bullseye', 'buster']}          │ │
│ │                  │   │   ],                                                                  │ │
│ │                  │   │   'galaxy_tags': ['debian', 'php', 'sury']                            │ │
│ │                  │   },                                                                      │ │
│ │                  │   'dependencies': []                                                      │ │
│ │                  }                                                                           │ │
│ │      namespace = <class 'jinja2.utils.Namespace'>                                            │ │
│ │          range = <class 'range'>                                                             │ │
│ │           role = 'php'                                                                       │ │
│ ╰──────────────────────────────────────────────────────────────────────────────────────────────╯ │
│                                                                                                  │
│ /home/chris/.local/lib/python3.11/site-packages/jinja2/environment.py:466 in getitem             │
│                                                                                                  │
│    463 │   ) -> t.Union[t.Any, Undefined]:                                                       │
│    464 │   │   """Get an item or attribute of an object but prefer the item."""                  │
│    465 │   │   try:                                                                              │
│ ❱  466 │   │   │   return obj[argument]                                                          │
│    467 │   │   except (AttributeError, TypeError, LookupError):                                  │
│    468 │   │   │   if isinstance(argument, str):                                                 │
│    469 │   │   │   │   try:                                                                      │
│                                                                                                  │
│ ╭─────────────────────────────── locals ───────────────────────────────╮                         │
│ │ argument = 'main'                                                    │                         │
│ │      obj = Undefined                                                 │                         │
│ │     self = <jinja2.environment.Environment object at 0x7fd5f008e190> │                         │
│ ╰──────────────────────────────────────────────────────────────────────╯                         │
╰──────────────────────────────────────────────────────────────────────────────────────────────────╯
UndefinedError: 'entrypoint_options' is undefined

These are the files in the meta directory:

Any idea what I'm doing wrong?

(The sury branch of the role in question is a WIP but I think the metadata files are valid.)