module

Krikri::TaskDebugger

debugger: - drop into an interactive prompt when a task reaches the configured condition (always / never / on_failed / on_skipped / on_unreachable).

Real Ansible's debugger reads from stdin even when it is NOT a terminal, unlike vars_prompt (verified against ansible-core 2.19.4: piping "c" continues the run; closing stdin yields "User interrupted execution" and exit 99). Both are reproduced.

Commands: p/print, r/redo, c/continue, q/quit, u/update_task, EOF, and ASSIGNMENT to task.args[...] / task_vars[...].

Real Ansible implements assignment by exec-ing the typed line as Python against a live scope dict, which this engine has no equivalent of. What it does instead is parse the two assignment SHAPES that make the debugger useful - task.args['x'] = <literal> and task_vars['y'] = <literal> - and apply them to the real task/vars, so the documented workflow (fix a wrong argument, r, watch it pass) works. Arbitrary Python beyond that shape is still refused rather than silently ignored: an unparseable assignment says so.

The interaction between the two, verified against real ansible-core 2.19.4 rather than assumed, is subtle and reproduced exactly:

  • task.args[...] = v takes effect on the very next r.
  • task_vars[...] = v does NOT, on its own, change the task's arguments - a redo right after it re-runs the ORIGINAL command (real Ansible's task object is already templated by then). Only u/update_task re-templates the task from the updated vars, which is exactly what real Ansible's own do_update_task ("Recreate the task from task._ds, and template with updated task_vars") does.

Constants

INTERRUPTED_EXIT = 99

Exit code real Ansible uses when the debugger is left by EOF or quit - its generic "user interrupted execution" code.

REGEX_ASSIGNMENT = /\A(task\.args|task_vars)\s*\[\s*(?:'([^']*)'|"([^"]*)")\s*\]\s*=\s*(.+)\z/

An assignment shape this understands: task.args['key'] = value or task_vars['key'] = value, with either quote style, and with or without the brackets' surrounding whitespace.

Class methods

run(task_name : String, host_name : String, result : JSON::Any, vars : Hash(String, JSON::Any), task : Task | Nil = nil, var_overrides : Hash(String, JSON::Any) | Nil = nil) : Outcome

Runs the prompt loop. Returns whether the caller should re-run the task; quitting or EOF exits the process, as real Ansible does.

task is mutated in place for task.args[...] = assignments (its params are this engine's equivalent - the raw, pre-substitution module arguments). var_overrides is the caller's own hash for task_vars[...] = assignments: entries land in it only once u/update_task promotes them, so the caller can merge it into the vars context it builds for a redo and get real Ansible's semantics for free.

Source
triggered?(setting : String | Nil, result : JSON::Any) : Bool
Source

Nested types