diff --git a/internal/env/env.go b/internal/env/env.go index 133b8d81b7..34a3a913c9 100644 --- a/internal/env/env.go +++ b/internal/env/env.go @@ -4,6 +4,7 @@ import ( "fmt" "os" + "github.com/go-task/task/v3/internal/experiments" "github.com/go-task/task/v3/taskfile/ast" ) @@ -11,7 +12,6 @@ func Get(t *ast.Task) []string { if t.Env == nil { return nil } - environ := os.Environ() for k, v := range t.Env.ToCacheMap() { @@ -19,9 +19,10 @@ func Get(t *ast.Task) []string { if !isString { continue } - - if _, alreadySet := os.LookupEnv(k); alreadySet { - continue + if !experiments.EnvPrecedence.Enabled { + if _, alreadySet := os.LookupEnv(k); alreadySet { + continue + } } environ = append(environ, fmt.Sprintf("%s=%s", k, str)) diff --git a/internal/experiments/experiments.go b/internal/experiments/experiments.go index 160f4219f2..5b69243a1e 100644 --- a/internal/experiments/experiments.go +++ b/internal/experiments/experiments.go @@ -29,6 +29,7 @@ var ( RemoteTaskfiles Experiment AnyVariables Experiment MapVariables Experiment + EnvPrecedence Experiment ) func init() { @@ -37,6 +38,7 @@ func init() { RemoteTaskfiles = New("REMOTE_TASKFILES") AnyVariables = New("ANY_VARIABLES", "1", "2") MapVariables = New("MAP_VARIABLES", "1", "2") + EnvPrecedence = New("ENV_PRECEDENCE") } func New(xName string, enabledValues ...string) Experiment { @@ -104,5 +106,6 @@ func List(l *logger.Logger) error { printExperiment(w, l, GentleForce) printExperiment(w, l, RemoteTaskfiles) printExperiment(w, l, MapVariables) + printExperiment(w, l, EnvPrecedence) return w.Flush() } diff --git a/task_test.go b/task_test.go index 207b08b6d7..b4548fdb44 100644 --- a/task_test.go +++ b/task_test.go @@ -60,7 +60,7 @@ func (fct fileContentTest) Run(t *testing.T) { for f := range fct.Files { _ = os.Remove(filepathext.SmartJoin(fct.Dir, f)) } - + t.Setenv("TASK_X_ENV_PRECEDENCE", "1") e := &task.Executor{ Dir: fct.Dir, TempDir: filepathext.SmartJoin(fct.Dir, ".task"), @@ -68,6 +68,7 @@ func (fct fileContentTest) Run(t *testing.T) { Stdout: io.Discard, Stderr: io.Discard, } + require.NoError(t, e.Setup(), "e.Setup()") require.NoError(t, e.Run(context.Background(), &ast.Call{Task: fct.Target}), "e.Run(target)") @@ -96,16 +97,30 @@ func TestEmptyTask(t *testing.T) { } func TestEnv(t *testing.T) { + t.Setenv("QUX", "from_env") tt := fileContentTest{ Dir: "testdata/env", Target: "default", TrimSpace: false, Files: map[string]string{ - "local.txt": "GOOS='linux' GOARCH='amd64' CGO_ENABLED='0'\n", - "global.txt": "FOO='foo' BAR='overriden' BAZ='baz'\n", + "local.txt": "GOOS='linux' GOARCH='amd64' CGO_ENABLED='0'\n", + "global.txt": "FOO='foo' BAR='overriden' BAZ='baz'\n", + "not-overriden.txt": "QUX='from_env'\n", }, } tt.Run(t) + + t.Setenv("TASK_X_ENV_PRECEDENCE", "1") + + ttt := fileContentTest{ + Dir: "testdata/env", + Target: "overriden", + TrimSpace: false, + Files: map[string]string{ + "overriden.txt": "QUX='from_taskfile'\n", + }, + } + ttt.Run(t) } func TestVars(t *testing.T) { diff --git a/testdata/env/Taskfile.yml b/testdata/env/Taskfile.yml index cb88f95e05..a331b9eff0 100644 --- a/testdata/env/Taskfile.yml +++ b/testdata/env/Taskfile.yml @@ -8,12 +8,14 @@ env: FOO: foo BAR: bar BAZ: "{{.BAZ}}" + QUX: from_taskfile tasks: default: cmds: - task: local - task: global + - task: not-overriden local: vars: @@ -31,3 +33,11 @@ tasks: BAR: overriden cmds: - echo "FOO='$FOO' BAR='$BAR' BAZ='$BAZ'" > global.txt + + not-overriden: + cmds: + - echo "QUX='$QUX'" > not-overriden.txt + + overriden: + cmds: + - echo "QUX='$QUX'" > overriden.txt diff --git a/website/docs/experiments/env_precedence.mdx b/website/docs/experiments/env_precedence.mdx new file mode 100644 index 0000000000..f95ad5cb35 --- /dev/null +++ b/website/docs/experiments/env_precedence.mdx @@ -0,0 +1,65 @@ +--- +draft: false # Hide in production +slug: '/experiments/env-precedence' +--- + +# Env Precedence (#1038) + +:::caution + +All experimental features are subject to breaking changes and/or removal _at any +time_. We strongly recommend that you do not use these features in a production +environment. They are intended for testing and feedback only. + +::: + +:::warning + +This experiment breaks the following functionality: + +- environment variable will take precedence over OS environment variables + +::: + +:::info + +To enable this experiment, set the environment variable: `TASK_X_ENV_PRECEDENCE=1`. +Check out [our guide to enabling experiments ][enabling-experiments] for more +information. + +::: + +Before this experiment, the OS variable took precedence over the task environment variable. This experiment changes the precedence to make the task environment variable take precedence over the OS variable. + +Consider the following example: + +```yml +version: '3' + +tasks: + default: + env: + KEY: 'other' + cmds: + - echo "$KEY" +``` +Running `KEY=some task` before this experiment, the output would be `some`, but after this experiment, the output would be `other`. + +If you still want to get the OS variable, you can use the template function env like follow : `{{env.OS_VAR}}`. + +```yml +version: '3' + +tasks: + default: + env: + KEY: 'other' + cmds: + - echo "$KEY" + - echo {{env "KEY"}} +``` +Running `KEY=some task`, the output would be `other` and `some`. + +{/* prettier-ignore-start */} +[enabling-experiments]: ./experiments.mdx#enabling-experiments +{/* prettier-ignore-end */}