[PATCH 19/26] lucid: render task diffs on --diff

Daniel Gomez <[email protected]> Tue, 19 May 2026 15:28:16 +0200
Newsgroups dev.linux.lists.kdevops
Message-ID <[email protected]>
From: Daniel Gomez <[email protected]>

Running ansible-playbook with --diff produces before/after diff
structures on each result, and the default stdout callback renders
them through CallbackBase._get_diff. Lucid did not implement
v2_on_file_diff at all, so --diff was silently dropped: users who
asked for a diff got nothing on screen and nothing in the log.

The new hook reads result._result['diff'], handles both the
single-dict and loop-nested list shapes, and delegates formatting to
_get_diff so that result_format, pretty_results, and result_indentation
from the result_format_callback fragment all apply uniformly. To keep
lucid's minimal default, on-screen rendering is gated behind
verbosity >= 1 so unchanged quiet runs stay quiet, while the log
always receives the diff for post-hoc inspection.

Generated-by: Claude AI
Signed-off-by: Daniel Gomez <[email protected]>
---
 callback_plugins/lucid.py | 36 ++++++++++++++++++++++++++++++++++++
 1 file changed, 36 insertions(+)

diff --git a/callback_plugins/lucid.py b/callback_plugins/lucid.py
index a9639b33..4fbce230 100644
--- a/callback_plugins/lucid.py
+++ b/callback_plugins/lucid.py
@@ -615,6 +615,42 @@ class CallbackModule(CallbackBase):
                 msg = json.dumps(msg, indent=2)
             self._write_to_log(f"  [{item}] msg: {msg}")
 
+    def v2_on_file_diff(self, result):
+        """Render task diff output when --diff is enabled.
+
+        Ansible populates result._result['diff'] with before/after
+        structures when the user runs with --diff. Without this hook
+        lucid would silently drop the diff. Render through
+        CallbackBase._get_diff so the formatting honors the same
+        result_format / pretty_results / result_indentation options
+        the rest of the result rendering does. On-screen display is
+        gated behind verbosity >= 1 so lucid stays silent at the
+        default verbosity; the log always captures the diff regardless
+        so post-hoc debugging has the full picture.
+        """
+        diff_data = result._result.get("diff")
+        if not diff_data:
+            return
+
+        # Handle loop tasks: diffs can be nested under results[*].diff.
+        if isinstance(diff_data, list) and diff_data and all(
+            isinstance(d, dict) for d in diff_data
+        ):
+            diffs_to_render = diff_data
+        elif isinstance(diff_data, dict):
+            diffs_to_render = [diff_data]
+        else:
+            diffs_to_render = diff_data
+
+        diff_text = self._get_diff(diffs_to_render)
+        if not diff_text:
+            return
+
+        if self._display.verbosity >= 1:
+            with self.output_lock:
+                self._display.display(diff_text)
+        self._write_to_log(f"DIFF:\n{diff_text}")
+
     def v2_runner_retry(self, result):
         """Task is being retried after failure"""
         host = result._host.name

-- 
2.53.0