mirror of
				https://github.com/go-task/task.git
				synced 2025-10-30 23:58:01 +02:00 
			
		
		
		
	feat: experiment taskfile envs take precedence over os envs (#1633)
* feat: experiment taskfile envs take precedence over os envs * fix test * fix typo Co-authored-by: Andrey Nering <andrey@nering.com.br> * docs: add p about default --------- Co-authored-by: Andrey Nering <andrey@nering.com.br>
This commit is contained in:
		
							
								
								
									
										9
									
								
								internal/env/env.go
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										9
									
								
								internal/env/env.go
									
									
									
									
										vendored
									
									
								
							| @@ -4,6 +4,7 @@ import ( | ||||
| 	"fmt" | ||||
| 	"os" | ||||
|  | ||||
| 	"github.com/go-task/task/v3/internal/experiments" | ||||
| 	"github.com/go-task/task/v3/taskfile/ast" | ||||
| ) | ||||
|  | ||||
| @@ -11,15 +12,15 @@ func Get(t *ast.Task) []string { | ||||
| 	if t.Env == nil { | ||||
| 		return nil | ||||
| 	} | ||||
|  | ||||
| 	environ := os.Environ() | ||||
| 	for k, v := range t.Env.ToCacheMap() { | ||||
| 		if !isTypeAllowed(v) { | ||||
| 			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=%v", k, v)) | ||||
| 	} | ||||
|   | ||||
| @@ -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() | ||||
| } | ||||
|   | ||||
							
								
								
									
										17
									
								
								task_test.go
									
									
									
									
									
								
							
							
						
						
									
										17
									
								
								task_test.go
									
									
									
									
									
								
							| @@ -19,6 +19,7 @@ import ( | ||||
|  | ||||
| 	"github.com/go-task/task/v3" | ||||
| 	"github.com/go-task/task/v3/errors" | ||||
| 	"github.com/go-task/task/v3/internal/experiments" | ||||
| 	"github.com/go-task/task/v3/internal/filepathext" | ||||
| 	"github.com/go-task/task/v3/taskfile/ast" | ||||
| ) | ||||
| @@ -60,7 +61,6 @@ func (fct fileContentTest) Run(t *testing.T) { | ||||
| 	for f := range fct.Files { | ||||
| 		_ = os.Remove(filepathext.SmartJoin(fct.Dir, f)) | ||||
| 	} | ||||
|  | ||||
| 	e := &task.Executor{ | ||||
| 		Dir: fct.Dir, | ||||
| 		TempDir: task.TempDir{ | ||||
| @@ -71,9 +71,9 @@ 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)") | ||||
|  | ||||
| 	for name, expectContent := range fct.Files { | ||||
| 		t.Run(fct.name(name), func(t *testing.T) { | ||||
| 			path := filepathext.SmartJoin(e.Dir, name) | ||||
| @@ -108,6 +108,7 @@ func TestEmptyTaskfile(t *testing.T) { | ||||
| } | ||||
|  | ||||
| func TestEnv(t *testing.T) { | ||||
| 	t.Setenv("QUX", "from_os") | ||||
| 	tt := fileContentTest{ | ||||
| 		Dir:       "testdata/env", | ||||
| 		Target:    "default", | ||||
| @@ -116,9 +117,21 @@ func TestEnv(t *testing.T) { | ||||
| 			"local.txt":         "GOOS='linux' GOARCH='amd64' CGO_ENABLED='0'\n", | ||||
| 			"global.txt":        "FOO='foo' BAR='overriden' BAZ='baz'\n", | ||||
| 			"multiple_type.txt": "FOO='1' BAR='true' BAZ='1.1'\n", | ||||
| 			"not-overriden.txt": "QUX='from_os'\n", | ||||
| 		}, | ||||
| 	} | ||||
| 	tt.Run(t) | ||||
| 	t.Setenv("TASK_X_ENV_PRECEDENCE", "1") | ||||
| 	experiments.EnvPrecedence = experiments.New("ENV_PRECEDENCE") | ||||
| 	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) { | ||||
|   | ||||
							
								
								
									
										10
									
								
								testdata/env/Taskfile.yml
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										10
									
								
								testdata/env/Taskfile.yml
									
									
									
									
										vendored
									
									
								
							| @@ -8,12 +8,14 @@ env: | ||||
|   FOO: foo | ||||
|   BAR: bar | ||||
|   BAZ: "{{.BAZ}}" | ||||
|   QUX: from_taskfile | ||||
|  | ||||
| tasks: | ||||
|   default: | ||||
|     cmds: | ||||
|       - task: local | ||||
|       - task: global | ||||
|       - task: not-overriden | ||||
|       - task: multiple_type | ||||
|  | ||||
|   local: | ||||
| @@ -40,3 +42,11 @@ tasks: | ||||
|       BAZ: 1.1 | ||||
|     cmds: | ||||
|       - echo "FOO='$FOO' BAR='$BAR' BAZ='$BAZ'" > multiple_type.txt | ||||
|  | ||||
|   not-overriden: | ||||
|     cmds: | ||||
|       - echo "QUX='$QUX'" > not-overriden.txt | ||||
|  | ||||
|   overriden: | ||||
|     cmds: | ||||
|       - echo "QUX='$QUX'" > overriden.txt | ||||
|   | ||||
							
								
								
									
										70
									
								
								website/docs/experiments/env_precedence.mdx
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										70
									
								
								website/docs/experiments/env_precedence.mdx
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,70 @@ | ||||
| --- | ||||
| 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`. | ||||
|  | ||||
| Like other variables/envs, you can also fall back to a given value using the default template function: | ||||
| ```yml | ||||
| MY_ENV: '{{.MY_ENV | default "fallback"}}' | ||||
| ``` | ||||
|  | ||||
| {/* prettier-ignore-start */} | ||||
| [enabling-experiments]: ./experiments.mdx#enabling-experiments | ||||
| {/* prettier-ignore-end */} | ||||
		Reference in New Issue
	
	Block a user