From 376f2945593bd4fe33ec4c674458de1b8935f29b Mon Sep 17 00:00:00 2001 From: rektdeckard Date: Wed, 22 Jan 2025 19:20:15 -0700 Subject: [PATCH] fix(experiments): use getters to prevent toc/tou limitations --- cmd/task/task.go | 2 +- internal/env/env.go | 2 +- internal/experiments/experiments.go | 36 ++++++++++++++++++----------- internal/flags/flags.go | 4 ++-- task_test.go | 8 ++++--- taskfile/ast/var.go | 7 +++--- taskfile/node.go | 2 +- 7 files changed, 36 insertions(+), 25 deletions(-) diff --git a/cmd/task/task.go b/cmd/task/task.go index cf07a5be6a..fbe9f48c74 100644 --- a/cmd/task/task.go +++ b/cmd/task/task.go @@ -145,7 +145,7 @@ func run() error { if err != nil { return err } - if experiments.AnyVariables.Enabled { + if experiments.AnyVariables().Enabled { logger.Warnf("The 'Any Variables' experiment flag is no longer required to use non-map variable types. If you wish to use map variables, please use 'TASK_X_MAP_VARIABLES' instead. See https://github.com/go-task/task/issues/1585\n") } diff --git a/internal/env/env.go b/internal/env/env.go index 1fbb38172d..27391e6158 100644 --- a/internal/env/env.go +++ b/internal/env/env.go @@ -23,7 +23,7 @@ func GetFromVars(env *ast.Vars) []string { if !isTypeAllowed(v) { continue } - if !experiments.EnvPrecedence.Enabled { + if !experiments.EnvPrecedence().Enabled { if _, alreadySet := os.LookupEnv(k); alreadySet { continue } diff --git a/internal/experiments/experiments.go b/internal/experiments/experiments.go index 58d2276437..69513bb254 100644 --- a/internal/experiments/experiments.go +++ b/internal/experiments/experiments.go @@ -23,22 +23,30 @@ type Experiment struct { Value string } +type ExperimentGetter func() Experiment + // A list of experiments. var ( - GentleForce Experiment - RemoteTaskfiles Experiment - AnyVariables Experiment - MapVariables Experiment - EnvPrecedence Experiment + GentleForce ExperimentGetter + RemoteTaskfiles ExperimentGetter + AnyVariables ExperimentGetter + MapVariables ExperimentGetter + EnvPrecedence ExperimentGetter ) func init() { readDotEnv() - GentleForce = New("GENTLE_FORCE") - RemoteTaskfiles = New("REMOTE_TASKFILES") - AnyVariables = New("ANY_VARIABLES", "1", "2") - MapVariables = New("MAP_VARIABLES", "1", "2") - EnvPrecedence = New("ENV_PRECEDENCE") + GentleForce = NewGetter("GENTLE_FORCE") + RemoteTaskfiles = NewGetter("REMOTE_TASKFILES") + AnyVariables = NewGetter("ANY_VARIABLES", "1", "2") + MapVariables = NewGetter("MAP_VARIABLES", "1", "2") + EnvPrecedence = NewGetter("ENV_PRECEDENCE") +} + +func NewGetter(xName string, enabledValues ...string) ExperimentGetter { + return func() Experiment { + return New(xName, enabledValues...) + } } func New(xName string, enabledValues ...string) Experiment { @@ -104,9 +112,9 @@ func printExperiment(w io.Writer, l *logger.Logger, x Experiment) { func List(l *logger.Logger) error { w := tabwriter.NewWriter(os.Stdout, 0, 8, 0, ' ', 0) - printExperiment(w, l, GentleForce) - printExperiment(w, l, RemoteTaskfiles) - printExperiment(w, l, MapVariables) - printExperiment(w, l, EnvPrecedence) + printExperiment(w, l, GentleForce()) + printExperiment(w, l, RemoteTaskfiles()) + printExperiment(w, l, MapVariables()) + printExperiment(w, l, EnvPrecedence()) return w.Flush() } diff --git a/internal/flags/flags.go b/internal/flags/flags.go index 58762c405d..6a05e2d1f8 100644 --- a/internal/flags/flags.go +++ b/internal/flags/flags.go @@ -115,7 +115,7 @@ func init() { pflag.BoolVar(&Experiments, "experiments", false, "Lists all the available experiments and whether or not they are enabled.") // Gentle force experiment will override the force flag and add a new force-all flag - if experiments.GentleForce.Enabled { + if experiments.GentleForce().Enabled { pflag.BoolVarP(&Force, "force", "f", false, "Forces execution of the directly called task.") pflag.BoolVar(&ForceAll, "force-all", false, "Forces execution of the called task and all its dependant tasks.") } else { @@ -123,7 +123,7 @@ func init() { } // Remote Taskfiles experiment will adds the "download" and "offline" flags - if experiments.RemoteTaskfiles.Enabled { + if experiments.RemoteTaskfiles().Enabled { pflag.BoolVar(&Download, "download", false, "Downloads a cached version of a remote Taskfile.") pflag.BoolVar(&Offline, "offline", offline, "Forces Task to only use local or cached Taskfiles.") pflag.DurationVar(&Timeout, "timeout", time.Second*10, "Timeout for downloading remote Taskfiles.") diff --git a/task_test.go b/task_test.go index e1a1a918f9..143c0a6caf 100644 --- a/task_test.go +++ b/task_test.go @@ -135,7 +135,7 @@ func TestEnv(t *testing.T) { } tt.Run(t) t.Setenv("TASK_X_ENV_PRECEDENCE", "1") - experiments.EnvPrecedence = experiments.New("ENV_PRECEDENCE") + experiments.EnvPrecedence = experiments.NewGetter("ENV_PRECEDENCE") ttt := fileContentTest{ Dir: "testdata/env", Target: "overridden", @@ -1220,7 +1220,8 @@ func TestIncludesMultiLevel(t *testing.T) { } func TestIncludesRemote(t *testing.T) { - enableExperimentForTest(t, &experiments.RemoteTaskfiles, "1") + exp := experiments.RemoteTaskfiles() + enableExperimentForTest(t, &exp, "1") dir := "testdata/includes_remote" @@ -1374,7 +1375,8 @@ func TestIncludesEmptyMain(t *testing.T) { } func TestIncludesHttp(t *testing.T) { - enableExperimentForTest(t, &experiments.RemoteTaskfiles, "1") + exp := experiments.RemoteTaskfiles() + enableExperimentForTest(t, &exp, "1") dir, err := filepath.Abs("testdata/includes_http") require.NoError(t, err) diff --git a/taskfile/ast/var.go b/taskfile/ast/var.go index b2d24977d0..3910f4c41d 100644 --- a/taskfile/ast/var.go +++ b/taskfile/ast/var.go @@ -175,10 +175,11 @@ type Var struct { } func (v *Var) UnmarshalYAML(node *yaml.Node) error { - if experiments.MapVariables.Enabled { + mv := experiments.MapVariables() + if mv.Enabled { // This implementation is not backwards-compatible and replaces the 'sh' key with map variables - if experiments.MapVariables.Value == "1" { + if mv.Value == "1" { var value any if err := node.Decode(&value); err != nil { return errors.NewTaskfileDecodeError(err, node) @@ -199,7 +200,7 @@ func (v *Var) UnmarshalYAML(node *yaml.Node) error { } // This implementation IS backwards-compatible and keeps the 'sh' key and allows map variables to be added under the `map` key - if experiments.MapVariables.Value == "2" { + if mv.Value == "2" { switch node.Kind { case yaml.MappingNode: key := node.Content[0].Value diff --git a/taskfile/node.go b/taskfile/node.go index 74b3245eaf..1229eb5d81 100644 --- a/taskfile/node.go +++ b/taskfile/node.go @@ -64,7 +64,7 @@ func NewNode( } - if node.Remote() && !experiments.RemoteTaskfiles.Enabled { + if node.Remote() && !experiments.RemoteTaskfiles().Enabled { return nil, errors.New("task: Remote taskfiles are not enabled. You can read more about this experiment and how to enable it at https://taskfile.dev/experiments/remote-taskfiles") } return node, err