From 4cc7091c8bdf89769c5101681dbd70a8a142980b Mon Sep 17 00:00:00 2001 From: Andrew Halberstadt Date: Tue, 7 Apr 2026 11:20:46 -0400 Subject: [PATCH] fix: ensure `taskgraph full -J --tasks ` shows all dependencies Overwriting `task.dependencies` with the graph edges doesn't play nicely with `get_filtered_taskgraph` (e.g `--tasks `) because in this case the full graph has already been trimmed. The result is that when using `--tasks`, the dependencies displayed will be a subset of the real dependencies. This is very misleading and can easily lead one to false conclusions while debugging. Recently this caused me to waste an hour of my time. I traced this comment back through history and it's been like this since it was originally implemented. Likely the intent was that if we a have a subset of the graph, then we should modify the task definition to not display any dependencies that aren't part of it. But I disagree with this. I'd argue that `Task.dependencies` is a very important part of the task definition, and not at all relevant to the higher level DAG that the task happens to be a part of. I believe mutating the task definition just because we did some filtering is a bug. Testing the full graph from the Firefox repo, I can confirm this change results in identical graphs (when not using `--tasks`). --- src/taskgraph/taskgraph.py | 3 --- test/test_main.py | 28 +++++++++++++++++++++++++++- test/test_taskgraph.py | 5 ++--- 3 files changed, 29 insertions(+), 7 deletions(-) diff --git a/src/taskgraph/taskgraph.py b/src/taskgraph/taskgraph.py index 839fcb385..8a5c38ce8 100644 --- a/src/taskgraph/taskgraph.py +++ b/src/taskgraph/taskgraph.py @@ -44,13 +44,10 @@ def __iter__(self): def to_json(self): "Return a JSON-able object representing the task graph, as documented" - named_links_dict = self.graph.named_links_dict() # this dictionary may be keyed by label or by taskid, so let's just call it 'key' tasks = {} for key in self.graph.visit_postorder(): tasks[key] = self.tasks[key].to_json() - # overwrite dependencies with the information in the taskgraph's edges. - tasks[key]["dependencies"] = named_links_dict.get(key, {}) return tasks @classmethod diff --git a/test/test_main.py b/test/test_main.py index 7a2c9100c..674f4819e 100644 --- a/test/test_main.py +++ b/test/test_main.py @@ -165,6 +165,26 @@ def test_output_file(run_taskgraph, tmpdir): }, id="no-op", ), + pytest.param( + "^a", + None, + { + "a": { + "attributes": {"kind": "task"}, + "dependencies": {"dep": "b"}, + "description": "", + "kind": "task", + "label": "a", + "optimization": None, + "soft_dependencies": [], + "if_dependencies": [], + "task": { + "foo": {"bar": 1}, + }, + }, + }, + id="regex-a-only", + ), pytest.param( "^b", None, @@ -226,7 +246,13 @@ def test_output_file(run_taskgraph, tmpdir): ) def test_get_filtered_taskgraph(regex, exclude, expected): tasks = { - "a": Task(kind="task", label="a", attributes={}, task={"foo": {"bar": 1}}), + "a": Task( + kind="task", + label="a", + attributes={}, + dependencies={"dep": "b"}, + task={"foo": {"bar": 1}}, + ), "b": Task( kind="task", label="b", diff --git a/test/test_taskgraph.py b/test/test_taskgraph.py index 1e62ae390..d7966efd8 100644 --- a/test/test_taskgraph.py +++ b/test/test_taskgraph.py @@ -20,6 +20,7 @@ def test_taskgraph_to_json(self): label="a", attributes={"attr": "a-task"}, task={"taskdef": True}, + dependencies={"edgelabel": "b"}, ), "b": Task( kind="test", @@ -27,8 +28,6 @@ def test_taskgraph_to_json(self): attributes={}, task={"task": "def"}, optimization={"seta": None}, - # note that this dep is ignored, superseded by that - # from the taskgraph's edges dependencies={"first": "a"}, ), } @@ -56,7 +55,7 @@ def test_taskgraph_to_json(self): "label": "b", "attributes": {"kind": "test"}, "task": {"task": "def"}, - "dependencies": {}, + "dependencies": {"first": "a"}, "description": "", "soft_dependencies": [], "if_dependencies": [],