feat: add command-level timeout support

Add a per-command `timeout` option that terminates a command once it
exceeds the given duration, preventing commands from hanging indefinitely
in a pipeline. Uses Go duration syntax (e.g. 30s, 5m, 1h30m) and applies
to both shell commands and task calls.

Closes #1569
This commit is contained in:
Valentin Maerten
2026-06-30 10:29:20 +02:00
parent a61f8ade36
commit 4d5f7337c1
6 changed files with 147 additions and 0 deletions

View File

@@ -1,6 +1,8 @@
package ast
import (
"time"
"go.yaml.in/yaml/v3"
"github.com/go-task/task/v3/errors"
@@ -21,6 +23,7 @@ type Cmd struct {
IgnoreError bool
Defer bool
Platforms []*Platform
Timeout time.Duration
}
func (c *Cmd) DeepCopy() *Cmd {
@@ -40,6 +43,7 @@ func (c *Cmd) DeepCopy() *Cmd {
IgnoreError: c.IgnoreError,
Defer: c.Defer,
Platforms: deepcopy.Slice(c.Platforms),
Timeout: c.Timeout,
}
}
@@ -67,10 +71,20 @@ func (c *Cmd) UnmarshalYAML(node *yaml.Node) error {
IgnoreError bool `yaml:"ignore_error"`
Defer *Defer
Platforms []*Platform
Timeout string
}
if err := node.Decode(&cmdStruct); err != nil {
return errors.NewTaskfileDecodeError(err, node)
}
if cmdStruct.Timeout != "" {
timeout, err := time.ParseDuration(cmdStruct.Timeout)
if err != nil {
return errors.NewTaskfileDecodeError(err, node).WithMessage("invalid timeout format")
}
c.Timeout = timeout
}
if cmdStruct.Defer != nil {
// A deferred command