fix(includes): propagate silent mode from included Taskfiles to tasks (#2640)

This commit is contained in:
Timothy Rule
2026-01-25 16:33:52 +01:00
committed by GitHub
parent 065236f076
commit f6720760b4
10 changed files with 89 additions and 8 deletions

View File

@@ -5,6 +5,9 @@
- Fixed `ROOT_TASKFILE` variable pointing to directory instead of the actual
Taskfile path when no explicit `-t` flag is provided (#2635, #1706 by
@trulede).
- Included Taskfiles with `silent: true` now properly propagate silence to their
tasks, while still allowing individual tasks to override with `silent: false`
(#2640, #1319 by @trulede).
## v3.47.0 - 2026-01-24

View File

@@ -1060,6 +1060,18 @@ func TestIncludeChecksum(t *testing.T) {
)
}
func TestIncludeSilent(t *testing.T) {
t.Parallel()
NewExecutorTest(t,
WithName("include-taskfile-silent"),
WithExecutorOptions(
task.WithDir("testdata/includes_silent"),
),
WithTask("default"),
)
}
func TestFailfast(t *testing.T) {
t.Parallel()

View File

@@ -10,6 +10,15 @@ type Copier[T any] interface {
DeepCopy() T
}
func Scalar[T any](orig *T) *T {
if orig == nil {
return nil
} else {
v := *orig
return &v
}
}
func Slice[T any](orig []T) []T {
if orig == nil {
return nil

View File

@@ -228,7 +228,7 @@ func (e *Executor) RunTask(ctx context.Context, call *Call) error {
}
if upToDate && preCondMet {
if e.Verbose || (!call.Silent && !t.Silent && !e.Taskfile.Silent && !e.Silent) {
if e.Verbose || (!call.Silent && !t.IsSilent() && !e.Taskfile.Silent && !e.Silent) {
name := t.Name()
if e.OutputStyle.Name == "prefixed" {
name = t.Prefix
@@ -383,7 +383,7 @@ func (e *Executor) runCommand(ctx context.Context, t *ast.Task, call *Call, i in
return nil
}
if e.Verbose || (!call.Silent && !cmd.Silent && !t.Silent && !e.Taskfile.Silent && !e.Silent) {
if e.Verbose || (!call.Silent && !cmd.Silent && !t.IsSilent() && !e.Taskfile.Silent && !e.Silent) {
e.Logger.Errf(logger.Green, "task: [%s] %s\n", t.Name(), cmd.Cmd)
}

View File

@@ -32,7 +32,7 @@ type Task struct {
Vars *Vars
Env *Vars
Dotenv []string
Silent bool
Silent *bool
Interactive bool
Internal bool
Method string
@@ -69,6 +69,12 @@ func (t *Task) LocalName() string {
return name
}
// IsSilent returns true if the task has silent mode explicitly enabled.
// Returns false if Silent is nil (not set) or explicitly set to false.
func (t *Task) IsSilent() bool {
return t.Silent != nil && *t.Silent
}
// WildcardMatch will check if the given string matches the name of the Task and returns any wildcard values.
func (t *Task) WildcardMatch(name string) (bool, []string) {
names := append([]string{t.Task}, t.Aliases...)
@@ -138,7 +144,7 @@ func (t *Task) UnmarshalYAML(node *yaml.Node) error {
Vars *Vars
Env *Vars
Dotenv []string
Silent bool
Silent *bool `yaml:"silent,omitempty"`
Interactive bool
Internal bool
Method string
@@ -178,7 +184,7 @@ func (t *Task) UnmarshalYAML(node *yaml.Node) error {
t.Vars = task.Vars
t.Env = task.Env
t.Dotenv = task.Dotenv
t.Silent = task.Silent
t.Silent = deepcopy.Scalar(task.Silent)
t.Interactive = task.Interactive
t.Internal = task.Internal
t.Method = task.Method
@@ -221,7 +227,7 @@ func (t *Task) DeepCopy() *Task {
Vars: t.Vars.DeepCopy(),
Env: t.Env.DeepCopy(),
Dotenv: deepcopy.Slice(t.Dotenv),
Silent: t.Silent,
Silent: deepcopy.Scalar(t.Silent),
Interactive: t.Interactive,
Internal: t.Internal,
Method: t.Method,

View File

@@ -59,6 +59,14 @@ func (t1 *Taskfile) Merge(t2 *Taskfile, include *Include) error {
if t1.Tasks == nil {
t1.Tasks = NewTasks()
}
if t2.Silent {
for _, t := range t2.Tasks.All(nil) {
if t.Silent == nil {
v := true
t.Silent = &v
}
}
}
t1.Vars.Merge(t2.Vars, include)
t1.Env.Merge(t2.Env, include)
return t1.Tasks.Merge(t2.Tasks, include, t1.Vars)

View File

@@ -0,0 +1,22 @@
version: '3'
silent: true
tasks:
hello:
cmds:
- echo "Hello from include"
hello-silent:
silent: true
cmds:
- echo "Hello from include silent task"
hello-silent-not-set:
cmds:
- echo "Hello from include silent not set task"
hello-silent-set-false:
silent: false
cmds:
- echo "Hello from include silent false task"

13
testdata/includes_silent/Taskfile.yml vendored Normal file
View File

@@ -0,0 +1,13 @@
version: '3'
includes:
inc: Taskfile-inc.yml
tasks:
default:
cmds:
- echo "Hello from root Taskfile"
- task: inc:hello
- task: inc:hello-silent
- task: inc:hello-silent-not-set
- task: inc:hello-silent-set-false

View File

@@ -0,0 +1,7 @@
task: [default] echo "Hello from root Taskfile"
Hello from root Taskfile
Hello from include
Hello from include silent task
Hello from include silent not set task
task: [inc:hello-silent-set-false] echo "Hello from include silent false task"
Hello from include silent false task

View File

@@ -10,6 +10,7 @@ import (
"github.com/joho/godotenv"
"github.com/go-task/task/v3/errors"
"github.com/go-task/task/v3/internal/deepcopy"
"github.com/go-task/task/v3/internal/env"
"github.com/go-task/task/v3/internal/execext"
"github.com/go-task/task/v3/internal/filepathext"
@@ -57,7 +58,7 @@ func (e *Executor) CompiledTaskForTaskList(call *Call) (*ast.Task, error) {
Vars: vars,
Env: nil,
Dotenv: origTask.Dotenv,
Silent: origTask.Silent,
Silent: deepcopy.Scalar(origTask.Silent),
Interactive: origTask.Interactive,
Internal: origTask.Internal,
Method: origTask.Method,
@@ -113,7 +114,7 @@ func (e *Executor) compiledTask(call *Call, evaluateShVars bool) (*ast.Task, err
Vars: vars,
Env: nil,
Dotenv: templater.Replace(origTask.Dotenv, cache),
Silent: origTask.Silent,
Silent: deepcopy.Scalar(origTask.Silent),
Interactive: origTask.Interactive,
Internal: origTask.Internal,
Method: templater.Replace(origTask.Method, cache),