diff --git a/cmd/dry.go b/cmd/dry.go index 7a90aeaf6..7bbdc5a59 100644 --- a/cmd/dry.go +++ b/cmd/dry.go @@ -36,7 +36,6 @@ func dryCmd() *cobra.Command { Run: func(cmd *cobra.Command, args []string) { cfg, err := config.Load() if err != nil { - // nolint log.Fatalf("Failed to load config: %v", err) } initLogger := logger.NewLogger(logger.NewLoggerArgs{ diff --git a/cmd/scheduler.go b/cmd/scheduler.go index 4d51f976b..e5ef45049 100644 --- a/cmd/scheduler.go +++ b/cmd/scheduler.go @@ -34,7 +34,6 @@ func schedulerCmd() *cobra.Command { Run: func(cmd *cobra.Command, _ []string) { cfg, err := config.Load() if err != nil { - // nolint log.Fatalf("Configuration load failed: %v", err) } logger := logger.NewLogger(logger.NewLoggerArgs{ diff --git a/cmd/server.go b/cmd/server.go index 51686e9e5..2889bee30 100644 --- a/cmd/server.go +++ b/cmd/server.go @@ -39,7 +39,6 @@ func serverCmd() *cobra.Command { Run: func(cmd *cobra.Command, _ []string) { cfg, err := config.Load() if err != nil { - // nolint log.Fatalf("Configuration load failed: %v", err) } logger := logger.NewLogger(logger.NewLoggerArgs{ @@ -53,7 +52,6 @@ func serverCmd() *cobra.Command { cli := newClient(cfg, dataStore, logger) server := frontend.New(cfg, logger, cli) if err := server.Serve(cmd.Context()); err != nil { - // nolint logger.Error("Server initialization failed", "error", err) os.Exit(1) } diff --git a/cmd/start.go b/cmd/start.go index 1af8f2344..f38002aba 100644 --- a/cmd/start.go +++ b/cmd/start.go @@ -36,7 +36,6 @@ func startCmd() *cobra.Command { Run: func(cmd *cobra.Command, args []string) { cfg, err := config.Load() if err != nil { - // nolint log.Fatalf("Configuration load failed: %v", err) } diff --git a/cmd/start_all.go b/cmd/start_all.go index 058b88680..a49c3f0f3 100644 --- a/cmd/start_all.go +++ b/cmd/start_all.go @@ -29,11 +29,9 @@ import ( func startAllCmd() *cobra.Command { cmd := &cobra.Command{ - Use: "start-all", - // nolint + Use: "start-all", Short: "Launches both the Dagu web UI server and the scheduler process.", - // nolint - Long: `dagu start-all [--dags=] [--host=] [--port=]`, + Long: `dagu start-all [--dags=] [--host=] [--port=]`, PreRun: func(cmd *cobra.Command, _ []string) { _ = viper.BindPFlag("port", cmd.Flags().Lookup("port")) _ = viper.BindPFlag("host", cmd.Flags().Lookup("host")) @@ -42,7 +40,6 @@ func startAllCmd() *cobra.Command { Run: func(cmd *cobra.Command, _ []string) { cfg, err := config.Load() if err != nil { - // nolint log.Fatalf("Configuration load failed: %v", err) } logger := logger.NewLogger(logger.NewLoggerArgs{ diff --git a/cmd/status.go b/cmd/status.go index 533df9234..9c823f008 100644 --- a/cmd/status.go +++ b/cmd/status.go @@ -34,7 +34,6 @@ func statusCmd() *cobra.Command { Run: func(_ *cobra.Command, args []string) { cfg, err := config.Load() if err != nil { - // nolint log.Fatalf("Configuration load failed: %v", err) } logger := logger.NewLogger(logger.NewLoggerArgs{ @@ -45,7 +44,6 @@ func statusCmd() *cobra.Command { // Load the DAG file and get the current running status. workflow, err := dag.Load(cfg.BaseConfig, args[0], "") if err != nil { - // nolint logger.Error("Workflow load failed", "error", err, "file", args[0]) os.Exit(1) } @@ -56,7 +54,6 @@ func statusCmd() *cobra.Command { curStatus, err := cli.GetCurrentStatus(workflow) if err != nil { - // nolint logger.Error("Current status retrieval failed", "error", err) os.Exit(1) } diff --git a/cmd/version.go b/cmd/version.go index 441073530..0c38522b8 100644 --- a/cmd/version.go +++ b/cmd/version.go @@ -26,7 +26,6 @@ func versionCmd() *cobra.Command { Short: "Display the binary version", Long: `dagu version`, Run: func(_ *cobra.Command, _ []string) { - // nolint // forbidgo println(constants.Version) }, } diff --git a/examples/foreach.yaml b/examples/foreach.yaml new file mode 100644 index 000000000..c437b1062 --- /dev/null +++ b/examples/foreach.yaml @@ -0,0 +1,11 @@ +steps: + - name: foreach + foreach: "[1, 2, 3]" + command: sh + output: "foreach-$1.txt" + script: | + echo "Hello, world! $1" + - name: merge + command: sh + script: | + cat foreach-*.txt diff --git a/internal/client/client_test.go b/internal/client/client_test.go index 3229d3352..b6d1a1e43 100644 --- a/internal/client/client_test.go +++ b/internal/client/client_test.go @@ -152,7 +152,6 @@ func TestClient_GetStatus(t *testing.T) { }) } -// nolint // paralleltest func TestClient_RunDAG(t *testing.T) { t.Run("RunDAG", func(t *testing.T) { setup := test.SetupTest(t) diff --git a/internal/dag/builder.go b/internal/dag/builder.go index c84e50ba0..f08403120 100644 --- a/internal/dag/builder.go +++ b/internal/dag/builder.go @@ -82,11 +82,11 @@ var ( errStepCommandMustBeArrayOrString = errors.New( "step command must be an array of strings or a string", ) + errInvalidParamValue = errors.New("invalid parameter value") errCallFunctionNotFound = errors.New( "call must specify a functions that exists", ) errNumberOfParamsMismatch = errors.New( - // nolint "the number of parameters defined in the function does not match the number of parameters given", ) errRequiredParameterNotFound = errors.New( @@ -362,7 +362,6 @@ func (b *builder) buildMiscs() (err error) { // Case 2: env is an array of maps. // Case 3: is recommended because the order of the environment variables is // preserved. -// nolint // cognitive complexity func loadVariables(strVariables any, opts buildOpts) ( map[string]string, error, ) { @@ -471,7 +470,6 @@ type stepBuilder struct { } // buildStep builds a step from the step definition. -// nolint // cognitive complexity func (b *stepBuilder) buildStep( variables []string, def *stepDef, fns []*funcDef, ) (*Step, error) { diff --git a/internal/dag/builder_test.go b/internal/dag/builder_test.go index b5325c3cd..81f4cc9f9 100644 --- a/internal/dag/builder_test.go +++ b/internal/dag/builder_test.go @@ -16,422 +16,320 @@ package dag import ( - "fmt" + "errors" "os" "path/filepath" - "reflect" "testing" + "time" + "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" ) -func TestBuilder_BuildErrors(t *testing.T) { - tests := []struct { - name string - input string - }{ +func TestBuilder_Build(t *testing.T) { + tests := []testCase{ { - name: "NoName", - input: ` -steps: - - command: echo 1`, + Name: "NoName", + InputFile: "no_name.yaml", + ExpectedErr: errStepNameRequired, }, { - name: "NoCommand", - input: ` -steps: - - name: step 1`, + Name: "NoCommand", + InputFile: "no_command.yaml", + ExpectedErr: errStepCommandOrCallRequired, }, { - name: "InvalidEnv", - input: fmt.Sprintf(` -env: - - VAR: %q`, "`invalid`"), + Name: "InvalidEnv", + InputFile: "invalid_env.yaml", + ExpectedErr: errInvalidEnvValue, }, { - name: "InvalidParams", - input: fmt.Sprintf(`params: %q`, "`invalid`"), + Name: "InvalidParams", + InputFile: "invalid_params.yaml", + ExpectedErr: errInvalidParamValue, }, { - name: "InvalidSchedule", - input: `schedule: "1"`, + Name: "InvalidSchedule", + InputFile: "invalid_schedule.yaml", + ExpectedErr: errInvalidSchedule, }, - } - - for _, tt := range tests { - t.Run(tt.name, func(t *testing.T) { - dg, err := unmarshalData([]byte(tt.input)) - require.NoError(t, err) - - def, err := decode(dg) - require.NoError(t, err) - - _, err = new(builder).build(def, nil) - require.Error(t, err) - }) - } -} - -func TestBuilder_BuildEnvs(t *testing.T) { - tests := []struct { - name string - input string - expected map[string]string - }{ { - name: "ValidEnv", - input: ` -env: - "1": "123" -`, - expected: map[string]string{"1": "123"}, + Name: "ValidEnv", + InputFile: "valid_env.yaml", + Expected: map[string]any{ + "env": map[string]string{"FOO": "123"}, + }, }, { - name: "ValidEnvWithSubstitution", - input: ` -env: - VAR: "` + "`echo 1`" + `" -`, - expected: map[string]string{"VAR": "1"}, + Name: "ValidEnvWithSubstitution", + InputFile: "valid_env_substitution.yaml", + Expected: map[string]any{ + "env": map[string]string{"VAR": "123"}, + }, }, { - name: "ValidEnvWithSubstitutionAndEnv", - input: ` -env: - - "FOO": "BAR" - - "FOO": "${FOO}:BAZ" - - "FOO": "${FOO}:BAR" - - "FOO": "${FOO}:FOO" -`, - expected: map[string]string{"FOO": "BAR:BAZ:BAR:FOO"}, + Name: "ValidEnvWithSubstitutionAndEnv", + InputFile: "valid_env_substitution_and_env.yaml", + Expected: map[string]any{ + "env": map[string]string{"FOO": "BAR:BAZ:BAR:FOO"}, + }, }, - } - - for _, tt := range tests { - t.Run(tt.name, func(t *testing.T) { - dg, err := unmarshalData([]byte(tt.input)) - require.NoError(t, err) - - def, err := decode(dg) - require.NoError(t, err) - - _, err = new(builder).build(def, nil) - require.NoError(t, err) - - for k, v := range tt.expected { - require.Equal(t, v, os.Getenv(k)) - } - }) - } -} - -func TestBuilder_BuildParams(t *testing.T) { - tests := []struct { - name string - params string - env string - expected map[string]string - }{ { - name: "ValidParams", - params: "x", - expected: map[string]string{ - "1": "x", + Name: "ValidCommand", + InputFile: "valid_command.yaml", + Expected: map[string]any{ + "steps": []stepTestCase{ + { + "command": "echo", + "args": []string{"1"}, + "name": "step 1", + }, + }, }, }, { - name: "TwoParams", - params: "x y", - expected: map[string]string{ - "1": "x", - "2": "y", + Name: "ValidCommandInArray", + InputFile: "valid_command_in_array.yaml", + Expected: map[string]any{ + "steps": []stepTestCase{ + { + "command": "echo", + "args": []string{"1"}, + "name": "step 1", + }, + }, }, }, { - name: "ThreeParams", - params: "x yy zzz", - expected: map[string]string{ - "1": "x", - "2": "yy", - "3": "zzz", + Name: "ValidCommandInList", + InputFile: "valid_command_in_list.yaml", + Expected: map[string]any{ + "steps": []stepTestCase{ + { + "command": "echo", + "args": []string{"1"}, + "name": "step 1", + }, + }, }, }, { - name: "ParamsWithSubstitution", - params: "x $1", - expected: map[string]string{ - "1": "x", - "2": "x", + Name: "ValidTags", + InputFile: "valid_tags.yaml", + Expected: map[string]any{ + "tags": []string{"daily", "monthly"}, }, }, { - name: "QuotedParams", - params: `x="1" y="2"`, - expected: map[string]string{ - "x": "1", - "y": "2", + Name: "ValidTagsList", + InputFile: "valid_tags_list.yaml", + Expected: map[string]any{ + "tags": []string{"daily", "monthly"}, }, }, { - name: "ComplexParams", - params: "first P1=foo P2=${FOO} P3=`/bin/echo BAR` X=bar Y=${P1} Z=\"A B C\"", - env: "FOO: BAR", - expected: map[string]string{ - "P1": "foo", - "P2": "BAR", - "P3": "BAR", - "X": "bar", - "Y": "foo", - "Z": "A B C", - "1": "first", - "2": `P1=foo`, - "3": `P2=BAR`, - "4": `P3=BAR`, - "5": `X=bar`, - "6": `Y=foo`, - "7": `Z=A B C`, + Name: "ValidSchedule", + InputFile: "valid_schedule.yaml", + Expected: map[string]any{ + "schedule": map[string][]string{ + "start": {"0 1 * * *"}, + "stop": {"0 2 * * *"}, + "restart": {"0 12 * * *"}, + }, }, }, - } - - for _, tt := range tests { - t.Run(tt.name, func(t *testing.T) { - var data string - if tt.env != "" { - data = fmt.Sprintf(`env: -- %s -params: %s -`, tt.env, tt.params) - } else { - data = fmt.Sprintf(`params: %s -`, tt.params) - } - dg, err := unmarshalData([]byte(data)) - require.NoError(t, err) - - def, err := decode(dg) - require.NoError(t, err) - - _, err = new(builder).build(def, nil) - require.NoError(t, err) - - for k, v := range tt.expected { - require.Equal(t, v, os.Getenv(k)) - } - }) - } -} - -func TestBuilder_BuildCommand(t *testing.T) { - tests := []struct { - name string - input string - }{ { - name: "ValidCommand", - input: ` -steps: - - name: step1 - command: echo 1`, + Name: "ScheduleWithMultipleValues", + InputFile: "schedule_with_multiple_values.yaml", + Expected: map[string]any{ + "schedule": map[string][]string{ + "start": { + "0 1 * * *", + "0 18 * * *", + }, + "stop": { + "0 2 * * *", + "0 20 * * *", + }, + "restart": { + "0 12 * * *", + "0 22 * * *", + }, + }, + }, }, { - name: "ValidCommandInArray", - input: ` -steps: - - name: step1 - command: ['echo', '1']`, + Name: "HTTPExecutor", + InputFile: "http_executor.yaml", + Expected: map[string]any{ + "steps": []stepTestCase{ + { + "executor": "http", + }, + }, + }, }, { - name: "ValidCommandInJSONArray", - input: ` -steps: - - name: step1 - command: [echo, 1]`, + Name: "HTTPExecutorWithConfig", + InputFile: "http_executor_with_config.yaml", + Expected: map[string]any{ + "steps": []stepTestCase{ + { + "executor": "http", + "executorConfig": map[string]any{ + "key": "value", + }, + }, + }, + }, }, { - name: "ValidCommandInYAMLArray", - input: ` -steps: - - name: step1 - command: - - echo - - 1`, + Name: "SignalOnStop", + InputFile: "signal_on_stop.yaml", + Expected: map[string]any{ + "steps": []stepTestCase{ + { + "signalOnStop": "SIGINT", + }, + }, + }, }, - } - - for _, tt := range tests { - t.Run(tt.name, func(t *testing.T) { - dg, err := unmarshalData([]byte(tt.input)) - require.NoError(t, err) - - def, err := decode(dg) - require.NoError(t, err) - - dag, err := new(builder).build(def, nil) - if err != nil { - t.Fatalf("unexpected error: %v", err) - } - - if len(dag.Steps) != 1 { - t.Fatalf("expected 1 step, got %d", len(dag.Steps)) - } - - step := dag.Steps[0] - require.Equal(t, "echo", step.Command) - require.Equal(t, []string{"1"}, step.Args) - }) - } -} - -func Test_expandEnv(t *testing.T) { - t.Run("ExpandEnv", func(t *testing.T) { - _ = os.Setenv("FOO", "BAR") - require.Equal(t, expandEnv("${FOO}", false), "BAR") - require.Equal(t, expandEnv("${FOO}", true), "${FOO}") - }) -} - -func TestBuilder_BuildTags(t *testing.T) { - t.Run("ValidTags", func(t *testing.T) { - input := `tags: Daily, Monthly` - expected := []string{"daily", "monthly"} - - m, err := unmarshalData([]byte(input)) - require.NoError(t, err) - - def, err := decode(m) - require.NoError(t, err) - - dg, err := new(builder).build(def, nil) - require.NoError(t, err) - - for _, tag := range expected { - require.True(t, dg.HasTag(tag)) - } - - require.False(t, dg.HasTag("weekly")) - }) -} - -func TestBuilder_BuildSchedule(t *testing.T) { - tests := []struct { - name string - input string - wantErr bool - expected map[string][]string - }{ { - name: "ValidSchedule", - input: ` -schedule: - start: "0 1 * * *" - stop: "0 2 * * *" - restart: "0 12 * * *" -`, - expected: map[string][]string{ - "start": {"0 1 * * *"}, - "stop": {"0 2 * * *"}, - "restart": {"0 12 * * *"}, + Name: "ParamsWithSubstitution", + InputFile: "params_with_substitution.yaml", + Expected: map[string]any{ + "env": map[string]string{ + "1": "x", + "2": "x", + }, }, }, { - name: "OnlyStartSchedule", - input: ` -schedule: - start: "0 1 * * *" -`, - expected: map[string][]string{ - "start": {"0 1 * * *"}, + Name: "ParamsWithQuotedValues", + InputFile: "params_with_quoted_values.yaml", + Expected: map[string]any{ + "env": map[string]string{ + "x": "a b c", + "y": "d e f", + }, }, }, { - name: "OnlyStopSchedule", - input: `schedule: - stop: "0 1 * * *" -`, - expected: map[string][]string{ - "stop": {"0 1 * * *"}, + Name: "ParamsWithComplexValues", + InputFile: "params_with_complex_values.yaml", + Expected: map[string]any{ + "env": map[string]string{ + "P1": "foo", + "P2": "BAR", + "P3": "BAR", + "X": "bar", + "Y": "foo", + "Z": "A B C", + "1": "first", + "2": `P1=foo`, + "3": `P2=BAR`, + "4": `P3=BAR`, + "5": `X=bar`, + "6": `Y=foo`, + "7": `Z=A B C`, + }, }, }, { - name: "MultipleSchedules", - input: ` -schedule: - start: - - "0 1 * * *" - - "0 18 * * *" - stop: - - "0 2 * * *" - - "0 20 * * *" -`, - expected: map[string][]string{ - "start": {"0 1 * * *", "0 18 * * *"}, - "stop": {"0 2 * * *", "0 20 * * *"}, + Name: "ValidHandlers", + InputFile: "valid_handlers.yaml", + Expected: map[string]any{ + "handlers": map[string]stepTestCase{ + "exit": { + "name": "onExit", + "command": "echo", + "args": []string{"exit"}, + }, + "success": { + "name": "onSuccess", + "command": "echo", + "args": []string{"success"}, + }, + "failure": { + "name": "onFailure", + "command": "echo", + "args": []string{"failure"}, + }, + "cancel": { + "name": "onCancel", + "command": "echo", + "args": []string{"cancel"}, + }, + }, }, }, { - name: "InvalidCronExp", - input: ` -schedule: - stop: "* * * * * * *" -`, - wantErr: true, + Name: "ValidMailConfig", + InputFile: "valid_mail_config.yaml", + Expected: map[string]any{ + "smtp": map[string]string{ + "host": "smtp.example.com", + "port": "587", + "username": "user@example.com", + "password": "password", + }, + "errorMail": map[string]any{ + "from": "error@example.com", + "to": "admin@example.com", + "prefix": "[ERROR]", + "attachLogs": true, + }, + "infoMail": map[string]any{ + "from": "info@example.com", + "to": "user@example.com", + "prefix": "[INFO]", + "attachLogs": false, + }, + }, + }, + { + Name: "ValidSubWorkflow", + InputFile: "valid_subworkflow.yaml", + Expected: map[string]any{ + "steps": []stepTestCase{ + { + "name": "sub_workflow_step", + "command": "run", + "args": []string{"sub_dag", "param1=value1 param2=value2"}, + "executor": "subworkflow", + "subWorkflow": map[string]string{ + "name": "sub_dag", + "params": "param1=value1 param2=value2", + }, + }, + }, + }, }, { - name: "InvalidKey", - input: ` -schedule: - invalid: "* * * * * * *" -`, - wantErr: true, + Name: "ValidMiscs", + InputFile: "valid_miscs.yaml", + Expected: map[string]any{ + "histRetentionDays": 7, + "maxActiveRuns": 3, + "maxCleanUpTime": time.Duration(300 * time.Second), + "preconditions": []Condition{ + {Condition: "test -f file.txt", Expected: "true"}, + }, + }, }, } - for _, tt := range tests { - t.Run(tt.name, func(t *testing.T) { - m, err := unmarshalData([]byte(tt.input)) - require.NoError(t, err) - - def, err := decode(m) - require.NoError(t, err) - - dg, err := new(builder).build(def, nil) - - if tt.wantErr { - require.Error(t, err) - return - } - require.NoError(t, err) - - for k, v := range tt.expected { - var actual []Schedule - switch scheduleKey(k) { - case scheduleKeyStart: - actual = dg.Schedule - case scheduleKeyStop: - actual = dg.StopSchedule - case scheduleKeyRestart: - actual = dg.RestartSchedule - } - - if len(actual) != len(v) { - t.Errorf("expected %d schedules, got %d", len(v), len(actual)) - } - - for i, s := range actual { - if s.Expression != v[i] { - t.Errorf("expected %s, got %s", v[i], s.Expression) - } - } - } + for _, tc := range tests { + t.Run(tc.Name, func(t *testing.T) { + runTest(t, tc) }) } } -func TestLoad(t *testing.T) { +func TestOverrideBaseConfig(t *testing.T) { // Base config has the following values: // MailOn: {Failure: true, Success: false} - t.Run("OverrideBaseConfig", func(t *testing.T) { + t.Run("Override", func(t *testing.T) { baseConfig := filepath.Join(testdataDir, "base.yaml") // Overwrite the base config with the following values: @@ -443,7 +341,7 @@ func TestLoad(t *testing.T) { require.Equal(t, &MailOn{Failure: false, Success: false}, dg.MailOn) require.Equal(t, dg.HistRetentionDays, 7) }) - t.Run("NoOverrideBaseConfig", func(t *testing.T) { + t.Run("WithoutOverride", func(t *testing.T) { baseConfig := filepath.Join(testdataDir, "base.yaml") // no_overwrite.yaml does not have the MailOn key. @@ -456,190 +354,173 @@ func TestLoad(t *testing.T) { }) } -func TestBuilder_BuildExecutor(t *testing.T) { - tests := []struct { - name string - input string - expectedExec string - expectedConfig map[string]any - }{ - { - name: "HTTPExecutor", - input: ` -steps: - - name: S1 - command: echo 1 - executor: http -`, - expectedExec: "http", - expectedConfig: nil, - }, - { - name: "HTTPExecutorWithConfig", - input: ` -steps: - - name: S1 - command: echo 1 - executor: - type: http - config: - key: value -`, - expectedExec: "http", - expectedConfig: map[string]any{ - "key": "value", - }, - }, - } - - for _, tt := range tests { - t.Run(tt.name, func(t *testing.T) { - dg, err := unmarshalData([]byte(tt.input)) - require.NoError(t, err) +type testCase struct { + Name string + InputFile string + Expected map[string]any + ExpectedErr error +} - def, err := decode(dg) - require.NoError(t, err) +type stepTestCase map[string]any - dag, err := new(builder).build(def, nil) - require.NoError(t, err) +func readTestFile(t *testing.T, filename string) []byte { + t.Helper() + data, err := os.ReadFile(filepath.Join(testdataDir, filename)) + require.NoError(t, err) + return data +} - if len(dag.Steps) != 1 { - t.Errorf("expected 1 step, got %d", len(dag.Steps)) +func runTest(t *testing.T, tc testCase) { + t.Helper() + dag, err := loadYAML(readTestFile(t, tc.InputFile), buildOpts{}) + + if tc.ExpectedErr != nil { + assert.Error(t, err) + if errs, ok := err.(*errorList); ok && len(*errs) > 0 { + // check if the error is in the list of errors + found := false + for _, e := range *errs { + if errors.Is(e, tc.ExpectedErr) { + found = true + break + } } - - require.Equal(t, tt.expectedExec, dag.Steps[0].ExecutorConfig.Type) - if tt.expectedConfig != nil { - require.Equal(t, tt.expectedConfig, dag.Steps[0].ExecutorConfig.Config) + if !found { + t.Errorf("expected error %v, got %v", tc.ExpectedErr, err) } - }) + } else if !errors.Is(err, tc.ExpectedErr) { + t.Errorf("expected error %v, got %v", tc.ExpectedErr, err) + } + return } -} - -const ( - testSignalOnStop = ` -steps: - - name: "1" - command: "true" - signalOnStop: "SIGINT" -` - testSignalOnStopInvalid = ` -steps: - - name: "1" - command: "true" - signalOnStop: 1000 -` -) -func TestBuilder_BuildSignalOnStop(t *testing.T) { - t.Run("SignalOnStop", func(t *testing.T) { - ret, err := LoadYAML([]byte(testSignalOnStop)) - require.NoError(t, err) - if len(ret.Steps) != 1 { - t.Fatalf("expected 1 step, got %d", len(ret.Steps)) + require.NoError(t, err) + for k, v := range tc.Expected { + switch k { + case "steps": + stepTestCases := v.([]stepTestCase) + require.Len(t, dag.Steps, len(stepTestCases)) + for i, step := range dag.Steps { + testStep(t, step, stepTestCases[i]) + } + case "env": + for envKey, envVal := range v.(map[string]string) { + assert.Equal(t, envVal, os.Getenv(envKey)) + } + case "tags": + for _, tag := range v.([]string) { + assert.True(t, dag.HasTag(tag)) + } + case "schedule": + schedules := v.(map[string][]string) + for scheduleType, expressions := range schedules { + var actual []Schedule + switch scheduleKey(scheduleType) { + case scheduleKeyStart: + actual = dag.Schedule + case scheduleKeyStop: + actual = dag.StopSchedule + case scheduleKeyRestart: + actual = dag.RestartSchedule + } + assert.Len(t, actual, len(expressions)) + for i, expr := range expressions { + assert.Equal(t, expr, actual[i].Expression) + } + } + case "histRetentionDays": + assert.Equal(t, v.(int), dag.HistRetentionDays) + case "maxActiveRuns": + assert.Equal(t, v.(int), dag.MaxActiveRuns) + case "maxCleanUpTime": + assert.Equal(t, v.(time.Duration), dag.MaxCleanUpTime) + case "preconditions": + assert.Equal(t, v.([]Condition), dag.Preconditions) + case "handlers": + for handlerName, handler := range v.(map[string]stepTestCase) { + switch handlerName { + case "exit": + testStep(t, *dag.HandlerOn.Exit, handler) + case "success": + testStep(t, *dag.HandlerOn.Success, handler) + case "failure": + testStep(t, *dag.HandlerOn.Failure, handler) + case "cancel": + testStep(t, *dag.HandlerOn.Cancel, handler) + default: + panic("unexpected handler: " + handlerName) + } + } + case "smtp": + for key, val := range v.(map[string]string) { + switch key { + case "host": + assert.Equal(t, val, dag.SMTP.Host) + case "port": + assert.Equal(t, val, dag.SMTP.Port) + case "username": + assert.Equal(t, val, dag.SMTP.Username) + case "password": + assert.Equal(t, val, dag.SMTP.Password) + default: + panic("unexpected smtp key: " + key) + } + } + case "errorMail": + testMailConfig(t, *dag.ErrorMail, v.(map[string]any)) + case "infoMail": + testMailConfig(t, *dag.InfoMail, v.(map[string]any)) + default: + panic("unexpected key: " + k) } - require.Equal(t, ret.Steps[0].SignalOnStop, "SIGINT") - }) - t.Run("InvalidSignal", func(t *testing.T) { - _, err := LoadYAML([]byte(testSignalOnStopInvalid)) - require.Error(t, err) - }) + } } -func Test_convertMap(t *testing.T) { - t.Run("ValidMap", func(t *testing.T) { - data := map[string]any{ - "key1": "value1", - "map": map[any]any{ - "key2": "value2", - "map": map[any]any{ - "key3": "value3", - }, - }, - } - - err := convertMap(data) - require.NoError(t, err) - - m1 := data["map"] - k1 := reflect.TypeOf(m1).Key().Kind() - require.True(t, k1 == reflect.String) - - m2 := data["map"].(map[string]any)["map"] - k2 := reflect.TypeOf(m2).Key().Kind() - require.True(t, k2 == reflect.String) - - expected := map[string]any{ - "key1": "value1", - "map": map[string]any{ - "key2": "value2", - "map": map[string]any{ - "key3": "value3", - }, - }, - } - require.Equal(t, expected, data) - }) - t.Run("InvalidMap", func(t *testing.T) { - data := map[string]any{ - "key1": "value1", - "map": map[any]any{ - 1: "value2", - }, +func testMailConfig(t *testing.T, mailConfig MailConfig, tc map[string]any) { + for key, val := range tc { + switch key { + case "from": + assert.Equal(t, val, mailConfig.From) + case "to": + assert.Equal(t, val, mailConfig.To) + case "prefix": + assert.Equal(t, val, mailConfig.Prefix) + case "attachLogs": + assert.Equal(t, val, mailConfig.AttachLogs) + default: + t.Errorf("unexpected mail key: %s", key) } - - err := convertMap(data) - require.Error(t, err) - }) -} - -func Test_evaluateValue(t *testing.T) { - tests := []struct { - name string - input string - expected string - wantErr bool - }{ - { - name: "EnvVar", - input: "${TEST_VAR}", - expected: "test", - }, - { - name: "CommandSubstitution", - input: "`echo test`", - expected: "test", - }, - { - name: "InvalidCommand", - input: "`ech test`", - wantErr: true, - }, } +} - // Set the environment variable for the tests - err := os.Setenv("TEST_VAR", "test") - require.NoError(t, err) - - for _, tt := range tests { - t.Run(tt.input, func(t *testing.T) { - r, err := substituteCommands(os.ExpandEnv(tt.input)) - if tt.wantErr { - require.Error(t, err) - return +func testStep(t *testing.T, step Step, tc stepTestCase) { + for k, v := range tc { + switch k { + case "name": + assert.Equal(t, v.(string), step.Name) + case "command": + assert.Equal(t, v.(string), step.Command) + case "args": + assert.Equal(t, v.([]string), step.Args) + case "executorConfig": + assert.Equal(t, v.(map[string]any), step.ExecutorConfig.Config) + case "executor": + assert.Equal(t, v.(string), step.ExecutorConfig.Type) + case "signalOnStop": + assert.Equal(t, v.(string), step.SignalOnStop) + case "subWorkflow": + for k, val := range v.(map[string]string) { + switch k { + case "name": + assert.Equal(t, val, step.SubWorkflow.Name) + case "params": + assert.Equal(t, val, step.SubWorkflow.Params) + default: + panic("unexpected subworkflow key: " + k) + } } - require.NoError(t, err) - require.Equal(t, tt.expected, r) - }) + default: + panic("unexpected key: " + k) + } } } - -func Test_parseParams(t *testing.T) { - t.Run("ParamsWithCommandSubstitution", func(t *testing.T) { - val := "QUESTION=\"what is your favorite activity?\"" - ret, err := parseParamValue(val, true) - require.NoError(t, err) - require.Equal(t, 1, len(ret)) - require.Equal(t, ret[0].name, "QUESTION") - require.Equal(t, ret[0].value, "what is your favorite activity?") - }) -} diff --git a/internal/dag/definition.go b/internal/dag/definition.go index d42264115..04c0a4a56 100644 --- a/internal/dag/definition.go +++ b/internal/dag/definition.go @@ -25,7 +25,7 @@ type definition struct { Schedule any LogDir string Env any - HandlerOn handerOnDef + HandlerOn handlerOnDef Functions []*funcDef Steps []*stepDef SMTP smtpConfigDef @@ -39,7 +39,7 @@ type definition struct { MaxActiveRuns int Params string MaxCleanUpTimeSec *int - Tags string + Tags any } type conditionDef struct { @@ -47,7 +47,7 @@ type conditionDef struct { Expected string } -type handerOnDef struct { +type handlerOnDef struct { Failure *stepDef Success *stepDef Cancel *stepDef diff --git a/internal/dag/executor/docker.go b/internal/dag/executor/docker.go index 7bdd83e0e..4b9a418d4 100644 --- a/internal/dag/executor/docker.go +++ b/internal/dag/executor/docker.go @@ -166,7 +166,6 @@ func newDocker( execCfg := step.ExecutorConfig if cfg, ok := execCfg.Config["container"]; ok { - // nolint // See https://pkg.go.dev/github.com/docker/docker/api/types/container#Config md, err := mapstructure.NewDecoder(&mapstructure.DecoderConfig{ Result: containerConfig, @@ -182,7 +181,6 @@ func newDocker( } if cfg, ok := execCfg.Config["host"]; ok { - // nolint // See https://pkg.go.dev/github.com/docker/docker/api/types/container#HostConfig md, err := mapstructure.NewDecoder(&mapstructure.DecoderConfig{ Result: hostConfig, diff --git a/internal/dag/executor/subworkflow.go b/internal/dag/executor/sub.go similarity index 97% rename from internal/dag/executor/subworkflow.go rename to internal/dag/executor/sub.go index 718f2f3e7..526d4ce31 100644 --- a/internal/dag/executor/subworkflow.go +++ b/internal/dag/executor/sub.go @@ -36,7 +36,7 @@ type subWorkflow struct { var errWorkingDirNotExist = fmt.Errorf("working directory does not exist") -func newSubworkflow( +func newSubWorkflow( ctx context.Context, step dag.Step, ) (Executor, error) { executable, err := os.Executable() @@ -113,5 +113,5 @@ func (e *subWorkflow) Kill(sig os.Signal) error { } func init() { - Register(dag.ExecutorTypeSubWorkflow, newSubworkflow) + Register(dag.ExecutorTypeSubWorkflow, newSubWorkflow) } diff --git a/internal/dag/loader.go b/internal/dag/loader.go index 9c7205837..3e4dab725 100644 --- a/internal/dag/loader.go +++ b/internal/dag/loader.go @@ -64,7 +64,17 @@ func LoadMetadata(dag string) (*DAG, error) { } // LoadYAML loads config from YAML data. +// It does not evaluate the environment variables. +// This is used to validate the YAML data. func LoadYAML(data []byte) (*DAG, error) { + return loadYAML(data, buildOpts{ + metadataOnly: false, + noEval: true, + }) +} + +// LoadYAML loads config from YAML data. +func loadYAML(data []byte, opts buildOpts) (*DAG, error) { raw, err := unmarshalData(data) if err != nil { return nil, err @@ -75,7 +85,7 @@ func LoadYAML(data []byte) (*DAG, error) { return nil, err } - b := &builder{opts: buildOpts{metadataOnly: false, noEval: true}} + b := &builder{opts: opts} return b.build(def, nil) } diff --git a/internal/dag/loader_test.go b/internal/dag/loader_test.go index a0fbce1b7..4ecde6c3c 100644 --- a/internal/dag/loader_test.go +++ b/internal/dag/loader_test.go @@ -123,7 +123,7 @@ steps: func Test_LoadYAML(t *testing.T) { t.Run("ValidYAMLData", func(t *testing.T) { - ret, err := LoadYAML([]byte(testDAG)) + ret, err := loadYAML([]byte(testDAG), buildOpts{}) require.NoError(t, err) require.Equal(t, ret.Name, "test DAG") @@ -132,7 +132,7 @@ func Test_LoadYAML(t *testing.T) { require.Equal(t, step.Command, "true") }) t.Run("InvalidYAMLData", func(t *testing.T) { - _, err := LoadYAML([]byte(`invalidyaml`)) + _, err := loadYAML([]byte(`invalidyaml`), buildOpts{}) require.Error(t, err) }) } diff --git a/internal/dag/parser.go b/internal/dag/parser.go index f6ed568ef..9f6142b3e 100644 --- a/internal/dag/parser.go +++ b/internal/dag/parser.go @@ -66,7 +66,6 @@ func parseSchedules(values []string) ([]Schedule, error) { // - "0 18 * * *" // // ``` -// nolint // cognitive complexity func parseScheduleMap( scheduleMap map[any]any, starts, stops, restarts *[]string, ) error { @@ -187,7 +186,6 @@ func parseParamValue( input string, executeCommandSubstitution bool, ) ([]paramPair, error) { paramRegex := regexp.MustCompile( - // nolint `(?:([^\s=]+)=)?("(?:\\"|[^"])*"|` + "`(" + `?:\\"|[^"]*)` + "`" + `|[^"\s]+)`, ) matches := paramRegex.FindAllStringSubmatch(input, -1) @@ -225,9 +223,7 @@ func parseParamValue( ) if cmdErr != nil { - return nil, fmt.Errorf( - "error evaluating '%s': %w", value, cmdErr, - ) + return nil, fmt.Errorf("%w: %s", errInvalidParamValue, cmdErr) } } } @@ -253,9 +249,12 @@ func parseKeyValue(m map[any]any, pairs *[]pair) error { return errInvalidKeyType } - val, ok := v.(string) - if !ok { - return errInvalidEnvValue + var val string + switch v := v.(type) { + case string: + val = v + default: + val = fmt.Sprintf("%v", v) } *pairs = append(*pairs, pair{key: key, val: val}) @@ -348,13 +347,27 @@ func parseKey(value any) (string, error) { // parseTags builds a list of tags from the value. // It converts the tags to lowercase and trims the whitespace. -func parseTags(value string) []string { - ret := []string{} +func parseTags(value any) []string { + var ret []string - for _, v := range strings.Split(value, ",") { - tag := strings.ToLower(strings.TrimSpace(v)) - if tag != "" { - ret = append(ret, tag) + switch v := value.(type) { + case string: + for _, v := range strings.Split(v, ",") { + tag := strings.ToLower(strings.TrimSpace(v)) + if tag != "" { + ret = append(ret, tag) + } + } + case []any: + for _, v := range v { + switch v := v.(type) { + case string: + ret = append(ret, strings.ToLower(strings.TrimSpace(v))) + default: + ret = append(ret, strings.ToLower( + strings.TrimSpace(fmt.Sprintf("%v", v))), + ) + } } } diff --git a/internal/dag/scheduler/node.go b/internal/dag/scheduler/node.go index 38ae78953..ef2af54e6 100644 --- a/internal/dag/scheduler/node.go +++ b/internal/dag/scheduler/node.go @@ -244,7 +244,6 @@ func (n *Node) setErr(err error) { n.data.Status = NodeStatusError } -// nolint func (n *Node) signal(sig os.Signal, allowOverride bool) { n.mu.Lock() defer n.mu.Unlock() diff --git a/internal/dag/scheduler/scheduler.go b/internal/dag/scheduler/scheduler.go index 3a0f040e1..9d0588bc7 100644 --- a/internal/dag/scheduler/scheduler.go +++ b/internal/dag/scheduler/scheduler.go @@ -106,7 +106,6 @@ type Config struct { } // Schedule runs the graph of steps. -// nolint // cognitive complexity func (sc *Scheduler) Schedule(ctx context.Context, g *ExecutionGraph, done chan *Node) error { if err := sc.setup(); err != nil { return err @@ -233,6 +232,8 @@ func (sc *Scheduler) Schedule(ctx context.Context, g *ExecutionGraph, done chan handlers = append(handlers, dag.HandlerOnFailure) case StatusCancel: handlers = append(handlers, dag.HandlerOnCancel) + case StatusNone: + case StatusRunning: } handlers = append(handlers, dag.HandlerOnExit) for _, h := range handlers { @@ -285,7 +286,6 @@ func (sc *Scheduler) execNode(ctx context.Context, n *Node) error { // for a node with repeat policy, it does not stop the node and // wait to finish current run. func (sc *Scheduler) Signal( - // nolint g *ExecutionGraph, sig os.Signal, done chan bool, allowOverride bool, ) { if !sc.isCanceled() { diff --git a/internal/dag/testdata/http_executor.yaml b/internal/dag/testdata/http_executor.yaml new file mode 100644 index 000000000..0fc1aec62 --- /dev/null +++ b/internal/dag/testdata/http_executor.yaml @@ -0,0 +1,4 @@ +steps: + - command: GET http://example.com + name: step 1 + executor: http diff --git a/internal/dag/testdata/http_executor_with_config.yaml b/internal/dag/testdata/http_executor_with_config.yaml new file mode 100644 index 000000000..08ecb93cf --- /dev/null +++ b/internal/dag/testdata/http_executor_with_config.yaml @@ -0,0 +1,7 @@ +steps: + - command: http://example.com + name: step 1 + executor: + type: http + config: + key: value diff --git a/internal/dag/testdata/invalid_env.yaml b/internal/dag/testdata/invalid_env.yaml new file mode 100644 index 000000000..01d8182dc --- /dev/null +++ b/internal/dag/testdata/invalid_env.yaml @@ -0,0 +1,2 @@ +env: + - VAR: "`invalid command`" diff --git a/internal/dag/testdata/invalid_params.yaml b/internal/dag/testdata/invalid_params.yaml new file mode 100644 index 000000000..3f5bbdb20 --- /dev/null +++ b/internal/dag/testdata/invalid_params.yaml @@ -0,0 +1 @@ +params: "`invalid command`" diff --git a/internal/dag/testdata/invalid_schedule.yaml b/internal/dag/testdata/invalid_schedule.yaml new file mode 100644 index 000000000..5dc08851d --- /dev/null +++ b/internal/dag/testdata/invalid_schedule.yaml @@ -0,0 +1,5 @@ +schedule: "1" + +steps: + - command: echo 1 + name: step 1 diff --git a/internal/dag/testdata/no_command.yaml b/internal/dag/testdata/no_command.yaml new file mode 100644 index 000000000..e17d718b4 --- /dev/null +++ b/internal/dag/testdata/no_command.yaml @@ -0,0 +1,2 @@ +steps: + - name: step 1 diff --git a/internal/dag/testdata/no_name.yaml b/internal/dag/testdata/no_name.yaml new file mode 100644 index 000000000..f706a9d71 --- /dev/null +++ b/internal/dag/testdata/no_name.yaml @@ -0,0 +1,2 @@ +steps: + - command: echo 1 diff --git a/internal/dag/testdata/params_with_complex_values.yaml b/internal/dag/testdata/params_with_complex_values.yaml new file mode 100644 index 000000000..6bddfc744 --- /dev/null +++ b/internal/dag/testdata/params_with_complex_values.yaml @@ -0,0 +1,3 @@ +params: first P1=foo P2=${FOO} P3=`/bin/echo BAR` X=bar Y=${P1} Z="A B C" +env: + - FOO: BAR diff --git a/internal/dag/testdata/params_with_quoted_values.yaml b/internal/dag/testdata/params_with_quoted_values.yaml new file mode 100644 index 000000000..1eed8e5b2 --- /dev/null +++ b/internal/dag/testdata/params_with_quoted_values.yaml @@ -0,0 +1 @@ +params: x="a b c" y="d e f" diff --git a/internal/dag/testdata/params_with_substitution.yaml b/internal/dag/testdata/params_with_substitution.yaml new file mode 100644 index 000000000..4a649e036 --- /dev/null +++ b/internal/dag/testdata/params_with_substitution.yaml @@ -0,0 +1 @@ +params: "x $1" diff --git a/internal/dag/testdata/schedule_with_multiple_values.yaml b/internal/dag/testdata/schedule_with_multiple_values.yaml new file mode 100644 index 000000000..51191d4c4 --- /dev/null +++ b/internal/dag/testdata/schedule_with_multiple_values.yaml @@ -0,0 +1,10 @@ +schedule: + start: + - "0 1 * * *" + - "0 18 * * *" + stop: + - "0 2 * * *" + - "0 20 * * *" + restart: + - "0 12 * * *" + - "0 22 * * *" diff --git a/internal/dag/testdata/signal_on_stop.yaml b/internal/dag/testdata/signal_on_stop.yaml new file mode 100644 index 000000000..8658a0072 --- /dev/null +++ b/internal/dag/testdata/signal_on_stop.yaml @@ -0,0 +1,4 @@ +steps: + - command: echo 1 + name: step 1 + signalOnStop: SIGINT diff --git a/internal/dag/testdata/valid_command.yaml b/internal/dag/testdata/valid_command.yaml new file mode 100644 index 000000000..057d25f11 --- /dev/null +++ b/internal/dag/testdata/valid_command.yaml @@ -0,0 +1,3 @@ +steps: + - command: echo 1 + name: step 1 diff --git a/internal/dag/testdata/valid_command_in_array.yaml b/internal/dag/testdata/valid_command_in_array.yaml new file mode 100644 index 000000000..58fd4cd26 --- /dev/null +++ b/internal/dag/testdata/valid_command_in_array.yaml @@ -0,0 +1,3 @@ +steps: + - command: [echo, 1] + name: step 1 diff --git a/internal/dag/testdata/valid_command_in_list.yaml b/internal/dag/testdata/valid_command_in_list.yaml new file mode 100644 index 000000000..f87590459 --- /dev/null +++ b/internal/dag/testdata/valid_command_in_list.yaml @@ -0,0 +1,5 @@ +steps: + - command: + - echo + - 1 + name: step 1 diff --git a/internal/dag/testdata/valid_env.yaml b/internal/dag/testdata/valid_env.yaml new file mode 100644 index 000000000..36c286bcd --- /dev/null +++ b/internal/dag/testdata/valid_env.yaml @@ -0,0 +1,2 @@ +env: + - "FOO": 123 diff --git a/internal/dag/testdata/valid_env_substitution.yaml b/internal/dag/testdata/valid_env_substitution.yaml new file mode 100644 index 000000000..c129e8848 --- /dev/null +++ b/internal/dag/testdata/valid_env_substitution.yaml @@ -0,0 +1,2 @@ +env: + - VAR: "`echo 123`" diff --git a/internal/dag/testdata/valid_env_substitution_and_env.yaml b/internal/dag/testdata/valid_env_substitution_and_env.yaml new file mode 100644 index 000000000..bcd5f44d8 --- /dev/null +++ b/internal/dag/testdata/valid_env_substitution_and_env.yaml @@ -0,0 +1,3 @@ +env: + - BAR: "`echo BAR`" + - FOO: "${BAR}:BAZ:${BAR}:`echo FOO`" diff --git a/internal/dag/testdata/valid_handlers.yaml b/internal/dag/testdata/valid_handlers.yaml new file mode 100644 index 000000000..bff3a1889 --- /dev/null +++ b/internal/dag/testdata/valid_handlers.yaml @@ -0,0 +1,9 @@ +handlerOn: + success: + command: "echo success" + failure: + command: "echo failure" + cancel: + command: "echo cancel" + exit: + command: "echo exit" diff --git a/internal/dag/testdata/valid_mail_config.yaml b/internal/dag/testdata/valid_mail_config.yaml new file mode 100644 index 000000000..6baa8fe6c --- /dev/null +++ b/internal/dag/testdata/valid_mail_config.yaml @@ -0,0 +1,20 @@ +# SMTP server settings +smtp: + host: "smtp.example.com" + port: "587" + username: user@example.com + password: password + +# Error mail configuration +errorMail: + from: "error@example.com" + to: "admin@example.com" + prefix: "[ERROR]" + attachLogs: true + +# Info mail configuration +infoMail: + from: "info@example.com" + to: "user@example.com" + prefix: "[INFO]" + attachLogs: false diff --git a/internal/dag/testdata/valid_miscs.yaml b/internal/dag/testdata/valid_miscs.yaml new file mode 100644 index 000000000..1656e7aa4 --- /dev/null +++ b/internal/dag/testdata/valid_miscs.yaml @@ -0,0 +1,6 @@ +histRetentionDays: 7 +maxActiveRuns: 3 +maxCleanUpTimeSec: 300 +preconditions: + - condition: "test -f file.txt" + expected: "true" diff --git a/internal/dag/testdata/valid_schedule.yaml b/internal/dag/testdata/valid_schedule.yaml new file mode 100644 index 000000000..5d3c748ef --- /dev/null +++ b/internal/dag/testdata/valid_schedule.yaml @@ -0,0 +1,4 @@ +schedule: + start: "0 1 * * *" + stop: "0 2 * * *" + restart: "0 12 * * *" diff --git a/internal/dag/testdata/valid_subworkflow.yaml b/internal/dag/testdata/valid_subworkflow.yaml new file mode 100644 index 000000000..c2c75605a --- /dev/null +++ b/internal/dag/testdata/valid_subworkflow.yaml @@ -0,0 +1,4 @@ +steps: + - name: sub_workflow_step + run: sub_dag + params: "param1=value1 param2=value2" diff --git a/internal/dag/testdata/valid_tags.yaml b/internal/dag/testdata/valid_tags.yaml new file mode 100644 index 000000000..bfa0d53ab --- /dev/null +++ b/internal/dag/testdata/valid_tags.yaml @@ -0,0 +1,4 @@ +tags: daily,monthly +steps: + - command: echo 1 + name: step 1 diff --git a/internal/dag/testdata/valid_tags_list.yaml b/internal/dag/testdata/valid_tags_list.yaml new file mode 100644 index 000000000..b2ce36afb --- /dev/null +++ b/internal/dag/testdata/valid_tags_list.yaml @@ -0,0 +1,6 @@ +tags: + - daily + - monthly +steps: + - command: echo 1 + name: step 1 diff --git a/internal/frontend/dag/handler.go b/internal/frontend/dag/handler.go index 378956979..05ff0910a 100644 --- a/internal/frontend/dag/handler.go +++ b/internal/frontend/dag/handler.go @@ -572,7 +572,6 @@ func addNodeStatus( data[nodeName][logIdx] = status } -// nolint // cognitive complexity func (h *Handler) postAction( params dags.PostDagActionParams, ) (*models.PostDagActionResponse, *codedError) { diff --git a/internal/frontend/middleware/basic_auth.go b/internal/frontend/middleware/basic_auth.go index 1658bfda9..116c9b304 100644 --- a/internal/frontend/middleware/basic_auth.go +++ b/internal/frontend/middleware/basic_auth.go @@ -26,7 +26,6 @@ const ( authHeaderKey = "Authorization" ) -// nolint:revive func BasicAuth(realm string, creds map[string]string) func( next http.Handler, ) http.Handler { diff --git a/internal/frontend/server/server.go b/internal/frontend/server/server.go index 9ea22f06f..0f8d5772a 100644 --- a/internal/frontend/server/server.go +++ b/internal/frontend/server/server.go @@ -31,7 +31,6 @@ import ( flags "github.com/jessevdk/go-flags" "github.com/daguflow/dagu/internal/frontend/gen/restapi/operations" - // nolint pkgmiddleware "github.com/daguflow/dagu/internal/frontend/middleware" "github.com/go-chi/chi/v5" diff --git a/internal/persistence/jsondb/store.go b/internal/persistence/jsondb/store.go index 860ba8be8..b050322ab 100644 --- a/internal/persistence/jsondb/store.go +++ b/internal/persistence/jsondb/store.go @@ -298,7 +298,7 @@ func (s *Store) Rename(oldID, newID string) error { } func (s *Store) getDirectory(name string, prefix string) string { - // nolint + // nolint: gosec h := md5.New() _, _ = h.Write([]byte(name)) v := hex.EncodeToString(h.Sum(nil)) @@ -321,7 +321,6 @@ func (s *Store) newFile( ), nil } -// nolint func (store *Store) latestToday( dagFile string, day time.Time, diff --git a/internal/util/utils.go b/internal/util/utils.go index 8a9c2b960..ad152240e 100644 --- a/internal/util/utils.go +++ b/internal/util/utils.go @@ -91,25 +91,20 @@ const splitCmdN = 2 // SplitCommandWithParse splits command string to program and arguments. func SplitCommandWithParse(cmd string) (cmdx string, args []string) { splits := strings.SplitN(cmd, " ", splitCmdN) - // nolint:revive if len(splits) == 1 { - // nolint:revive return splits[0], []string{} } - // nolint:revive cmdx = splits[0] parser := shellwords.NewParser() parser.ParseBacktick = true parser.ParseEnv = false - // nolint:revive args, err := parser.Parse(escapeReplacer.Replace(splits[1])) if err != nil { log.Printf("failed to parse arguments: %s", err) // if parse shell world error use all string as argument - // nolint:revive return cmdx, []string{splits[1]} } @@ -124,13 +119,10 @@ func SplitCommandWithParse(cmd string) (cmdx string, args []string) { // SplitCommand splits command string to program and arguments. func SplitCommand(cmd string) (cmdx string, args []string) { splits := strings.SplitN(cmd, " ", splitCmdN) - // nolint:revive if len(splits) == 1 { - // nolint:revive return splits[0], []string{} } - // nolint:revive return splits[0], strings.Fields(splits[1]) }