From 4b3cfbef8d6da6aff0f8adba052844a211e42f67 Mon Sep 17 00:00:00 2001 From: Virgil Date: Fri, 3 Apr 2026 11:57:28 +0000 Subject: [PATCH] Support mixed legacy action syntax --- parser.go | 33 ++++++++++++++++++++++----------- types_test.go | 31 +++++++++++++++++++++++++++++++ 2 files changed, 53 insertions(+), 11 deletions(-) diff --git a/parser.go b/parser.go index ea3af9f..8aae8bf 100644 --- a/parser.go +++ b/parser.go @@ -5,7 +5,6 @@ import ( "maps" "slices" "strings" - "unicode" coreio "dappco.re/go/core/io" coreerr "dappco.re/go/core/log" @@ -671,26 +670,38 @@ func parseActionSpecString(raw string) (string, map[string]any) { } args := make(map[string]any) - allKeyValues := true - for _, part := range parts[start:] { + freeFormStart := len(parts) + for i, part := range parts[start:] { key, value, ok := strings.Cut(part, "=") if !ok || key == "" { - allKeyValues = false + freeFormStart = start + i break } args[key] = value } - if allKeyValues && len(args) > 0 { - return module, args - } - - sepIndex := strings.IndexFunc(raw, unicode.IsSpace) - if sepIndex < 0 || sepIndex >= len(raw)-1 { + if freeFormStart == len(parts) { + if len(args) > 0 { + return module, args + } return module, nil } - return module, map[string]any{"_raw_params": strings.TrimSpace(raw[sepIndex:])} + if freeFormStart < len(parts) { + rawParams := strings.Join(parts[freeFormStart:], " ") + if rawParams != "" { + if len(args) == 0 { + return module, map[string]any{"_raw_params": rawParams} + } + args["_raw_params"] = rawParams + } + } + + if len(args) == 0 { + return module, nil + } + + return module, args } // expandNestedLoop converts with_nested input into a loop of cartesian diff --git a/types_test.go b/types_test.go index f467d2d..decde42 100644 --- a/types_test.go +++ b/types_test.go @@ -350,6 +350,21 @@ action: module=copy src=/tmp/source dest=/tmp/dest mode=0644 assert.Equal(t, "0644", task.Args["mode"]) } +func TestTypes_Task_UnmarshalYAML_Good_ActionAliasMixedArgs(t *testing.T) { + input := ` +name: Legacy action with mixed args +action: command chdir=/tmp echo hello world +` + var task Task + err := yaml.Unmarshal([]byte(input), &task) + + require.NoError(t, err) + assert.Equal(t, "command", task.Module) + require.NotNil(t, task.Args) + assert.Equal(t, "/tmp", task.Args["chdir"]) + assert.Equal(t, "echo hello world", task.Args["_raw_params"]) +} + func TestTypes_Task_UnmarshalYAML_Good_LocalAction(t *testing.T) { input := ` name: Legacy local action @@ -395,6 +410,22 @@ local_action: module=command chdir=/tmp assert.Equal(t, "/tmp", task.Args["chdir"]) } +func TestTypes_Task_UnmarshalYAML_Good_LocalActionMixedArgs(t *testing.T) { + input := ` +name: Legacy local action with mixed args +local_action: command chdir=/var/tmp echo local +` + var task Task + err := yaml.Unmarshal([]byte(input), &task) + + require.NoError(t, err) + assert.Equal(t, "command", task.Module) + assert.Equal(t, "localhost", task.Delegate) + require.NotNil(t, task.Args) + assert.Equal(t, "/var/tmp", task.Args["chdir"]) + assert.Equal(t, "echo local", task.Args["_raw_params"]) +} + func TestTypes_Task_UnmarshalYAML_Good_WithNested(t *testing.T) { input := ` name: Nested loop values