diff --git a/task_test.go b/task_test.go index 52a147f02a..dc525e86e1 100644 --- a/task_test.go +++ b/task_test.go @@ -2,6 +2,7 @@ package task_test import ( "bytes" + "cmp" "fmt" "io" "io/fs" @@ -1639,20 +1640,30 @@ func TestDotenvShouldIncludeAllEnvFiles(t *testing.T) { }) } -func TestDotenvShouldErrorWhenIncludingDependantDotenvs(t *testing.T) { +func TestDotenvIncludeDotenvs(t *testing.T) { t.Parallel() - var buff bytes.Buffer - e := task.NewExecutor( - task.WithDir("testdata/dotenv/error_included_envs"), - task.WithSummary(true), - task.WithStdout(&buff), - task.WithStderr(&buff), - ) - - err := e.Setup() - require.Error(t, err) - assert.Contains(t, err.Error(), "move the dotenv") + tests := []struct { + name string + dir string + call string + }{ + { + name: "included dotenv has priority", + dir: "testdata/dotenv/included_envs", + }, + } + for _, test := range tests { + NewExecutorTest(t, + WithName(test.name), + WithExecutorOptions( + task.WithDir(test.dir), + task.WithSilent(true), + task.WithForce(true), + ), + WithTask(cmp.Or(test.call, "default")), + ) + } } func TestDotenvShouldAllowMissingEnv(t *testing.T) { diff --git a/taskfile/ast/taskfile.go b/taskfile/ast/taskfile.go index 8085e41b63..ede5e682dd 100644 --- a/taskfile/ast/taskfile.go +++ b/taskfile/ast/taskfile.go @@ -2,12 +2,15 @@ package ast import ( "fmt" + "path/filepath" + "slices" "time" "github.com/Masterminds/semver/v3" "go.yaml.in/yaml/v4" "github.com/go-task/task/v3/errors" + "github.com/go-task/task/v3/internal/filepathext" ) // NamespaceSeparator contains the character that separates namespaces @@ -15,9 +18,6 @@ const NamespaceSeparator = ":" var V3 = semver.MustParse("3") -// ErrIncludedTaskfilesCantHaveDotenvs is returned when a included Taskfile contains dotenvs -var ErrIncludedTaskfilesCantHaveDotenvs = errors.New("task: Included Taskfiles can't have dotenv declarations. Please, move the dotenv declaration to the main Taskfile") - // Taskfile is the abstract syntax tree for a Taskfile type Taskfile struct { Location string @@ -41,8 +41,11 @@ func (t1 *Taskfile) Merge(t2 *Taskfile, include *Include) error { if !t1.Version.Equal(t2.Version) { return fmt.Errorf(`task: Taskfiles versions should match. First is "%s" but second is "%s"`, t1.Version, t2.Version) } - if len(t2.Dotenv) > 0 { - return ErrIncludedTaskfilesCantHaveDotenvs + for _, dotenvfile := range t2.Dotenv { + fp := filepathext.SmartJoin(filepath.Dir(t2.Location), dotenvfile) + if !slices.Contains(t1.Dotenv, fp) { + t1.Dotenv = append([]string{fp}, t1.Dotenv...) + } } if t2.Output.IsSet() { t1.Output = t2.Output diff --git a/testdata/dotenv/included_envs/Taskfile.yaml b/testdata/dotenv/included_envs/Taskfile.yaml new file mode 100644 index 0000000000..a13d69979b --- /dev/null +++ b/testdata/dotenv/included_envs/Taskfile.yaml @@ -0,0 +1,15 @@ +version: '3' + +silent: true + +dotenv: + - dotenv.txt + +includes: + foo: ./inc/Taskfile.yaml + +tasks: + default: + cmds: + - echo "var FOO = {{.FOO}}" + - echo "env FOO = $FOO" \ No newline at end of file diff --git a/testdata/dotenv/included_envs/inc/Taskfile.yaml b/testdata/dotenv/included_envs/inc/Taskfile.yaml new file mode 100644 index 0000000000..1eeb62d319 --- /dev/null +++ b/testdata/dotenv/included_envs/inc/Taskfile.yaml @@ -0,0 +1,10 @@ +version: '3' + +dotenv: + - dotenv.txt + +tasks: + default: + cmds: + - echo "FOO = {{.FOO}}" + - echo "FOO = $FOO" \ No newline at end of file diff --git a/testdata/dotenv/included_envs/testdata/TestDotenvIncludeDotenvs-included_dotenv_has_priority.golden b/testdata/dotenv/included_envs/testdata/TestDotenvIncludeDotenvs-included_dotenv_has_priority.golden new file mode 100644 index 0000000000..b3efe10677 --- /dev/null +++ b/testdata/dotenv/included_envs/testdata/TestDotenvIncludeDotenvs-included_dotenv_has_priority.golden @@ -0,0 +1,2 @@ +var FOO = bar +env FOO = bar