Proposal: Variable scope and inheritance
Variable/environment variable precedence is an area that has caused a lot of confusion. The ENV_PRECEDENCE experiment has mitigated some of this, but does not solve all of the issues.
One of the main causes of our problems is the merging that is done when parsing Taskfiles. In this process, we lose all information about the priority of variables and where they were set. This leads to a situation where variables are not scoped correctly and leak from a child Taskfile into a parent.
We also have small differences between how variables and environment variables inherit values. This proposal is intended to be paired with #2036 which merges the two types of variable together. The rest of this issue will assume that there is only one type of variable in Task.
I propose that variables should be set with the following precedence, starting with the lowest priority and finishing with the highest. Each item in the list inherits from the previous items and overrides any existing variables.
- Environment Scope
- Set in the host shell
- Set in the command line when calling task
- Entrypoint Scope
- Global
dotenvfiles in the entrypoint Taskfile - Global
varsin the entrypoint Taskfile
- Global
- Include Scope(s) (in the order in which they are included)
- Variables specified in the
includestatement - ~~Global
dotenvfiles in each included taskfile~~ (not currently supported) - Global
varsin each included taskfile
- Variables specified in the
- Task Scope
- Variables passed into a task via another task
- Task-level
dotenvfiles - Task-level
vars
Note the term "scope". Scopes are used to sandbox variables so that they don't leak into other tasks. Variables in a higher priority scope cannot be accessed from a scope of lower priority or a sibling scope. For example, a variable set in one task, cannot be accessed in another task because that would be a sibling scope. Likewise, it cannot be accessed in a parent Taskfile since this is a lower priority scope.
This kind of precedence and scoping can be achieved by utilizing the new Taskfile DAG and removing merging entirely. This allows us to traverse the graph when searching for a Task and resolve variables as we go. This also has the distinct advantage that we only need to resolve variables that are relevant to the task(s) being called, rather than resolving all variables in all Taskfiles.
Related issues:
- #1030
- #2046
The main point of this proposal and #2036 is to unify, simplify and iron out any unexpected behavior with variables and environment variables. If we assume that #2036 is accepted, then we need to decide how unified variables will behave as environment variables do not always behave the same way as the current variables.
You are right that the existing variables largely follow the inheritance model described here. However, they are not scoped at all and the piece of work to enable this is significant (stopping the merging of Taskfiles). This proposal tracks that work.
I think this is a good proposal and the right way to go. Scoping variables has been missing, and its absence often leads to confusion.