mirror of
https://github.com/go-task/task.git
synced 2025-05-13 22:16:31 +02:00
#324 implement dotenv
This commit is contained in:
parent
e5a3c861cb
commit
8b962fb8e8
@ -33,8 +33,10 @@ executable called must be available by the OS or in PATH.
|
|||||||
|
|
||||||
If you omit a task name, "default" will be assumed.
|
If you omit a task name, "default" will be assumed.
|
||||||
|
|
||||||
## Environment
|
## Environment variables
|
||||||
|
|
||||||
|
|
||||||
|
### Task
|
||||||
You can use `env` to set custom environment variables for a specific task:
|
You can use `env` to set custom environment variables for a specific task:
|
||||||
|
|
||||||
```yaml
|
```yaml
|
||||||
@ -66,6 +68,39 @@ tasks:
|
|||||||
> NOTE: `env` supports expansion and retrieving output from a shell command
|
> NOTE: `env` supports expansion and retrieving output from a shell command
|
||||||
> just like variables, as you can see on the [Variables](#variables) section.
|
> just like variables, as you can see on the [Variables](#variables) section.
|
||||||
|
|
||||||
|
|
||||||
|
### Operating System
|
||||||
|
Environment variables from the OS are accessible using `$VARNAME`:
|
||||||
|
|
||||||
|
```yaml
|
||||||
|
version: '2'
|
||||||
|
|
||||||
|
tasks:
|
||||||
|
greet:
|
||||||
|
cmds:
|
||||||
|
- echo "Hello $USER"
|
||||||
|
```
|
||||||
|
|
||||||
|
### .env
|
||||||
|
|
||||||
|
*.env* files are supported in v3 using the `dotenv` declaration:
|
||||||
|
|
||||||
|
.env
|
||||||
|
```
|
||||||
|
KEYNAME=VALUE
|
||||||
|
```
|
||||||
|
Taskfile.yml
|
||||||
|
```yaml
|
||||||
|
version: '3'
|
||||||
|
|
||||||
|
dotenv: ['.env']
|
||||||
|
|
||||||
|
tasks:
|
||||||
|
greet:
|
||||||
|
cmds:
|
||||||
|
- echo "Using $KEYNAME"
|
||||||
|
```
|
||||||
|
|
||||||
## Operating System specific tasks
|
## Operating System specific tasks
|
||||||
|
|
||||||
If you add a `Taskfile_{{GOOS}}.yml` you can override or amend your Taskfile
|
If you add a `Taskfile_{{GOOS}}.yml` you can override or amend your Taskfile
|
||||||
|
1
go.mod
1
go.mod
@ -3,6 +3,7 @@ module github.com/go-task/task/v2
|
|||||||
require (
|
require (
|
||||||
github.com/fatih/color v1.7.0
|
github.com/fatih/color v1.7.0
|
||||||
github.com/go-task/slim-sprig v0.0.0-20200516131648-f9bac4e523eb
|
github.com/go-task/slim-sprig v0.0.0-20200516131648-f9bac4e523eb
|
||||||
|
github.com/joho/godotenv v1.3.0
|
||||||
github.com/mattn/go-colorable v0.1.2 // indirect
|
github.com/mattn/go-colorable v0.1.2 // indirect
|
||||||
github.com/mattn/go-zglob v0.0.1
|
github.com/mattn/go-zglob v0.0.1
|
||||||
github.com/radovskyb/watcher v1.0.5
|
github.com/radovskyb/watcher v1.0.5
|
||||||
|
2
go.sum
2
go.sum
@ -11,6 +11,8 @@ github.com/fatih/color v1.7.0/go.mod h1:Zm6kSWBoL9eyXnKyktHP6abPY2pDugNf5Kwzbycv
|
|||||||
github.com/go-task/slim-sprig v0.0.0-20200516131648-f9bac4e523eb h1:/qbv1F67s6ehqX9mG23cJOeca3FWpOVKgtPfPUMAi0k=
|
github.com/go-task/slim-sprig v0.0.0-20200516131648-f9bac4e523eb h1:/qbv1F67s6ehqX9mG23cJOeca3FWpOVKgtPfPUMAi0k=
|
||||||
github.com/go-task/slim-sprig v0.0.0-20200516131648-f9bac4e523eb/go.mod h1:fyg7847qk6SyHyPtNmDHnmrv/HOrqktSC+C9fM+CJOE=
|
github.com/go-task/slim-sprig v0.0.0-20200516131648-f9bac4e523eb/go.mod h1:fyg7847qk6SyHyPtNmDHnmrv/HOrqktSC+C9fM+CJOE=
|
||||||
github.com/google/renameio v0.1.0/go.mod h1:KWCgfxg9yswjAJkECMjeO8J8rahYeXnNhOm40UhjYkI=
|
github.com/google/renameio v0.1.0/go.mod h1:KWCgfxg9yswjAJkECMjeO8J8rahYeXnNhOm40UhjYkI=
|
||||||
|
github.com/joho/godotenv v1.3.0 h1:Zjp+RcGpHhGlrMbJzXTrZZPrWj+1vfm90La1wgB6Bhc=
|
||||||
|
github.com/joho/godotenv v1.3.0/go.mod h1:7hK45KPybAkOC6peb+G5yklZfMxEjkZhHbwpqxOKXbg=
|
||||||
github.com/kr/pretty v0.1.0 h1:L/CwN0zerZDmRFUapSPitk6f+Q3+0za1rQkzVuMiMFI=
|
github.com/kr/pretty v0.1.0 h1:L/CwN0zerZDmRFUapSPitk6f+Q3+0za1rQkzVuMiMFI=
|
||||||
github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo=
|
github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo=
|
||||||
github.com/kr/pretty v0.2.0 h1:s5hAObm+yFO5uHYt5dYjxi2rXrsnmRpJx4OYvIWUaQs=
|
github.com/kr/pretty v0.2.0 h1:s5hAObm+yFO5uHYt5dYjxi2rXrsnmRpJx4OYvIWUaQs=
|
||||||
|
@ -3,6 +3,7 @@ package read
|
|||||||
import (
|
import (
|
||||||
"errors"
|
"errors"
|
||||||
"fmt"
|
"fmt"
|
||||||
|
"github.com/joho/godotenv"
|
||||||
"os"
|
"os"
|
||||||
"path/filepath"
|
"path/filepath"
|
||||||
"runtime"
|
"runtime"
|
||||||
@ -16,6 +17,7 @@ import (
|
|||||||
var (
|
var (
|
||||||
// ErrIncludedTaskfilesCantHaveIncludes is returned when a included Taskfile contains includes
|
// ErrIncludedTaskfilesCantHaveIncludes is returned when a included Taskfile contains includes
|
||||||
ErrIncludedTaskfilesCantHaveIncludes = errors.New("task: Included Taskfiles can't have includes. Please, move the include to the main Taskfile")
|
ErrIncludedTaskfilesCantHaveIncludes = errors.New("task: Included Taskfiles can't have includes. Please, move the include to the main Taskfile")
|
||||||
|
ErrIncludedTaskfilesCantHaveDotenvs = errors.New("task: Included Taskfiles can't have dotenv declarations. Please, move the dotenv declaration to the main Taskfile")
|
||||||
)
|
)
|
||||||
|
|
||||||
// Taskfile reads a Taskfile for a given directory
|
// Taskfile reads a Taskfile for a given directory
|
||||||
@ -34,6 +36,22 @@ func Taskfile(dir string, entrypoint string) (*taskfile.Taskfile, error) {
|
|||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if v >= 3.0 {
|
||||||
|
if len(t.Dotenv) > 0 {
|
||||||
|
for _, envFile := range t.Dotenv {
|
||||||
|
var envFilePath string
|
||||||
|
if filepath.IsAbs(envFile) {
|
||||||
|
envFilePath = envFile
|
||||||
|
} else {
|
||||||
|
envFilePath = filepath.Join(dir, envFile)
|
||||||
|
}
|
||||||
|
if err = godotenv.Load(envFilePath); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
for namespace, includedTask := range t.Includes {
|
for namespace, includedTask := range t.Includes {
|
||||||
if v >= 3.0 {
|
if v >= 3.0 {
|
||||||
tr := templater.Templater{Vars: &taskfile.Vars{}, RemoveNoValue: true}
|
tr := templater.Templater{Vars: &taskfile.Vars{}, RemoveNoValue: true}
|
||||||
@ -68,6 +86,12 @@ func Taskfile(dir string, entrypoint string) (*taskfile.Taskfile, error) {
|
|||||||
return nil, ErrIncludedTaskfilesCantHaveIncludes
|
return nil, ErrIncludedTaskfilesCantHaveIncludes
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if v >= 3.0 {
|
||||||
|
if len(includedTaskfile.Dotenv) > 0 {
|
||||||
|
return nil, ErrIncludedTaskfilesCantHaveDotenvs
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
if includedTask.AdvancedImport {
|
if includedTask.AdvancedImport {
|
||||||
for _, task := range includedTaskfile.Tasks {
|
for _, task := range includedTaskfile.Tasks {
|
||||||
if !filepath.IsAbs(task.Dir) {
|
if !filepath.IsAbs(task.Dir) {
|
||||||
|
@ -16,6 +16,7 @@ type Taskfile struct {
|
|||||||
Env *Vars
|
Env *Vars
|
||||||
Tasks Tasks
|
Tasks Tasks
|
||||||
Silent bool
|
Silent bool
|
||||||
|
Dotenv []string
|
||||||
}
|
}
|
||||||
|
|
||||||
// UnmarshalYAML implements yaml.Unmarshaler interface
|
// UnmarshalYAML implements yaml.Unmarshaler interface
|
||||||
@ -30,6 +31,7 @@ func (tf *Taskfile) UnmarshalYAML(unmarshal func(interface{}) error) error {
|
|||||||
Env *Vars
|
Env *Vars
|
||||||
Tasks Tasks
|
Tasks Tasks
|
||||||
Silent bool
|
Silent bool
|
||||||
|
Dotenv []string
|
||||||
}
|
}
|
||||||
if err := unmarshal(&taskfile); err != nil {
|
if err := unmarshal(&taskfile); err != nil {
|
||||||
return err
|
return err
|
||||||
@ -43,6 +45,7 @@ func (tf *Taskfile) UnmarshalYAML(unmarshal func(interface{}) error) error {
|
|||||||
tf.Env = taskfile.Env
|
tf.Env = taskfile.Env
|
||||||
tf.Tasks = taskfile.Tasks
|
tf.Tasks = taskfile.Tasks
|
||||||
tf.Silent = taskfile.Silent
|
tf.Silent = taskfile.Silent
|
||||||
|
tf.Dotenv = taskfile.Dotenv
|
||||||
if tf.Expansions <= 0 {
|
if tf.Expansions <= 0 {
|
||||||
tf.Expansions = 2
|
tf.Expansions = 2
|
||||||
}
|
}
|
||||||
|
47
task_test.go
47
task_test.go
@ -816,3 +816,50 @@ func TestShortTaskNotation(t *testing.T) {
|
|||||||
assert.NoError(t, e.Run(context.Background(), taskfile.Call{Task: "default"}))
|
assert.NoError(t, e.Run(context.Background(), taskfile.Call{Task: "default"}))
|
||||||
assert.Equal(t, "string-slice-1\nstring-slice-2\nstring\n", buff.String())
|
assert.Equal(t, "string-slice-1\nstring-slice-2\nstring\n", buff.String())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func TestDotenvShouldIncludeAllEnvFiles(t *testing.T) {
|
||||||
|
tt := fileContentTest{
|
||||||
|
Dir: "testdata/dotenv",
|
||||||
|
Target: "default",
|
||||||
|
TrimSpace: false,
|
||||||
|
Files: map[string]string{
|
||||||
|
"include.txt": "INCLUDE1='from_include1' INCLUDE2='from_include2'\n",
|
||||||
|
},
|
||||||
|
}
|
||||||
|
tt.Run(t)
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestDotenvShouldErrorWithIncludeEnvPath(t *testing.T) {
|
||||||
|
const dir = "testdata/dotenv"
|
||||||
|
const entry = "Taskfile-errors1.yml"
|
||||||
|
|
||||||
|
var buff bytes.Buffer
|
||||||
|
e := task.Executor{
|
||||||
|
Dir: dir,
|
||||||
|
Entrypoint: entry,
|
||||||
|
Summary: true,
|
||||||
|
Stdout: &buff,
|
||||||
|
Stderr: &buff,
|
||||||
|
}
|
||||||
|
err := e.Setup()
|
||||||
|
assert.Error(t, err)
|
||||||
|
assert.Contains(t, err.Error(), "no such file")
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestDotenvShouldErrorWhenIncludingDependantDotenvs(t *testing.T) {
|
||||||
|
const dir = "testdata/dotenv"
|
||||||
|
const entry = "Taskfile-errors2.yml"
|
||||||
|
|
||||||
|
var buff bytes.Buffer
|
||||||
|
e := task.Executor{
|
||||||
|
Dir: dir,
|
||||||
|
Entrypoint: entry,
|
||||||
|
Summary: true,
|
||||||
|
Stdout: &buff,
|
||||||
|
Stderr: &buff,
|
||||||
|
}
|
||||||
|
|
||||||
|
err := e.Setup()
|
||||||
|
assert.Error(t, err)
|
||||||
|
assert.Contains(t, err.Error(), "move the dotenv")
|
||||||
|
}
|
||||||
|
1
testdata/dotenv/.gitignore
vendored
Normal file
1
testdata/dotenv/.gitignore
vendored
Normal file
@ -0,0 +1 @@
|
|||||||
|
*.txt
|
8
testdata/dotenv/Taskfile-errors1.yml
vendored
Normal file
8
testdata/dotenv/Taskfile-errors1.yml
vendored
Normal file
@ -0,0 +1,8 @@
|
|||||||
|
version: '3'
|
||||||
|
|
||||||
|
dotenv: ['include1/.env', 'include1/envs/.env', 'file-does-not-exist']
|
||||||
|
|
||||||
|
tasks:
|
||||||
|
default:
|
||||||
|
cmds:
|
||||||
|
- echo "INCLUDE1='$INCLUDE1' INCLUDE2='$INCLUDE2'" > include-errors1.txt
|
9
testdata/dotenv/Taskfile-errors2.yml
vendored
Normal file
9
testdata/dotenv/Taskfile-errors2.yml
vendored
Normal file
@ -0,0 +1,9 @@
|
|||||||
|
version: '3'
|
||||||
|
|
||||||
|
includes:
|
||||||
|
include1: './include1'
|
||||||
|
|
||||||
|
tasks:
|
||||||
|
default:
|
||||||
|
cmds:
|
||||||
|
- echo "INCLUDE1='$INCLUDE1' INCLUDE2='$INCLUDE2'" > include-errors2.txt
|
8
testdata/dotenv/Taskfile.yml
vendored
Normal file
8
testdata/dotenv/Taskfile.yml
vendored
Normal file
@ -0,0 +1,8 @@
|
|||||||
|
version: '3'
|
||||||
|
|
||||||
|
dotenv: ['include1/.env', 'include1/envs/.env']
|
||||||
|
|
||||||
|
tasks:
|
||||||
|
default:
|
||||||
|
cmds:
|
||||||
|
- echo "INCLUDE1='$INCLUDE1' INCLUDE2='$INCLUDE2'" > include.txt
|
1
testdata/dotenv/include1/.env
vendored
Normal file
1
testdata/dotenv/include1/.env
vendored
Normal file
@ -0,0 +1 @@
|
|||||||
|
INCLUDE1=from_include1
|
3
testdata/dotenv/include1/Taskfile.yml
vendored
Normal file
3
testdata/dotenv/include1/Taskfile.yml
vendored
Normal file
@ -0,0 +1,3 @@
|
|||||||
|
version: '3'
|
||||||
|
|
||||||
|
dotenv: ['.env']
|
1
testdata/dotenv/include1/envs/.env
vendored
Normal file
1
testdata/dotenv/include1/envs/.env
vendored
Normal file
@ -0,0 +1 @@
|
|||||||
|
INCLUDE2=from_include2
|
Loading…
x
Reference in New Issue
Block a user