Proposal: document role "return variables" in argument_specs.yml (like module RETURN)

Hello everyone,

I’d like to propose way for roles to document the variables they set, rendered as a table on the role’s documentation page the same way modules document their return values today.

I initiated this in antsibull-docs#491, where @felixfontein pointed out that this is an ecosystem decision that needs buy-in from the Core team, not something antsibull-docs can decide on its own.

The problem

Modules can declare RETURN: and users get a nice, typed “Return Values” table in the docs, plus RV() cross-reference markup. Roles have no equivalent. A role that sets facts/variables (via set_fact) has no structured, typed way to document them.

A weak workaround I found is writing bullet lists into the notes: section of meta/argument_specs.yml.

Here are some examples of return variables that we have in our roles:

bootloader:

notes:
  - "B(Variables):"
  - >
    • C(bootloader_reboot_required) — V(true) means the host must reboot to
    apply the role's changes. The role sets this when O(bootloader_reboot_ok)
    is V(false) and it made changes.
  - >
    • C(bootloader_facts) — boot information for all kernels. The role returns
    this when O(bootloader_gather_facts) is V(true). Each entry is a dict with
    C(kernel), C(args), C(root), and C(default) keys.

ha_cluster:

notes:
  - "B(Variables):"
  - >
    • C(ha_cluster_facts) — the exported current cluster configuration,
    populated when O(ha_cluster_export_configuration) is V(true). The exported
    dictionary may not be complete and manual modification is expected; most
    notably you must set O(ha_cluster_hacluster_password). Depending on the
    C(pcs) version installed on managed nodes, certain variables may not be
    present in the export.
  - "The following variables are present in the export:"
  - "• C(ha_cluster_enable_repos) — RHEL and CentOS only."
  - "• C(ha_cluster_enable_repos_resilient_storage) — RHEL and CentOS only."
  - >
    • C(ha_cluster_manage_firewall) — requires C(python3-firewall) to be
    installed on managed nodes.
  - >
    • C(ha_cluster_manage_selinux) — requires C(python3-policycoreutils) to be
    installed on managed nodes.
  - "• C(ha_cluster_cluster_present) — whether a cluster is configured."
  - "• C(ha_cluster_start_on_boot) — whether cluster services start on boot."
  - "• C(ha_cluster_install_cloud_agents) — RHEL and CentOS only."

podman:

notes:
  - "B(Variables):"
  - >
    • C(podman_version) — the version string of the podman version in use.
    Useful in templates, for example
    C(podman_version is version("4.5", ">=")).
  - >
    • C(podman_subuid_info) — the subuid ranges for the users managed by the
    role, extracted from C(getsubids) if available, or from F(/etc/subuid).
    A dict keyed by user name; each value is a dict with C(start) (int) and
    C(range) (int).
  - >
    • C(podman_subgid_info) — the subgid ranges for the groups managed by the
    role, extracted from C(getsubids) if available, or from F(/etc/subgid).
    A dict keyed by group name; each value is a dict with C(start) (int) and
    C(range) (int).
  - >
    • C(podman_use_new_toml_formatter) — whether the role used the new TOML
    formatter when rendering configuration files.

This has no type/returned/sample columns, no anchors, no RV() targets, and is easy to let drift out of date.

Proposed approach

Allow an optional return: key per entry point in meta/argument_specs.yml, using the same structure as module RETURN::.

Here’s an example of how the bootloader role return would look like:

argument_specs:
  main:
    short_description: ...
    options: ...
  return:
    bootloader_reboot_required:
      description:
        - V(true) means the host must reboot to apply the role's changes.
        - The role sets this when O(bootloader_reboot_ok) is V(false) and it
          made changes.
      type: bool
      returned: always
      sample: true
    bootloader_facts:
      description:
        - Boot information for all kernels.
      type: list
      elements: dict
      returned: When O(bootloader_gather_facts) is V(true).
      contains:
        kernel:
          description: Path to the kernel image.
          type: str
        args:
          description: The kernel command-line arguments.
          type: str
        root:
          description: The root device for this boot entry.
          type: str
        default:
          description: Whether this is the default boot entry.
          type: bool
      sample:
        - kernel: /boot/vmlinuz-6.9.0
          args: "ro crashkernel=1G-4G:192M"
          root: UUID=1aefe582
          default: true

Key points:

  • Fully optional — roles without a return: block are completely unaffected.
  • Rendered as a dedicated table on the role page. I’d suggest titling it “Return Variables” rather than modules’ “Return Values”, since a role sets variables rather than returning a structured result.
  • RV(fqcn#role:entrypoint:name) markup already supports roles (groundwork was added in antsibull-docs #113 “in case they are ever added”), so references would resolve once the data exists.

Where the changes would land

I’ve already prototyped this end to end. The notable finding: the data path mostly works already.

  1. ansible-core — ansible-doc -t role --json already passes a return: key through untouched (the role doc builder copies the whole entry-point spec), so no change is strictly required for the data to reach doc tooling. Two things would be nice-to-have and are really the Core team’s call: (a) rendering the section in ansible-doc’s text output, and (b) officially documenting/“blessing” the return: key in the role arg-spec format.
  2. antsibull-docs — needs to parse and render the return: block. Prototyped in antsibull-docs#491.
  3. ansible-lint — its role-arg-spec.json schema currently has additionalProperties: false and no return property, so it rejects the key. This would need an optional return property added, otherwise roles using it fail schema[role-arg-spec].

I ealize why the decision to avoid adding return: for roles was made - they don’t return values the way modules do, however in linux-system-role it’s a common practice for the roles to set Variables for users to see the current configuration, or whether reboot is needed, or other extra info. I think it’s the time to add this support.

If there’s agreement on the direction, I’m happy to open PRs with the implementation across the three repos (antsibull-docs, ansible-lint schema, and any ansible-core doc/text-output changes).

What do you think? Do you have any concerns about adding return: for roles?

Thank you