diff --git a/internal/cmd/params_validation.go b/internal/cmd/params_validation.go index 2274b3f80..a55316b18 100644 --- a/internal/cmd/params_validation.go +++ b/internal/cmd/params_validation.go @@ -26,7 +26,7 @@ func buildStartValidationInput(ctx *Context, args []string) (core.StartParamInpu if argsLenAtDash >= len(args) { return core.StartParamInput{}, nil } - return core.StartParamInput{DashArgs: args[argsLenAtDash:]}, nil + return core.StartParamInput{DashArgs: quoteStartDashArgs(args[argsLenAtDash:])}, nil } raw, err := ctx.Command.Flags().GetString("params") diff --git a/internal/cmd/start.go b/internal/cmd/start.go index 1a630e2f2..9f0772b08 100644 --- a/internal/cmd/start.go +++ b/internal/cmd/start.go @@ -340,7 +340,9 @@ func loadDAGWithParams(ctx *Context, args []string, isSubDAGRun bool) (*core.DAG var params string if ctx.Command.ArgsLenAtDash() != -1 && len(args) > 0 { - loadOpts = append(loadOpts, spec.WithParams(args[ctx.Command.ArgsLenAtDash():])) + dashArgs := args[ctx.Command.ArgsLenAtDash():] + loadOpts = append(loadOpts, spec.WithParams(quoteStartDashArgs(dashArgs))) + params = strings.Join(dashArgs, " ") } else { params, err = ctx.Command.Flags().GetString("params") if err != nil { diff --git a/internal/cmd/start_params.go b/internal/cmd/start_params.go new file mode 100644 index 000000000..56e61dbcd --- /dev/null +++ b/internal/cmd/start_params.go @@ -0,0 +1,37 @@ +// Copyright (C) 2026 Yota Hamada +// SPDX-License-Identifier: GPL-3.0-or-later + +package cmd + +import ( + "encoding/json" + "strings" + + "github.com/dagucloud/dagu/internal/cmn/stringutil" + "github.com/dagucloud/dagu/internal/core/spec" +) + +func quoteStartDashArgs(args []string) []string { + if isSingleJSONDashArg(args) { + return args + } + return spec.QuoteRuntimeParams(args, nil) +} + +func isSingleJSONDashArg(args []string) bool { + if len(args) != 1 { + return false + } + + input := strings.TrimSpace(stringutil.RemoveQuotes(args[0])) + if input == "" { + return false + } + + isObject := strings.HasPrefix(input, "{") && strings.HasSuffix(input, "}") + isArray := strings.HasPrefix(input, "[") && strings.HasSuffix(input, "]") + if !isObject && !isArray { + return false + } + return json.Valid([]byte(input)) +} diff --git a/internal/cmd/start_test.go b/internal/cmd/start_test.go index c9bf35455..a671e33c2 100644 --- a/internal/cmd/start_test.go +++ b/internal/cmd/start_test.go @@ -36,6 +36,11 @@ steps: - name: "1" run: "echo \"params is $1 and $2\"" `) + dagStartWithSingleParam := th.DAG(t, `params: "p1" +steps: + - name: "1" + run: "echo \"params is $1\"" +`) dagStartWithDAGRunID := th.DAG(t, `steps: - name: "1" @@ -63,6 +68,11 @@ steps: Args: []string{"start", dagStartWithParams.Location, "--", "p5", "p6"}, ExpectedOut: []string{`params="[1=p5 2=p6`}, }, + { + Name: "StartDAGWithSpacedParamAfterDash", + Args: []string{"start", dagStartWithSingleParam.Location, "--", "Something here"}, + ExpectedOut: []string{`params="[1=Something here]"`}, + }, { Name: "StartDAGWithRequestID", Args: []string{"start", dagStartWithDAGRunID.Location, "--run-id", "CfmC9GPywTC24bXbY1yEU7eQANNvpdxAPJXdSKTSaCVC"}, @@ -204,6 +214,14 @@ steps: Args: []string{"start", dagFile, "--", `{"KEY":"value"}`}, }) require.NoError(t, err) + + dag, err := spec.Load(th.Context, dagFile) + require.NoError(t, err) + + status, err := th.DAGRunMgr.GetLatestStatus(th.Context, dag) + require.NoError(t, err) + require.Equal(t, core.Succeeded, status.Status) + require.Contains(t, status.Params, "KEY=value") }) t.Run("AllowsNamedPairsWhenNoParamsDeclared", func(t *testing.T) {