From 77bfa2e813bc85a88fe67868b8fdeaa372b8844f Mon Sep 17 00:00:00 2001 From: Virgil Date: Wed, 1 Apr 2026 07:14:03 +0000 Subject: [PATCH] feat(ansible): support playbook unmarshalling Co-Authored-By: Virgil --- types.go | 26 ++++++++++++++++++++++++++ types_test.go | 20 ++++++++++++++++++++ 2 files changed, 46 insertions(+) diff --git a/types.go b/types.go index f5322fa..1176a3c 100644 --- a/types.go +++ b/types.go @@ -2,6 +2,8 @@ package ansible import ( "time" + + "gopkg.in/yaml.v3" ) // Playbook represents an Ansible playbook. @@ -13,6 +15,30 @@ type Playbook struct { Plays []Play `yaml:",inline"` } +// UnmarshalYAML allows a top-level YAML sequence to decode directly into a +// Playbook wrapper. +func (p *Playbook) UnmarshalYAML(node *yaml.Node) error { + if p == nil { + return nil + } + + switch node.Kind { + case yaml.SequenceNode: + return node.Decode(&p.Plays) + case yaml.MappingNode: + var raw struct { + Plays []Play `yaml:"plays"` + } + if err := node.Decode(&raw); err != nil { + return err + } + p.Plays = raw.Plays + return nil + default: + return node.Decode(&p.Plays) + } +} + // Play represents a single play in a playbook. // // Example: diff --git a/types_test.go b/types_test.go index 5119c8c..f074ffe 100644 --- a/types_test.go +++ b/types_test.go @@ -8,6 +8,26 @@ import ( "gopkg.in/yaml.v3" ) +// --- Playbook UnmarshalYAML --- + +func TestTypes_Playbook_UnmarshalYAML_Good_Sequence(t *testing.T) { + input := ` +- name: Bootstrap + hosts: all + tasks: + - debug: + msg: hello +` + + var book Playbook + err := yaml.Unmarshal([]byte(input), &book) + + require.NoError(t, err) + require.Len(t, book.Plays, 1) + assert.Equal(t, "Bootstrap", book.Plays[0].Name) + assert.Equal(t, "all", book.Plays[0].Hosts) +} + // --- RoleRef UnmarshalYAML --- func TestTypes_RoleRef_UnmarshalYAML_Good_StringForm(t *testing.T) { -- 2.45.3