Skip to content

Commit 08265ed

Browse files
nfoandreynering
andcommitted
Allow vars in dotenv paths, including environment variables
Closes #453 Closes #434 Ref #433 Co-authored-by: Andrey Nering <[email protected]>
1 parent cded9af commit 08265ed

File tree

14 files changed

+165
-28
lines changed

14 files changed

+165
-28
lines changed

docs/usage.md

Lines changed: 9 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -78,18 +78,25 @@ setting:
7878
KEYNAME=VALUE
7979
```
8080

81+
```
82+
# testing/.env
83+
ENDPOINT=testing.com
84+
```
8185
8286
```yaml
8387
# Taskfile.yml
8488
8589
version: '3'
8690
87-
dotenv: ['.env']
91+
env:
92+
ENV: testing
93+
94+
dotenv: ['.env', '{{.ENV}}/.env.', '{{.HOME}}/.env']
8895
8996
tasks:
9097
greet:
9198
cmds:
92-
- echo "Using $KEYNAME"
99+
- echo "Using $KEYNAME and endpoint $ENDPOINT"
93100
```
94101

95102
## Including other Taskfiles

internal/compiler/compiler.go

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@ import (
77
// Compiler handles compilation of a task before its execution.
88
// E.g. variable merger, template processing, etc.
99
type Compiler interface {
10+
GetTaskfileVariables() (*taskfile.Vars, error)
1011
GetVariables(t *taskfile.Task, call taskfile.Call) (*taskfile.Vars, error)
1112
FastGetVariables(t *taskfile.Task, call taskfile.Call) (*taskfile.Vars, error)
1213
HandleDynamicVar(v taskfile.Var, dir string) (string, error)

internal/compiler/v2/compiler_v2.go

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,10 @@ type CompilerV2 struct {
3030
muDynamicCache sync.Mutex
3131
}
3232

33+
func (c *CompilerV2) GetTaskfileVariables() (*taskfile.Vars, error) {
34+
return &taskfile.Vars{}, nil
35+
}
36+
3337
// FastGetVariables is a no-op on v2
3438
func (c *CompilerV2) FastGetVariables(t *taskfile.Task, call taskfile.Call) (*taskfile.Vars, error) {
3539
return c.GetVariables(t, call)

internal/compiler/v3/compiler_v3.go

Lines changed: 15 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -29,17 +29,23 @@ type CompilerV3 struct {
2929
muDynamicCache sync.Mutex
3030
}
3131

32+
func (c *CompilerV3) GetTaskfileVariables() (*taskfile.Vars, error) {
33+
return c.getVariables(nil, nil, true)
34+
}
35+
3236
func (c *CompilerV3) GetVariables(t *taskfile.Task, call taskfile.Call) (*taskfile.Vars, error) {
33-
return c.getVariables(t, call, true)
37+
return c.getVariables(t, &call, true)
3438
}
3539

3640
func (c *CompilerV3) FastGetVariables(t *taskfile.Task, call taskfile.Call) (*taskfile.Vars, error) {
37-
return c.getVariables(t, call, false)
41+
return c.getVariables(t, &call, false)
3842
}
3943

40-
func (c *CompilerV3) getVariables(t *taskfile.Task, call taskfile.Call, evaluateShVars bool) (*taskfile.Vars, error) {
44+
func (c *CompilerV3) getVariables(t *taskfile.Task, call *taskfile.Call, evaluateShVars bool) (*taskfile.Vars, error) {
4145
result := compiler.GetEnviron()
42-
result.Set("TASK", taskfile.Var{Static: t.Task})
46+
if t != nil {
47+
result.Set("TASK", taskfile.Var{Static: t.Task})
48+
}
4349

4450
getRangeFunc := func(dir string) func(k string, v taskfile.Var) error {
4551
return func(k string, v taskfile.Var) error {
@@ -74,6 +80,11 @@ func (c *CompilerV3) getVariables(t *taskfile.Task, call taskfile.Call, evaluate
7480
if err := c.TaskfileVars.Range(rangeFunc); err != nil {
7581
return nil, err
7682
}
83+
84+
if t == nil || call == nil {
85+
return result, nil
86+
}
87+
7788
if err := call.Vars.Range(rangeFunc); err != nil {
7889
return nil, err
7990
}

task.go

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -176,6 +176,23 @@ func (e *Executor) Setup() error {
176176
}
177177
}
178178

179+
if v >= 3.0 {
180+
env, err := read.Dotenv(e.Compiler, e.Taskfile, e.Dir)
181+
if err != nil {
182+
return err
183+
}
184+
185+
err = env.Range(func(key string, value taskfile.Var) error {
186+
if _, ok := e.Taskfile.Env.Mapping[key]; !ok {
187+
e.Taskfile.Env.Set(key, value)
188+
}
189+
return nil
190+
})
191+
if err != nil {
192+
return err
193+
}
194+
}
195+
179196
if v < 2.1 && e.Taskfile.Output != "" {
180197
return fmt.Errorf(`task: Taskfile option "output" is only available starting on Taskfile version v2.1`)
181198
}

task_test.go

Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -904,6 +904,44 @@ func TestDotenvShouldAllowMissingEnv(t *testing.T) {
904904
tt.Run(t)
905905
}
906906

907+
func TestDotenvHasLocalEnvInPath(t *testing.T) {
908+
tt := fileContentTest{
909+
Dir: "testdata/dotenv/local_env_in_path",
910+
Target: "default",
911+
TrimSpace: false,
912+
Files: map[string]string{
913+
"var.txt": "VAR='var_in_dot_env_1'\n",
914+
},
915+
}
916+
tt.Run(t)
917+
}
918+
919+
func TestDotenvHasLocalVarInPath(t *testing.T) {
920+
tt := fileContentTest{
921+
Dir: "testdata/dotenv/local_var_in_path",
922+
Target: "default",
923+
TrimSpace: false,
924+
Files: map[string]string{
925+
"var.txt": "VAR='var_in_dot_env_3'\n",
926+
},
927+
}
928+
tt.Run(t)
929+
}
930+
931+
func TestDotenvHasEnvVarInPath(t *testing.T) {
932+
os.Setenv("ENV_VAR", "testing")
933+
934+
tt := fileContentTest{
935+
Dir: "testdata/dotenv/env_var_in_path",
936+
Target: "default",
937+
TrimSpace: false,
938+
Files: map[string]string{
939+
"var.txt": "VAR='var_in_dot_env_2'\n",
940+
},
941+
}
942+
tt.Run(t)
943+
}
944+
907945
func TestExitImmediately(t *testing.T) {
908946
const dir = "testdata/exit_immediately"
909947

taskfile/read/dotenv.go

Lines changed: 46 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,46 @@
1+
package read
2+
3+
import (
4+
"os"
5+
"path/filepath"
6+
7+
"github.com/joho/godotenv"
8+
9+
"github.com/go-task/task/v3/internal/compiler"
10+
"github.com/go-task/task/v3/internal/templater"
11+
"github.com/go-task/task/v3/taskfile"
12+
)
13+
14+
func Dotenv(c compiler.Compiler, tf *taskfile.Taskfile, dir string) (*taskfile.Vars, error) {
15+
vars, err := c.GetTaskfileVariables()
16+
if err != nil {
17+
return nil, err
18+
}
19+
20+
env := &taskfile.Vars{}
21+
22+
tr := templater.Templater{Vars: vars, RemoveNoValue: true}
23+
24+
for _, dotEnvPath := range tf.Dotenv {
25+
dotEnvPath = tr.Replace(dotEnvPath)
26+
27+
if !filepath.IsAbs(dotEnvPath) {
28+
dotEnvPath = filepath.Join(dir, dotEnvPath)
29+
}
30+
if _, err := os.Stat(dotEnvPath); os.IsNotExist(err) {
31+
continue
32+
}
33+
34+
envs, err := godotenv.Read(dotEnvPath)
35+
if err != nil {
36+
return nil, err
37+
}
38+
for key, value := range envs {
39+
if _, ok := env.Mapping[key]; !ok {
40+
env.Set(key, taskfile.Var{Static: value})
41+
}
42+
}
43+
}
44+
45+
return env, nil
46+
}

taskfile/read/taskfile.go

Lines changed: 0 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,6 @@ import (
77
"path/filepath"
88
"runtime"
99

10-
"github.com/joho/godotenv"
1110
"gopkg.in/yaml.v3"
1211

1312
"github.com/go-task/task/v3/internal/templater"
@@ -37,27 +36,6 @@ func Taskfile(dir string, entrypoint string) (*taskfile.Taskfile, error) {
3736
return nil, err
3837
}
3938

40-
if v >= 3.0 {
41-
for _, dotEnvPath := range t.Dotenv {
42-
if !filepath.IsAbs(dotEnvPath) {
43-
dotEnvPath = filepath.Join(dir, dotEnvPath)
44-
}
45-
if _, err := os.Stat(dotEnvPath); os.IsNotExist(err) {
46-
continue
47-
}
48-
49-
envs, err := godotenv.Read(dotEnvPath)
50-
if err != nil {
51-
return nil, err
52-
}
53-
for key, value := range envs {
54-
if _, ok := t.Env.Mapping[key]; !ok {
55-
t.Env.Set(key, taskfile.Var{Static: value})
56-
}
57-
}
58-
}
59-
}
60-
6139
err = t.Includes.Range(func(namespace string, includedTask taskfile.IncludedTaskfile) error {
6240
if v >= 3.0 {
6341
tr := templater.Templater{Vars: &taskfile.Vars{}, RemoveNoValue: true}
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
VAR_IN_DOTENV=var_in_dot_env_2
Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
version: "3"
2+
3+
dotenv: [".env.{{.ENV_VAR}}"]
4+
5+
tasks:
6+
default:
7+
cmds:
8+
- echo "VAR='$VAR_IN_DOTENV'" > var.txt

0 commit comments

Comments
 (0)