From 3691845742ac6b52885f0c9c0015e39abb9acfff Mon Sep 17 00:00:00 2001 From: Mohamed Abdeen Date: Thu, 14 Sep 2023 17:52:33 +0300 Subject: [PATCH 1/6] Adding max-runs flag --- CHANGELOG.md | 9 +++++++++ cmd/task/task.go | 7 +++++++ docs/docs/changelog.md | 9 +++++++++ task.go | 13 ++++++++++--- task_test.go | 35 +++++++++++++++++++++++++++++++++++ testdata/for/Taskfile.yml | 10 ++++++++++ 6 files changed, 80 insertions(+), 3 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index b01bf2121a..de5a742a92 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,14 @@ # Changelog +## v3.31.0 - 2023-09-14 + +- Added a `--max-runs` flag, which sets the maximum number of times a task + should run before being considered an infinite loop or a cyclic dep, + and therefore killed (#1321 by @bogwro). +- Added a test task in `testdata/for` and test case to test `--max-runs` behavior. +- Fixed a bug where `TaskCalledTooManyTimes` error always shows 0 as number + of exceeded max runs. + ## v3.30.0 - 2023-09-13 - Prep work for Remote Taskfiles (#1316 by @pd93). diff --git a/cmd/task/task.go b/cmd/task/task.go index b424bfacd7..81e1ebc896 100644 --- a/cmd/task/task.go +++ b/cmd/task/task.go @@ -74,6 +74,7 @@ var flags struct { experiments bool download bool offline bool + maxRuns int } func main() { @@ -135,6 +136,7 @@ func run() error { pflag.DurationVarP(&flags.interval, "interval", "I", 0, "Interval to watch for changes.") pflag.BoolVarP(&flags.global, "global", "g", false, "Runs global Taskfile, from $HOME/{T,t}askfile.{yml,yaml}.") pflag.BoolVar(&flags.experiments, "experiments", false, "Lists all the available experiments and whether or not they are enabled.") + pflag.IntVar(&flags.maxRuns, "max-runs", task.MaximumTaskCall, "Set the maximum number of runs per task before being considered infinte or cyclic and therefore terminated.") // Gentle force experiment will override the force flag and add a new force-all flag if experiments.GentleForce { @@ -227,6 +229,10 @@ func run() error { taskSorter = &sort.AlphaNumeric{} } + if flags.maxRuns < 1 { + return errors.New("task: You can't set --max-runs to less than 1") + } + e := task.Executor{ Force: flags.force, ForceAll: flags.forceAll, @@ -245,6 +251,7 @@ func run() error { Color: flags.color, Concurrency: flags.concurrency, Interval: flags.interval, + MaxRuns: flags.maxRuns, Stdin: os.Stdin, Stdout: os.Stdout, diff --git a/docs/docs/changelog.md b/docs/docs/changelog.md index c5b9d7b555..0f60e9b63e 100644 --- a/docs/docs/changelog.md +++ b/docs/docs/changelog.md @@ -5,6 +5,15 @@ sidebar_position: 14 # Changelog +## v3.31.0 - 2023-09-14 + +- Added a `--max-runs` flag, which sets the maximum number of times a task + should run before being considered an infinite loop or a cyclic dep, + and therefore killed ([#1321](https://github.com/go-task/task/issues/1321) by [@bogwro](https://github.com/bogwro)). +- Added a test task in `testdata/for` and a test case in `task_test` to test `--max-runs` behavior. +- Fixed a bug where `TaskCalledTooManyTimes` error always shows 0 as number + of exceeded max runs. + ## v3.30.0 - 2023-09-13 - Prep work for Remote Taskfiles ([#1316](https://github.com/go-task/task/issues/1316) by [@pd93](https://github.com/pd93)). diff --git a/task.go b/task.go index b02eceb303..7a8be20df4 100644 --- a/task.go +++ b/task.go @@ -33,7 +33,8 @@ import ( const ( // MaximumTaskCall is the max number of times a task can be called. - // This exists to prevent infinite loops on cyclic dependencies + // This exists to prevent infinite loops on cyclic dependencies. + // Used as the default value if max-runs flag is not set. MaximumTaskCall = 100 ) @@ -64,6 +65,7 @@ type Executor struct { Color bool Concurrency int Interval time.Duration + MaxRuns int AssumesTerm bool Stdin io.Reader @@ -127,6 +129,11 @@ func (e *Executor) Run(ctx context.Context, calls ...taskfile.Call) error { return e.watchTasks(calls...) } + // if executor wasn't created through CLI, (i.e. for testing) + if e.MaxRuns == 0 { + e.MaxRuns = MaximumTaskCall + } + g, ctx := errgroup.WithContext(ctx) for _, c := range calls { c := c @@ -147,8 +154,8 @@ func (e *Executor) RunTask(ctx context.Context, call taskfile.Call) error { if err != nil { return err } - if !e.Watch && atomic.AddInt32(e.taskCallCount[t.Task], 1) >= MaximumTaskCall { - return &errors.TaskCalledTooManyTimesError{TaskName: t.Task} + if !e.Watch && atomic.AddInt32(e.taskCallCount[t.Task], 1) > int32(e.MaxRuns) { + return &errors.TaskCalledTooManyTimesError{TaskName: t.Task, MaximumTaskCall: e.MaxRuns} } release := e.acquireConcurrencyLimit() diff --git a/task_test.go b/task_test.go index 8370afce59..107d0e2e0e 100644 --- a/task_test.go +++ b/task_test.go @@ -218,6 +218,12 @@ func TestVarsInvalidTmpl(t *testing.T) { assert.EqualError(t, e.Run(context.Background(), taskfile.Call{Task: target}), expectError, "e.Run(target)") } +func TestMaxRuns(t *testing.T) { + const ( + dir = "testdata/for" + ) +} + func TestConcurrency(t *testing.T) { const ( dir = "testdata/concurrency" @@ -2289,3 +2295,32 @@ func TestFor(t *testing.T) { }) } } + +func TestTooManyRuns(t *testing.T) { + tests := []struct { + name string + expectedError string + }{ + { + name: "loop-too-many", + expectedError: `task: Failed to run task "loop-too-many": task: Maximum task call exceeded (4) for task "looped-task": probably an cyclic dep or infinite loop`, + }, + } + + for _, test := range tests { + t.Run(test.name, func(t *testing.T) { + var buff bytes.Buffer + e := task.Executor{ + Dir: "testdata/for", + Stdout: &buff, + Stderr: &buff, + Silent: true, + Force: true, + MaxRuns: 4, // task contains 5 loops + } + + require.NoError(t, e.Setup()) + assert.EqualError(t, e.Run(context.Background(), taskfile.Call{Task: test.name, Direct: true}), test.expectedError) + }) + } +} diff --git a/testdata/for/Taskfile.yml b/testdata/for/Taskfile.yml index 576f684dfa..9e2bc8a4da 100644 --- a/testdata/for/Taskfile.yml +++ b/testdata/for/Taskfile.yml @@ -76,6 +76,16 @@ tasks: var: FOO task: task-{{.ITEM}} + loop-too-many: + vars: + FOO: foo.txt foo.txt foo.txt foo.txt foo.txt + cmds: + - for: + var: FOO + task: looped-task + vars: + FILE: "{{.ITEM}}" + looped-task: internal: true cmd: cat "{{.FILE}}" From b5db153ab524f68b7cd62ac02930f4d0e4bb55b6 Mon Sep 17 00:00:00 2001 From: Mohamed Abdeen Date: Thu, 14 Sep 2023 18:16:46 +0300 Subject: [PATCH 2/6] Changed changelogs.md --- CHANGELOG.md | 5 ++++- docs/docs/changelog.md | 1 + 2 files changed, 5 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index de5a742a92..debcbb2871 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -5,9 +5,12 @@ - Added a `--max-runs` flag, which sets the maximum number of times a task should run before being considered an infinite loop or a cyclic dep, and therefore killed (#1321 by @bogwro). -- Added a test task in `testdata/for` and test case to test `--max-runs` behavior. +- Added a test task in `testdata/for` and test case to test `--max-runs` + behavior. - Fixed a bug where `TaskCalledTooManyTimes` error always shows 0 as number of exceeded max runs. +- Fixed a bug where a task will be one run short from the number specified + by`MaximumTaskCall`. ## v3.30.0 - 2023-09-13 diff --git a/docs/docs/changelog.md b/docs/docs/changelog.md index 0f60e9b63e..3c152bb9e0 100644 --- a/docs/docs/changelog.md +++ b/docs/docs/changelog.md @@ -13,6 +13,7 @@ sidebar_position: 14 - Added a test task in `testdata/for` and a test case in `task_test` to test `--max-runs` behavior. - Fixed a bug where `TaskCalledTooManyTimes` error always shows 0 as number of exceeded max runs. +- Fixed a bug where a task will be one run short from the number specified by`MaximumTaskCall`. ## v3.30.0 - 2023-09-13 From 6bddb6ae88833a42c2636e921538b2a392a0f6bd Mon Sep 17 00:00:00 2001 From: Mohamed Abdeen Date: Thu, 14 Sep 2023 18:26:06 +0300 Subject: [PATCH 3/6] Cleaned tests --- task_test.go | 6 ------ 1 file changed, 6 deletions(-) diff --git a/task_test.go b/task_test.go index 107d0e2e0e..61bf660a3a 100644 --- a/task_test.go +++ b/task_test.go @@ -218,12 +218,6 @@ func TestVarsInvalidTmpl(t *testing.T) { assert.EqualError(t, e.Run(context.Background(), taskfile.Call{Task: target}), expectError, "e.Run(target)") } -func TestMaxRuns(t *testing.T) { - const ( - dir = "testdata/for" - ) -} - func TestConcurrency(t *testing.T) { const ( dir = "testdata/concurrency" From 52589b7e1bedf8be22150ae2da362954b1612a38 Mon Sep 17 00:00:00 2001 From: Mohamed Abdeen Date: Thu, 14 Sep 2023 19:12:27 +0300 Subject: [PATCH 4/6] version 3.31.0 --- package-lock.json | 2 +- package.json | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/package-lock.json b/package-lock.json index 97297d036c..74a4b87659 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,6 +1,6 @@ { "name": "@go-task/cli", - "version": "3.30.0", + "version": "3.31.0", "lockfileVersion": 2, "requires": true, "packages": { diff --git a/package.json b/package.json index 21188d57e3..be3e4fd2f0 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "@go-task/cli", - "version": "3.30.0", + "version": "3.31.0", "description": "A task runner / simpler Make alternative written in Go", "scripts": { "postinstall": "go-npm install", From 6d48d502e6a7b366e52d536230af083b2ed6f069 Mon Sep 17 00:00:00 2001 From: Mohamed Abdeen Date: Thu, 14 Sep 2023 22:52:57 +0300 Subject: [PATCH 5/6] Updating documention from review --- CHANGELOG.md | 2 +- cmd/task/task.go | 2 +- docs/docs/api_reference.md | 1 + docs/docs/changelog.md | 10 ---------- package-lock.json | 2 +- package.json | 2 +- 6 files changed, 5 insertions(+), 14 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index debcbb2871..799fbbe75e 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,6 +1,6 @@ # Changelog -## v3.31.0 - 2023-09-14 +## Unreleased - Added a `--max-runs` flag, which sets the maximum number of times a task should run before being considered an infinite loop or a cyclic dep, diff --git a/cmd/task/task.go b/cmd/task/task.go index 81e1ebc896..758383a425 100644 --- a/cmd/task/task.go +++ b/cmd/task/task.go @@ -136,7 +136,7 @@ func run() error { pflag.DurationVarP(&flags.interval, "interval", "I", 0, "Interval to watch for changes.") pflag.BoolVarP(&flags.global, "global", "g", false, "Runs global Taskfile, from $HOME/{T,t}askfile.{yml,yaml}.") pflag.BoolVar(&flags.experiments, "experiments", false, "Lists all the available experiments and whether or not they are enabled.") - pflag.IntVar(&flags.maxRuns, "max-runs", task.MaximumTaskCall, "Set the maximum number of runs per task before being considered infinte or cyclic and therefore terminated.") + pflag.IntVar(&flags.maxRuns, "max-runs", task.MaximumTaskCall, "Maximum number of runs per task before being considered infinte loop or cyclic dep and therefore terminated.") // Gentle force experiment will override the force flag and add a new force-all flag if experiments.GentleForce { diff --git a/docs/docs/api_reference.md b/docs/docs/api_reference.md index e36d21b9eb..ae531d6be6 100644 --- a/docs/docs/api_reference.md +++ b/docs/docs/api_reference.md @@ -51,6 +51,7 @@ If `--` is given, all remaining arguments will be assigned to a special | `-v` | `--verbose` | `bool` | `false` | Enables verbose mode. | | | `--version` | `bool` | `false` | Show Task version. | | `-w` | `--watch` | `bool` | `false` | Enables watch of the given task. | +| | `--max-runs` | `int` | `100` | Maximum number of runs per task before being considered infinte loop or cyclic dep and therefore terminated. | ## Exit Codes diff --git a/docs/docs/changelog.md b/docs/docs/changelog.md index 3c152bb9e0..c5b9d7b555 100644 --- a/docs/docs/changelog.md +++ b/docs/docs/changelog.md @@ -5,16 +5,6 @@ sidebar_position: 14 # Changelog -## v3.31.0 - 2023-09-14 - -- Added a `--max-runs` flag, which sets the maximum number of times a task - should run before being considered an infinite loop or a cyclic dep, - and therefore killed ([#1321](https://github.com/go-task/task/issues/1321) by [@bogwro](https://github.com/bogwro)). -- Added a test task in `testdata/for` and a test case in `task_test` to test `--max-runs` behavior. -- Fixed a bug where `TaskCalledTooManyTimes` error always shows 0 as number - of exceeded max runs. -- Fixed a bug where a task will be one run short from the number specified by`MaximumTaskCall`. - ## v3.30.0 - 2023-09-13 - Prep work for Remote Taskfiles ([#1316](https://github.com/go-task/task/issues/1316) by [@pd93](https://github.com/pd93)). diff --git a/package-lock.json b/package-lock.json index 74a4b87659..97297d036c 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,6 +1,6 @@ { "name": "@go-task/cli", - "version": "3.31.0", + "version": "3.30.0", "lockfileVersion": 2, "requires": true, "packages": { diff --git a/package.json b/package.json index be3e4fd2f0..21188d57e3 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "@go-task/cli", - "version": "3.31.0", + "version": "3.30.0", "description": "A task runner / simpler Make alternative written in Go", "scripts": { "postinstall": "go-npm install", From 58f77599bf171105e2ffd28acd1ed4ec7a1187f2 Mon Sep 17 00:00:00 2001 From: Mohamed Abdeen Date: Fri, 15 Sep 2023 00:21:23 +0300 Subject: [PATCH 6/6] Update changelog --- CHANGELOG.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 799fbbe75e..8073058012 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -4,7 +4,7 @@ - Added a `--max-runs` flag, which sets the maximum number of times a task should run before being considered an infinite loop or a cyclic dep, - and therefore killed (#1321 by @bogwro). + and therefore killed. - Added a test task in `testdata/for` and test case to test `--max-runs` behavior. - Fixed a bug where `TaskCalledTooManyTimes` error always shows 0 as number