Skip to content

fix: honour the global run setting from an included Taskfile - #3005

Open
no-hup wants to merge 1 commit into
go-task:mainfrom
no-hup:fix-1738-included-global-run
Open

fix: honour the global run setting from an included Taskfile#3005
no-hup wants to merge 1 commit into
go-task:mainfrom
no-hup:fix-1738-included-global-run

Conversation

@no-hup

@no-hup no-hup commented Aug 29, 2026

Copy link
Copy Markdown
Contributor

Fixes #1738

The problem

If an included Taskfile sets run at the top level rather than per-task, the setting is dropped when it's included. Its tasks run once per call instead of once per run — no warning, no error. Running that same Taskfile directly works fine, which makes it a confusing one to track down.

Why it happens

Taskfile.Merge already pushes two of the included file's global settings down onto its tasks — silent and use_gitignore — but never looks at Run:

https://github.com/go-task/task/blob/main/taskfile/ast/taskfile.go#L63-L79

Downstream, GetHash falls back to the root Taskfile:

r := cmp.Or(t.Run, e.Taskfile.Run)

After graph.Merge(), e.Taskfile is the root file, so an included file's Run is unreachable from there.

The change

One block in Merge, mirroring the silent block directly above it: when the included Taskfile sets run, apply it to its own tasks that don't set run themselves. Tasks with an explicit run: are untouched.

@trulede proposed this same fix in #2548 and described the cause in the issue thread — that PR was closed without tests, so I've added the coverage it was missing.

Tests

New testdata/run_once_included/ fixture: a root Taskfile including a library Taskfile that sets run: once globally, with service-a and service-b both depending on library:build. The test sits next to TestRunOnceSharedDeps and follows the same style.

  • With the change: go test ./... → 817 passed across 33 packages; go vet ./... clean.
  • Without it, the new test fails as you'd expect — the library task runs twice:
[FAIL] TestRunOnceGlobalInIncludedTaskfile
    task_test.go:2397: Not equal:
    expected: 1
    actual  : 2

Behaviour change worth calling out

This does change behaviour for anyone currently affected by the bug, and I'd rather flag it than have it surface in review:

  1. An included Taskfile with a global run: once will now actually run its tasks once. If someone was calling such a task repeatedly with different vars and relying on it re-running, they'll now get one execution — run: once hashes on task name only, not vars.
  2. Where the root sets a global run and an included file sets a different one, the included file's value now wins for its own tasks. That's the same precedence silent and use_gitignore already have, so this makes run consistent with its siblings rather than introducing a new rule.

Both follow from honouring the setting at all, but if you'd prefer the root's global to keep winning, that's a one-line change and I'm happy to make it.


Disclosure: I used an AI assistant while writing and testing this change. I've reviewed it and verified the behaviour myself, and I'm happy to explain or revise anything here.

Taskfile.Merge already pushes the included file's global silent and
use_gitignore settings down onto its tasks, but never did the same for
run. Because GetHash falls back to the root Taskfile's run value, an
included Taskfile's top-level 'run: once' was silently ignored and its
tasks executed once per call instead of once per run.

Tasks that set run explicitly are unaffected.

Fixes go-task#1738
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Honour the global-level 'run: once' setting in an included Taskfile

1 participant