From 35014b52fc16149bb4e6f48f43644c119bf80055 Mon Sep 17 00:00:00 2001 From: Virgil Date: Thu, 2 Apr 2026 01:58:31 +0000 Subject: [PATCH] feat(ansible): expose loop label without extended metadata Co-Authored-By: Virgil --- executor.go | 53 ++++++++++++++++++++++++------------------------ executor_test.go | 27 ++++++++++++++++++++++++ 2 files changed, 54 insertions(+), 26 deletions(-) diff --git a/executor.go b/executor.go index cfd60c2..23f1dd8 100644 --- a/executor.go +++ b/executor.go @@ -911,7 +911,7 @@ func (e *Executor) runLoop(ctx context.Context, host string, client sshExecutorC } } var savedLoopMeta any - if task.LoopControl != nil && task.LoopControl.Extended { + if task.LoopControl != nil && (task.LoopControl.Extended || task.LoopControl.Label != "") { if v, ok := e.vars["ansible_loop"]; ok { savedLoopMeta = v } @@ -924,30 +924,31 @@ func (e *Executor) runLoop(ctx context.Context, host string, client sshExecutorC if indexVar != "" { e.vars[indexVar] = i } - if task.LoopControl != nil && task.LoopControl.Extended { - var prevItem any - if i > 0 { - prevItem = items[i-1] - } - var nextItem any - if i+1 < len(items) { - nextItem = items[i+1] - } - loopMeta := map[string]any{ - "index": i + 1, - "index0": i, - "first": i == 0, - "last": i == len(items)-1, - "length": len(items), - "revindex": len(items) - i, - "revindex0": len(items) - i - 1, - "allitems": append([]any(nil), items...), - } - if prevItem != nil { - loopMeta["previtem"] = prevItem - } - if nextItem != nil { - loopMeta["nextitem"] = nextItem + if task.LoopControl != nil && (task.LoopControl.Extended || task.LoopControl.Label != "") { + loopMeta := map[string]any{} + if task.LoopControl.Extended { + var prevItem any + if i > 0 { + prevItem = items[i-1] + } + var nextItem any + if i+1 < len(items) { + nextItem = items[i+1] + } + loopMeta["index"] = i + 1 + loopMeta["index0"] = i + loopMeta["first"] = i == 0 + loopMeta["last"] = i == len(items)-1 + loopMeta["length"] = len(items) + loopMeta["revindex"] = len(items) - i + loopMeta["revindex0"] = len(items) - i - 1 + loopMeta["allitems"] = append([]any(nil), items...) + if prevItem != nil { + loopMeta["previtem"] = prevItem + } + if nextItem != nil { + loopMeta["nextitem"] = nextItem + } } if task.LoopControl.Label != "" { loopMeta["label"] = e.templateString(task.LoopControl.Label, host, task) @@ -991,7 +992,7 @@ func (e *Executor) runLoop(ctx context.Context, host string, client sshExecutorC delete(e.vars, indexVar) } } - if task.LoopControl != nil && task.LoopControl.Extended { + if task.LoopControl != nil && (task.LoopControl.Extended || task.LoopControl.Label != "") { if savedLoopMeta != nil { e.vars["ansible_loop"] = savedLoopMeta } else { diff --git a/executor_test.go b/executor_test.go index 1b97e27..37e0667 100644 --- a/executor_test.go +++ b/executor_test.go @@ -840,6 +840,33 @@ func TestExecutor_RunTaskOnHost_Good_LoopControlExtendedExposesMetadata(t *testi assert.Equal(t, "two 1/2 first=false last=true", result.Results[1].Msg) } +func TestExecutor_RunTaskOnHost_Good_LoopControlLabelWithoutExtended(t *testing.T) { + e := NewExecutor("/tmp") + e.clients["host1"] = NewMockSSHClient() + + task := &Task{ + Name: "Label-only loop metadata", + Module: "debug", + Args: map[string]any{ + "msg": "{{ ansible_loop.label }}={{ item }}", + }, + Loop: []any{"one", "two"}, + LoopControl: &LoopControl{ + Label: "{{ item }}", + }, + Register: "loop_result", + } + + err := e.runTaskOnHosts(context.Background(), []string{"host1"}, task, &Play{}) + require.NoError(t, err) + + result := e.results["host1"]["loop_result"] + require.NotNil(t, result) + require.Len(t, result.Results, 2) + assert.Equal(t, "one=one", result.Results[0].Msg) + assert.Equal(t, "two=two", result.Results[1].Msg) +} + func TestExecutor_RunTaskOnHost_Good_LoopControlExtendedExposesNeighbourItems(t *testing.T) { e := NewExecutor("/tmp") e.clients["host1"] = NewMockSSHClient()