Skip to content

Nested include vars overridden by ancestor include vars #2600

@daeh

Description

@daeh

Nested include vars overridden by ancestor include vars

Description

This issue may be related to the Variables Megathread discussion (#2034) and the scoping proposal in #2035, but I want to report a specific behavior that appears to contradict the fix in PR #1256 ("fix: propagate include vars in multi-level includes").

I believe the behavior described here is an edge case not covered by #1256 or a regression of that PR, but apologies if this is the expected behavior or a duplicate issue.

Summary

When using nested includes (A → B → C), explicit vars passed in the immediate include (B → C) are overridden by vars from ancestor includes (A → B). This seems inconsistent with PR #1256's intent to properly propagate include vars in multi-level includes.

Minimal Reproduction

minimal_test/
├── Taskfile.yml        # includes level1 with VAR='root-value'
├── level1/
│   └── Taskfile.yml    # includes level2 with VAR='level1-value'
└── level2/
    └── Taskfile.yml    # expects VAR='level1-value', gets 'root-value'

Taskfile.yml (root)

version: '3'

includes:
  level1:
    taskfile: './level1/Taskfile.yml'
    dir: './level1'
    vars:
      VAR: 'root-value'

level1/Taskfile.yml

version: '3'

includes:
  level2:
    taskfile: '../level2/Taskfile.yml'
    dir: '../level2'
    vars:
      VAR: 'level1-value'  # Explicit override - IGNORED

tasks:
  show:
    cmds:
      - echo "VAR={{.VAR}}"

level2/Taskfile.yml

version: '3'

vars:
  # Global var computed from VAR at parse time
  DERIVED_VAR: '{{.VAR}}-derived'

tasks:
  show:
    cmds:
      - echo "VAR={{.VAR}}"
      - echo "DERIVED_VAR={{.DERIVED_VAR}}"

Observed Behavior

# Through nested includes - ancestor value wins
$ task level1:level2:show
VAR=root-value
DERIVED_VAR=root-value-derived

# Direct invocation - immediate include value works
$ task -t level1/Taskfile.yml level2:show
VAR=level1-value
DERIVED_VAR=level1-value-derived

The explicit VAR: 'level1-value' in level1's include of level2 is ignored when called through the root taskfile.

Expected Behavior

level2 should see VAR=level1-value and DERIVED_VAR=level1-value-derived because that's what level1/Taskfile.yml explicitly passes in its include statement. The direct invocation proves the configuration is correct.

Why This Seems Like a Bug

  1. PR fix: propagate include vars in multi-level includes #1256 addressed this scenario: The fix was specifically for "multi-level includes" where vars weren't propagating. The vars now propagate, but with incorrect precedence (ancestor wins over immediate parent).

  2. Explicit declarations ignored: Writing vars: { VAR: 'level1-value' } in an include is an explicit, intentional action. Having it silently overridden by an ancestor's include vars is unexpected.

  3. Behavior changes based on entry point: The same taskfile (level1) behaves differently depending on whether it's the entry point or included from a parent.

Difference From Other Issues

Issue Problem This Bug
#996 Vars completely nil/lost Vars propagate, but wrong precedence
#1800 Child vars override parent Opposite: ancestor overrides descendant
#2595 Race condition Deterministic, reproducible

Note on Task-Call-Time Vars

Passing vars at task-call time (rather than include time) does not fully resolve this issue:

# level1/Taskfile.yml - attempted workaround
tasks:
  call-level2:
    cmds:
      - task: level2:show
        vars:
          VAR: 'level1-value'

Result:

$ task level1:call-level2
VAR=level1-value           # Task-call var works
DERIVED_VAR=root-value-derived  # Global var still wrong

Task-call vars override VAR at runtime, but DERIVED_VAR was already computed at parse/include time using the ancestor's value. This demonstrates that the root issue is when include vars are resolved in the include chain, not just precedence at a single point in time.

Related

Version

3.46.3

Operating system

macOS 14.8.3

Experiments Enabled

No response

Example Taskfile

See above

Metadata

Metadata

Assignees

No one assigned

    Labels

    state: needs triageWaiting to be triaged by a maintainer.

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions