fix(ansible): preserve YAML module order
Some checks failed
CI / test (push) Failing after 3s
CI / auto-merge (push) Failing after 0s
CI / auto-fix (push) Failing after 0s

Co-Authored-By: Virgil <virgil@lethean.io>
This commit is contained in:
Virgil 2026-04-01 08:53:29 +00:00
parent 585123d817
commit f0878a202c
2 changed files with 52 additions and 22 deletions

View file

@ -287,7 +287,7 @@ func (t *Task) UnmarshalYAML(node *yaml.Node) error {
*t = Task(raw)
t.raw = m
// Find the module key
// Find the module key in YAML order so the first recognised module wins.
knownKeys := map[string]bool{
"name": true, "register": true, "when": true, "loop": true,
"loop_control": true, "vars": true, "environment": true,
@ -301,28 +301,43 @@ func (t *Task) UnmarshalYAML(node *yaml.Node) error {
"with_items": true, "with_dict": true, "with_file": true,
}
for key, val := range m {
if knownKeys[key] {
continue
}
// Check if this is a module
if isModule(key) {
t.Module = key
t.Args = make(map[string]any)
switch v := val.(type) {
case string:
// Free-form args (e.g., shell: echo hello)
t.Args["_raw_params"] = v
case map[string]any:
t.Args = v
case nil:
// Module with no args
default:
t.Args["_raw_params"] = v
if node.Kind == yaml.MappingNode {
for i := 0; i+1 < len(node.Content); i += 2 {
keyNode := node.Content[i]
valueNode := node.Content[i+1]
key := keyNode.Value
if knownKeys[key] {
continue
}
// Check if this is a module.
if isModule(key) {
t.Module = key
t.Args = make(map[string]any)
switch valueNode.Kind {
case yaml.MappingNode:
var args map[string]any
if err := valueNode.Decode(&args); err == nil {
t.Args = args
}
case yaml.SequenceNode:
var args any
if err := valueNode.Decode(&args); err == nil {
t.Args["_raw_params"] = args
}
case yaml.ScalarNode:
if valueNode.Tag != "!!null" {
t.Args["_raw_params"] = valueNode.Value
}
default:
var args any
if err := valueNode.Decode(&args); err == nil && args != nil {
t.Args["_raw_params"] = args
}
}
break
}
break
}
}

View file

@ -196,6 +196,21 @@ with_items:
assert.Len(t, items, 2)
}
func TestTypes_Task_UnmarshalYAML_Good_FirstRecognisedModuleWins(t *testing.T) {
input := `
name: Multiple modules
shell: echo first
debug:
msg: "second"
`
var task Task
err := yaml.Unmarshal([]byte(input), &task)
require.NoError(t, err)
assert.Equal(t, "shell", task.Module)
assert.Equal(t, "echo first", task.Args["_raw_params"])
}
func TestTypes_Task_UnmarshalYAML_Good_WithNotify(t *testing.T) {
input := `
name: Install package