taskcluster-taskgraph 8.0.1__tar.gz → 8.2.0__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (138) hide show
  1. {taskcluster-taskgraph-8.0.1/src/taskcluster_taskgraph.egg-info → taskcluster-taskgraph-8.2.0}/PKG-INFO +1 -1
  2. {taskcluster-taskgraph-8.0.1 → taskcluster-taskgraph-8.2.0/src/taskcluster_taskgraph.egg-info}/PKG-INFO +1 -1
  3. {taskcluster-taskgraph-8.0.1 → taskcluster-taskgraph-8.2.0}/src/taskgraph/__init__.py +1 -1
  4. {taskcluster-taskgraph-8.0.1 → taskcluster-taskgraph-8.2.0}/src/taskgraph/decision.py +2 -0
  5. {taskcluster-taskgraph-8.0.1 → taskcluster-taskgraph-8.2.0}/src/taskgraph/docker.py +7 -5
  6. {taskcluster-taskgraph-8.0.1 → taskcluster-taskgraph-8.2.0}/src/taskgraph/main.py +3 -0
  7. {taskcluster-taskgraph-8.0.1 → taskcluster-taskgraph-8.2.0}/src/taskgraph/optimize/base.py +33 -1
  8. {taskcluster-taskgraph-8.0.1 → taskcluster-taskgraph-8.2.0}/src/taskgraph/optimize/strategies.py +22 -4
  9. {taskcluster-taskgraph-8.0.1 → taskcluster-taskgraph-8.2.0}/src/taskgraph/util/taskcluster.py +85 -0
  10. {taskcluster-taskgraph-8.0.1 → taskcluster-taskgraph-8.2.0}/test/test_optimize.py +16 -13
  11. {taskcluster-taskgraph-8.0.1 → taskcluster-taskgraph-8.2.0}/test/test_optimize_strategies.py +17 -1
  12. {taskcluster-taskgraph-8.0.1 → taskcluster-taskgraph-8.2.0}/LICENSE +0 -0
  13. {taskcluster-taskgraph-8.0.1 → taskcluster-taskgraph-8.2.0}/MANIFEST.in +0 -0
  14. {taskcluster-taskgraph-8.0.1 → taskcluster-taskgraph-8.2.0}/README.rst +0 -0
  15. {taskcluster-taskgraph-8.0.1 → taskcluster-taskgraph-8.2.0}/pyproject.toml +0 -0
  16. {taskcluster-taskgraph-8.0.1 → taskcluster-taskgraph-8.2.0}/requirements/base.in +0 -0
  17. {taskcluster-taskgraph-8.0.1 → taskcluster-taskgraph-8.2.0}/requirements/base.txt +0 -0
  18. {taskcluster-taskgraph-8.0.1 → taskcluster-taskgraph-8.2.0}/requirements/dev.in +0 -0
  19. {taskcluster-taskgraph-8.0.1 → taskcluster-taskgraph-8.2.0}/requirements/dev.txt +0 -0
  20. {taskcluster-taskgraph-8.0.1 → taskcluster-taskgraph-8.2.0}/requirements/test.in +0 -0
  21. {taskcluster-taskgraph-8.0.1 → taskcluster-taskgraph-8.2.0}/requirements/test.txt +0 -0
  22. {taskcluster-taskgraph-8.0.1 → taskcluster-taskgraph-8.2.0}/setup.cfg +0 -0
  23. {taskcluster-taskgraph-8.0.1 → taskcluster-taskgraph-8.2.0}/setup.py +0 -0
  24. {taskcluster-taskgraph-8.0.1 → taskcluster-taskgraph-8.2.0}/src/taskcluster_taskgraph.egg-info/SOURCES.txt +0 -0
  25. {taskcluster-taskgraph-8.0.1 → taskcluster-taskgraph-8.2.0}/src/taskcluster_taskgraph.egg-info/dependency_links.txt +0 -0
  26. {taskcluster-taskgraph-8.0.1 → taskcluster-taskgraph-8.2.0}/src/taskcluster_taskgraph.egg-info/entry_points.txt +0 -0
  27. {taskcluster-taskgraph-8.0.1 → taskcluster-taskgraph-8.2.0}/src/taskcluster_taskgraph.egg-info/requires.txt +0 -0
  28. {taskcluster-taskgraph-8.0.1 → taskcluster-taskgraph-8.2.0}/src/taskcluster_taskgraph.egg-info/top_level.txt +0 -0
  29. {taskcluster-taskgraph-8.0.1 → taskcluster-taskgraph-8.2.0}/src/taskgraph/actions/__init__.py +0 -0
  30. {taskcluster-taskgraph-8.0.1 → taskcluster-taskgraph-8.2.0}/src/taskgraph/actions/add_new_jobs.py +0 -0
  31. {taskcluster-taskgraph-8.0.1 → taskcluster-taskgraph-8.2.0}/src/taskgraph/actions/cancel.py +0 -0
  32. {taskcluster-taskgraph-8.0.1 → taskcluster-taskgraph-8.2.0}/src/taskgraph/actions/cancel_all.py +0 -0
  33. {taskcluster-taskgraph-8.0.1 → taskcluster-taskgraph-8.2.0}/src/taskgraph/actions/rebuild_cached_tasks.py +0 -0
  34. {taskcluster-taskgraph-8.0.1 → taskcluster-taskgraph-8.2.0}/src/taskgraph/actions/registry.py +0 -0
  35. {taskcluster-taskgraph-8.0.1 → taskcluster-taskgraph-8.2.0}/src/taskgraph/actions/retrigger.py +0 -0
  36. {taskcluster-taskgraph-8.0.1 → taskcluster-taskgraph-8.2.0}/src/taskgraph/actions/util.py +0 -0
  37. {taskcluster-taskgraph-8.0.1 → taskcluster-taskgraph-8.2.0}/src/taskgraph/config.py +0 -0
  38. {taskcluster-taskgraph-8.0.1 → taskcluster-taskgraph-8.2.0}/src/taskgraph/create.py +0 -0
  39. {taskcluster-taskgraph-8.0.1 → taskcluster-taskgraph-8.2.0}/src/taskgraph/filter_tasks.py +0 -0
  40. {taskcluster-taskgraph-8.0.1 → taskcluster-taskgraph-8.2.0}/src/taskgraph/generator.py +0 -0
  41. {taskcluster-taskgraph-8.0.1 → taskcluster-taskgraph-8.2.0}/src/taskgraph/graph.py +0 -0
  42. {taskcluster-taskgraph-8.0.1 → taskcluster-taskgraph-8.2.0}/src/taskgraph/loader/__init__.py +0 -0
  43. {taskcluster-taskgraph-8.0.1 → taskcluster-taskgraph-8.2.0}/src/taskgraph/loader/default.py +0 -0
  44. {taskcluster-taskgraph-8.0.1 → taskcluster-taskgraph-8.2.0}/src/taskgraph/loader/transform.py +0 -0
  45. {taskcluster-taskgraph-8.0.1 → taskcluster-taskgraph-8.2.0}/src/taskgraph/morph.py +0 -0
  46. {taskcluster-taskgraph-8.0.1 → taskcluster-taskgraph-8.2.0}/src/taskgraph/optimize/__init__.py +0 -0
  47. {taskcluster-taskgraph-8.0.1 → taskcluster-taskgraph-8.2.0}/src/taskgraph/parameters.py +0 -0
  48. {taskcluster-taskgraph-8.0.1 → taskcluster-taskgraph-8.2.0}/src/taskgraph/run-task/fetch-content +0 -0
  49. {taskcluster-taskgraph-8.0.1 → taskcluster-taskgraph-8.2.0}/src/taskgraph/run-task/hgrc +0 -0
  50. {taskcluster-taskgraph-8.0.1 → taskcluster-taskgraph-8.2.0}/src/taskgraph/run-task/robustcheckout.py +0 -0
  51. {taskcluster-taskgraph-8.0.1 → taskcluster-taskgraph-8.2.0}/src/taskgraph/run-task/run-task +0 -0
  52. {taskcluster-taskgraph-8.0.1 → taskcluster-taskgraph-8.2.0}/src/taskgraph/target_tasks.py +0 -0
  53. {taskcluster-taskgraph-8.0.1 → taskcluster-taskgraph-8.2.0}/src/taskgraph/task.py +0 -0
  54. {taskcluster-taskgraph-8.0.1 → taskcluster-taskgraph-8.2.0}/src/taskgraph/taskgraph.py +0 -0
  55. {taskcluster-taskgraph-8.0.1 → taskcluster-taskgraph-8.2.0}/src/taskgraph/transforms/__init__.py +0 -0
  56. {taskcluster-taskgraph-8.0.1 → taskcluster-taskgraph-8.2.0}/src/taskgraph/transforms/base.py +0 -0
  57. {taskcluster-taskgraph-8.0.1 → taskcluster-taskgraph-8.2.0}/src/taskgraph/transforms/cached_tasks.py +0 -0
  58. {taskcluster-taskgraph-8.0.1 → taskcluster-taskgraph-8.2.0}/src/taskgraph/transforms/chunking.py +0 -0
  59. {taskcluster-taskgraph-8.0.1 → taskcluster-taskgraph-8.2.0}/src/taskgraph/transforms/code_review.py +0 -0
  60. {taskcluster-taskgraph-8.0.1 → taskcluster-taskgraph-8.2.0}/src/taskgraph/transforms/docker_image.py +0 -0
  61. {taskcluster-taskgraph-8.0.1 → taskcluster-taskgraph-8.2.0}/src/taskgraph/transforms/fetch.py +0 -0
  62. {taskcluster-taskgraph-8.0.1 → taskcluster-taskgraph-8.2.0}/src/taskgraph/transforms/from_deps.py +0 -0
  63. {taskcluster-taskgraph-8.0.1 → taskcluster-taskgraph-8.2.0}/src/taskgraph/transforms/notify.py +0 -0
  64. {taskcluster-taskgraph-8.0.1 → taskcluster-taskgraph-8.2.0}/src/taskgraph/transforms/run/__init__.py +0 -0
  65. {taskcluster-taskgraph-8.0.1 → taskcluster-taskgraph-8.2.0}/src/taskgraph/transforms/run/common.py +0 -0
  66. {taskcluster-taskgraph-8.0.1 → taskcluster-taskgraph-8.2.0}/src/taskgraph/transforms/run/index_search.py +0 -0
  67. {taskcluster-taskgraph-8.0.1 → taskcluster-taskgraph-8.2.0}/src/taskgraph/transforms/run/run_task.py +0 -0
  68. {taskcluster-taskgraph-8.0.1 → taskcluster-taskgraph-8.2.0}/src/taskgraph/transforms/run/toolchain.py +0 -0
  69. {taskcluster-taskgraph-8.0.1 → taskcluster-taskgraph-8.2.0}/src/taskgraph/transforms/task.py +0 -0
  70. {taskcluster-taskgraph-8.0.1 → taskcluster-taskgraph-8.2.0}/src/taskgraph/transforms/task_context.py +0 -0
  71. {taskcluster-taskgraph-8.0.1 → taskcluster-taskgraph-8.2.0}/src/taskgraph/util/__init__.py +0 -0
  72. {taskcluster-taskgraph-8.0.1 → taskcluster-taskgraph-8.2.0}/src/taskgraph/util/archive.py +0 -0
  73. {taskcluster-taskgraph-8.0.1 → taskcluster-taskgraph-8.2.0}/src/taskgraph/util/attributes.py +0 -0
  74. {taskcluster-taskgraph-8.0.1 → taskcluster-taskgraph-8.2.0}/src/taskgraph/util/cached_tasks.py +0 -0
  75. {taskcluster-taskgraph-8.0.1 → taskcluster-taskgraph-8.2.0}/src/taskgraph/util/dependencies.py +0 -0
  76. {taskcluster-taskgraph-8.0.1 → taskcluster-taskgraph-8.2.0}/src/taskgraph/util/docker.py +0 -0
  77. {taskcluster-taskgraph-8.0.1 → taskcluster-taskgraph-8.2.0}/src/taskgraph/util/hash.py +0 -0
  78. {taskcluster-taskgraph-8.0.1 → taskcluster-taskgraph-8.2.0}/src/taskgraph/util/keyed_by.py +0 -0
  79. {taskcluster-taskgraph-8.0.1 → taskcluster-taskgraph-8.2.0}/src/taskgraph/util/memoize.py +0 -0
  80. {taskcluster-taskgraph-8.0.1 → taskcluster-taskgraph-8.2.0}/src/taskgraph/util/parameterization.py +0 -0
  81. {taskcluster-taskgraph-8.0.1 → taskcluster-taskgraph-8.2.0}/src/taskgraph/util/path.py +0 -0
  82. {taskcluster-taskgraph-8.0.1 → taskcluster-taskgraph-8.2.0}/src/taskgraph/util/python_path.py +0 -0
  83. {taskcluster-taskgraph-8.0.1 → taskcluster-taskgraph-8.2.0}/src/taskgraph/util/readonlydict.py +0 -0
  84. {taskcluster-taskgraph-8.0.1 → taskcluster-taskgraph-8.2.0}/src/taskgraph/util/schema.py +0 -0
  85. {taskcluster-taskgraph-8.0.1 → taskcluster-taskgraph-8.2.0}/src/taskgraph/util/set_name.py +0 -0
  86. {taskcluster-taskgraph-8.0.1 → taskcluster-taskgraph-8.2.0}/src/taskgraph/util/shell.py +0 -0
  87. {taskcluster-taskgraph-8.0.1 → taskcluster-taskgraph-8.2.0}/src/taskgraph/util/taskgraph.py +0 -0
  88. {taskcluster-taskgraph-8.0.1 → taskcluster-taskgraph-8.2.0}/src/taskgraph/util/templates.py +0 -0
  89. {taskcluster-taskgraph-8.0.1 → taskcluster-taskgraph-8.2.0}/src/taskgraph/util/time.py +0 -0
  90. {taskcluster-taskgraph-8.0.1 → taskcluster-taskgraph-8.2.0}/src/taskgraph/util/treeherder.py +0 -0
  91. {taskcluster-taskgraph-8.0.1 → taskcluster-taskgraph-8.2.0}/src/taskgraph/util/vcs.py +0 -0
  92. {taskcluster-taskgraph-8.0.1 → taskcluster-taskgraph-8.2.0}/src/taskgraph/util/verify.py +0 -0
  93. {taskcluster-taskgraph-8.0.1 → taskcluster-taskgraph-8.2.0}/src/taskgraph/util/workertypes.py +0 -0
  94. {taskcluster-taskgraph-8.0.1 → taskcluster-taskgraph-8.2.0}/src/taskgraph/util/yaml.py +0 -0
  95. {taskcluster-taskgraph-8.0.1 → taskcluster-taskgraph-8.2.0}/test/test_actions_rebuild_cached_tasks.py +0 -0
  96. {taskcluster-taskgraph-8.0.1 → taskcluster-taskgraph-8.2.0}/test/test_actions_registry.py +0 -0
  97. {taskcluster-taskgraph-8.0.1 → taskcluster-taskgraph-8.2.0}/test/test_create.py +0 -0
  98. {taskcluster-taskgraph-8.0.1 → taskcluster-taskgraph-8.2.0}/test/test_decision.py +0 -0
  99. {taskcluster-taskgraph-8.0.1 → taskcluster-taskgraph-8.2.0}/test/test_docker.py +0 -0
  100. {taskcluster-taskgraph-8.0.1 → taskcluster-taskgraph-8.2.0}/test/test_generator.py +0 -0
  101. {taskcluster-taskgraph-8.0.1 → taskcluster-taskgraph-8.2.0}/test/test_graph.py +0 -0
  102. {taskcluster-taskgraph-8.0.1 → taskcluster-taskgraph-8.2.0}/test/test_main.py +0 -0
  103. {taskcluster-taskgraph-8.0.1 → taskcluster-taskgraph-8.2.0}/test/test_morph.py +0 -0
  104. {taskcluster-taskgraph-8.0.1 → taskcluster-taskgraph-8.2.0}/test/test_parameters.py +0 -0
  105. {taskcluster-taskgraph-8.0.1 → taskcluster-taskgraph-8.2.0}/test/test_scripts_fetch_content.py +0 -0
  106. {taskcluster-taskgraph-8.0.1 → taskcluster-taskgraph-8.2.0}/test/test_scripts_run_task.py +0 -0
  107. {taskcluster-taskgraph-8.0.1 → taskcluster-taskgraph-8.2.0}/test/test_target_tasks.py +0 -0
  108. {taskcluster-taskgraph-8.0.1 → taskcluster-taskgraph-8.2.0}/test/test_taskgraph.py +0 -0
  109. {taskcluster-taskgraph-8.0.1 → taskcluster-taskgraph-8.2.0}/test/test_transform_chunking.py +0 -0
  110. {taskcluster-taskgraph-8.0.1 → taskcluster-taskgraph-8.2.0}/test/test_transform_docker_image.py +0 -0
  111. {taskcluster-taskgraph-8.0.1 → taskcluster-taskgraph-8.2.0}/test/test_transform_task_context.py +0 -0
  112. {taskcluster-taskgraph-8.0.1 → taskcluster-taskgraph-8.2.0}/test/test_transforms_base.py +0 -0
  113. {taskcluster-taskgraph-8.0.1 → taskcluster-taskgraph-8.2.0}/test/test_transforms_cached_tasks.py +0 -0
  114. {taskcluster-taskgraph-8.0.1 → taskcluster-taskgraph-8.2.0}/test/test_transforms_fetch.py +0 -0
  115. {taskcluster-taskgraph-8.0.1 → taskcluster-taskgraph-8.2.0}/test/test_transforms_from_deps.py +0 -0
  116. {taskcluster-taskgraph-8.0.1 → taskcluster-taskgraph-8.2.0}/test/test_transforms_notify.py +0 -0
  117. {taskcluster-taskgraph-8.0.1 → taskcluster-taskgraph-8.2.0}/test/test_transforms_run.py +0 -0
  118. {taskcluster-taskgraph-8.0.1 → taskcluster-taskgraph-8.2.0}/test/test_transforms_run_run_task.py +0 -0
  119. {taskcluster-taskgraph-8.0.1 → taskcluster-taskgraph-8.2.0}/test/test_transforms_run_toolchain.py +0 -0
  120. {taskcluster-taskgraph-8.0.1 → taskcluster-taskgraph-8.2.0}/test/test_transforms_task.py +0 -0
  121. {taskcluster-taskgraph-8.0.1 → taskcluster-taskgraph-8.2.0}/test/test_util_archive.py +0 -0
  122. {taskcluster-taskgraph-8.0.1 → taskcluster-taskgraph-8.2.0}/test/test_util_attributes.py +0 -0
  123. {taskcluster-taskgraph-8.0.1 → taskcluster-taskgraph-8.2.0}/test/test_util_cached_tasks.py +0 -0
  124. {taskcluster-taskgraph-8.0.1 → taskcluster-taskgraph-8.2.0}/test/test_util_dependencies.py +0 -0
  125. {taskcluster-taskgraph-8.0.1 → taskcluster-taskgraph-8.2.0}/test/test_util_docker.py +0 -0
  126. {taskcluster-taskgraph-8.0.1 → taskcluster-taskgraph-8.2.0}/test/test_util_parameterization.py +0 -0
  127. {taskcluster-taskgraph-8.0.1 → taskcluster-taskgraph-8.2.0}/test/test_util_path.py +0 -0
  128. {taskcluster-taskgraph-8.0.1 → taskcluster-taskgraph-8.2.0}/test/test_util_python_path.py +0 -0
  129. {taskcluster-taskgraph-8.0.1 → taskcluster-taskgraph-8.2.0}/test/test_util_readonlydict.py +0 -0
  130. {taskcluster-taskgraph-8.0.1 → taskcluster-taskgraph-8.2.0}/test/test_util_schema.py +0 -0
  131. {taskcluster-taskgraph-8.0.1 → taskcluster-taskgraph-8.2.0}/test/test_util_taskcluster.py +0 -0
  132. {taskcluster-taskgraph-8.0.1 → taskcluster-taskgraph-8.2.0}/test/test_util_templates.py +0 -0
  133. {taskcluster-taskgraph-8.0.1 → taskcluster-taskgraph-8.2.0}/test/test_util_time.py +0 -0
  134. {taskcluster-taskgraph-8.0.1 → taskcluster-taskgraph-8.2.0}/test/test_util_treeherder.py +0 -0
  135. {taskcluster-taskgraph-8.0.1 → taskcluster-taskgraph-8.2.0}/test/test_util_vcs.py +0 -0
  136. {taskcluster-taskgraph-8.0.1 → taskcluster-taskgraph-8.2.0}/test/test_util_verify.py +0 -0
  137. {taskcluster-taskgraph-8.0.1 → taskcluster-taskgraph-8.2.0}/test/test_util_workertypes.py +0 -0
  138. {taskcluster-taskgraph-8.0.1 → taskcluster-taskgraph-8.2.0}/test/test_util_yaml.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: taskcluster-taskgraph
3
- Version: 8.0.1
3
+ Version: 8.2.0
4
4
  Summary: Build taskcluster taskgraphs
5
5
  Home-page: https://github.com/taskcluster/taskgraph
6
6
  Classifier: Development Status :: 5 - Production/Stable
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: taskcluster-taskgraph
3
- Version: 8.0.1
3
+ Version: 8.2.0
4
4
  Summary: Build taskcluster taskgraphs
5
5
  Home-page: https://github.com/taskcluster/taskgraph
6
6
  Classifier: Development Status :: 5 - Production/Stable
@@ -2,7 +2,7 @@
2
2
  # License, v. 2.0. If a copy of the MPL was not distributed with this
3
3
  # file, You can obtain one at http://mozilla.org/MPL/2.0/.
4
4
 
5
- __version__ = "8.0.1"
5
+ __version__ = "8.2.0"
6
6
 
7
7
  # Maximum number of dependencies a single task can have
8
8
  # https://docs.taskcluster.net/reference/platform/taskcluster-queue/references/api#createTask
@@ -74,6 +74,8 @@ def taskgraph_decision(options, parameters=None):
74
74
  * generating a set of artifacts to memorialize the graph
75
75
  * calling TaskCluster APIs to create the graph
76
76
  """
77
+ if options.get("verbose"):
78
+ logging.root.setLevel(logging.DEBUG)
77
79
 
78
80
  parameters = parameters or (
79
81
  lambda graph_config: get_decision_parameters(graph_config, options)
@@ -16,7 +16,10 @@ except ImportError as e:
16
16
  zstd = e
17
17
 
18
18
  from taskgraph.util import docker
19
- from taskgraph.util.taskcluster import get_artifact_url, get_session
19
+ from taskgraph.util.taskcluster import (
20
+ get_artifact_url,
21
+ get_session,
22
+ )
20
23
 
21
24
  DEPLOY_WARNING = """
22
25
  *****************************************************************
@@ -59,10 +62,9 @@ def load_image_by_name(image_name, tag=None):
59
62
  )
60
63
  tasks = load_tasks_for_kind(params, "docker-image")
61
64
  task = tasks[f"build-docker-image-{image_name}"]
62
- deadline = None
63
- task_id = IndexSearch().should_replace_task(
64
- task, {}, deadline, task.optimization.get("index-search", [])
65
- )
65
+
66
+ indexes = task.optimization.get("index-search", [])
67
+ task_id = IndexSearch().should_replace_task(task, {}, None, indexes)
66
68
 
67
69
  if task_id in (True, False):
68
70
  print(
@@ -697,6 +697,9 @@ def image_digest(args):
697
697
  "--tasks-for", required=True, help="the tasks_for value used to generate this task"
698
698
  )
699
699
  @argument("--try-task-config-file", help="path to try task configuration file")
700
+ @argument(
701
+ "--verbose", "-v", action="store_true", help="include debug-level logging output"
702
+ )
700
703
  def decision(options):
701
704
  from taskgraph.decision import taskgraph_decision
702
705
 
@@ -22,6 +22,7 @@ from taskgraph.graph import Graph
22
22
  from taskgraph.taskgraph import TaskGraph
23
23
  from taskgraph.util.parameterization import resolve_task_references, resolve_timestamps
24
24
  from taskgraph.util.python_path import import_sibling_modules
25
+ from taskgraph.util.taskcluster import find_task_id_batched, status_task_batched
25
26
 
26
27
  logger = logging.getLogger(__name__)
27
28
  registry = {}
@@ -51,6 +52,9 @@ def optimize_task_graph(
51
52
  Perform task optimization, returning a taskgraph and a map from label to
52
53
  assigned taskId, including replacement tasks.
53
54
  """
55
+ # avoid circular import
56
+ from taskgraph.optimize.strategies import IndexSearch
57
+
54
58
  label_to_taskid = {}
55
59
  if not existing_tasks:
56
60
  existing_tasks = {}
@@ -70,6 +74,23 @@ def optimize_task_graph(
70
74
  do_not_optimize=do_not_optimize,
71
75
  )
72
76
 
77
+ # Gather each relevant task's index
78
+ indexes = set()
79
+ for label in target_task_graph.graph.visit_postorder():
80
+ if label in do_not_optimize:
81
+ continue
82
+ _, strategy, arg = optimizations(label)
83
+ if isinstance(strategy, IndexSearch) and arg is not None:
84
+ indexes.update(arg)
85
+
86
+ index_to_taskid = {}
87
+ taskid_to_status = {}
88
+ if indexes:
89
+ # Find their respective status using TC index/queue batch APIs
90
+ indexes = list(indexes)
91
+ index_to_taskid = find_task_id_batched(indexes)
92
+ taskid_to_status = status_task_batched(list(index_to_taskid.values()))
93
+
73
94
  replaced_tasks = replace_tasks(
74
95
  target_task_graph=target_task_graph,
75
96
  optimizations=optimizations,
@@ -78,6 +99,8 @@ def optimize_task_graph(
78
99
  label_to_taskid=label_to_taskid,
79
100
  existing_tasks=existing_tasks,
80
101
  removed_tasks=removed_tasks,
102
+ index_to_taskid=index_to_taskid,
103
+ taskid_to_status=taskid_to_status,
81
104
  )
82
105
 
83
106
  return (
@@ -259,12 +282,17 @@ def replace_tasks(
259
282
  label_to_taskid,
260
283
  removed_tasks,
261
284
  existing_tasks,
285
+ index_to_taskid,
286
+ taskid_to_status,
262
287
  ):
263
288
  """
264
289
  Implement the "Replacing Tasks" phase, returning a set of task labels of
265
290
  all replaced tasks. The replacement taskIds are added to label_to_taskid as
266
291
  a side-effect.
267
292
  """
293
+ # avoid circular import
294
+ from taskgraph.optimize.strategies import IndexSearch
295
+
268
296
  opt_counts = defaultdict(int)
269
297
  replaced = set()
270
298
  dependents_of = target_task_graph.graph.reverse_links_dict()
@@ -307,6 +335,10 @@ def replace_tasks(
307
335
  deadline = max(
308
336
  resolve_timestamps(now, task.task["deadline"]) for task in dependents
309
337
  )
338
+
339
+ if isinstance(opt, IndexSearch):
340
+ arg = arg, index_to_taskid, taskid_to_status
341
+
310
342
  repl = opt.should_replace_task(task, params, deadline, arg)
311
343
  if repl:
312
344
  if repl is True:
@@ -316,7 +348,7 @@ def replace_tasks(
316
348
  removed_tasks.add(label)
317
349
  else:
318
350
  logger.debug(
319
- f"replace_tasks: {label} replaced by optimization strategy"
351
+ f"replace_tasks: {label} replaced with {repl} by optimization strategy"
320
352
  )
321
353
  label_to_taskid[label] = repl
322
354
  replaced.add(label)
@@ -22,12 +22,30 @@ class IndexSearch(OptimizationStrategy):
22
22
 
23
23
  fmt = "%Y-%m-%dT%H:%M:%S.%fZ"
24
24
 
25
- def should_replace_task(self, task, params, deadline, index_paths):
25
+ def should_replace_task(self, task, params, deadline, arg):
26
26
  "Look for a task with one of the given index paths"
27
+ batched = False
28
+ # Appease static checker that doesn't understand that this is not needed
29
+ label_to_taskid = {}
30
+ taskid_to_status = {}
31
+
32
+ if isinstance(arg, tuple) and len(arg) == 3:
33
+ # allow for a batched call optimization instead of two queries
34
+ # per index path
35
+ index_paths, label_to_taskid, taskid_to_status = arg
36
+ batched = True
37
+ else:
38
+ index_paths = arg
39
+
27
40
  for index_path in index_paths:
28
41
  try:
29
- task_id = find_task_id(index_path)
30
- status = status_task(task_id)
42
+ if batched:
43
+ task_id = label_to_taskid[index_path]
44
+ status = taskid_to_status[task_id]
45
+ else:
46
+ # 404 is raised as `KeyError` also end up here
47
+ task_id = find_task_id(index_path)
48
+ status = status_task(task_id)
31
49
  # status can be `None` if we're in `testing` mode
32
50
  # (e.g. test-action-callback)
33
51
  if not status or status.get("state") in ("exception", "failed"):
@@ -40,7 +58,7 @@ class IndexSearch(OptimizationStrategy):
40
58
 
41
59
  return task_id
42
60
  except KeyError:
43
- # 404 will end up here and go on to the next index path
61
+ # go on to the next index path
44
62
  pass
45
63
 
46
64
  return False
@@ -193,6 +193,48 @@ def find_task_id(index_path, use_proxy=False):
193
193
  return response.json()["taskId"]
194
194
 
195
195
 
196
+ def find_task_id_batched(index_paths, use_proxy=False):
197
+ """Gets the task id of multiple tasks given their respective index.
198
+
199
+ Args:
200
+ index_paths (List[str]): A list of task indexes.
201
+ use_proxy (bool): Whether to use taskcluster-proxy (default: False)
202
+
203
+ Returns:
204
+ Dict[str, str]: A dictionary object mapping each valid index path
205
+ to its respective task id.
206
+
207
+ See the endpoint here:
208
+ https://docs.taskcluster.net/docs/reference/core/index/api#findTasksAtIndex
209
+ """
210
+ endpoint = liburls.api(get_root_url(use_proxy), "index", "v1", "tasks/indexes")
211
+ task_ids = {}
212
+ continuation_token = None
213
+
214
+ while True:
215
+ response = _do_request(
216
+ endpoint,
217
+ json={
218
+ "indexes": index_paths,
219
+ },
220
+ params={"continuationToken": continuation_token},
221
+ )
222
+
223
+ response_data = response.json()
224
+ if not response_data["tasks"]:
225
+ break
226
+ response_tasks = response_data["tasks"]
227
+ if (len(task_ids) + len(response_tasks)) > len(index_paths):
228
+ # Sanity check
229
+ raise ValueError("more task ids were returned than were asked for")
230
+ task_ids.update((t["namespace"], t["taskId"]) for t in response_tasks)
231
+
232
+ continuationToken = response_data.get("continuationToken")
233
+ if continuationToken is None:
234
+ break
235
+ return task_ids
236
+
237
+
196
238
  def get_artifact_from_index(index_path, artifact_path, use_proxy=False):
197
239
  full_path = index_path + "/artifacts/" + artifact_path
198
240
  response = _do_request(get_index_url(full_path, use_proxy))
@@ -271,6 +313,49 @@ def status_task(task_id, use_proxy=False):
271
313
  return status
272
314
 
273
315
 
316
+ def status_task_batched(task_ids, use_proxy=False):
317
+ """Gets the status of multiple tasks given task_ids.
318
+
319
+ In testing mode, just logs that it would have retrieved statuses.
320
+
321
+ Args:
322
+ task_id (List[str]): A list of task ids.
323
+ use_proxy (bool): Whether to use taskcluster-proxy (default: False)
324
+
325
+ Returns:
326
+ dict: A dictionary object as defined here:
327
+ https://docs.taskcluster.net/docs/reference/platform/queue/api#statuses
328
+ """
329
+ if testing:
330
+ logger.info(f"Would have gotten status for {len(task_ids)} tasks.")
331
+ return
332
+ endpoint = liburls.api(get_root_url(use_proxy), "queue", "v1", "tasks/status")
333
+ statuses = {}
334
+ continuation_token = None
335
+
336
+ while True:
337
+ response = _do_request(
338
+ endpoint,
339
+ json={
340
+ "taskIds": task_ids,
341
+ },
342
+ params={
343
+ "continuationToken": continuation_token,
344
+ },
345
+ )
346
+ response_data = response.json()
347
+ if not response_data["statuses"]:
348
+ break
349
+ response_tasks = response_data["statuses"]
350
+ if (len(statuses) + len(response_tasks)) > len(task_ids):
351
+ raise ValueError("more task statuses were returned than were asked for")
352
+ statuses.update((t["taskId"], t["status"]) for t in response_tasks)
353
+ continuationToken = response_data.get("continuationToken")
354
+ if continuationToken is None:
355
+ break
356
+ return statuses
357
+
358
+
274
359
  def state_task(task_id, use_proxy=False):
275
360
  """Gets the state of a task given a task_id.
276
361
 
@@ -269,7 +269,7 @@ def test_remove_tasks(monkeypatch, graph, kwargs, exp_removed):
269
269
 
270
270
 
271
271
  @pytest.mark.parametrize(
272
- "graph,kwargs,exp_replaced,exp_removed,exp_label_to_taskid",
272
+ "graph,kwargs,exp_replaced,exp_removed",
273
273
  (
274
274
  # A task cannot be replaced if it depends on one that was not replaced
275
275
  pytest.param(
@@ -277,11 +277,12 @@ def test_remove_tasks(monkeypatch, graph, kwargs, exp_removed):
277
277
  t1={"replace": "e1"},
278
278
  t3={"replace": "e3"},
279
279
  ),
280
- {},
280
+ {
281
+ "index_to_taskid": {"t1": "e1"},
282
+ },
281
283
  # expectations
282
284
  {"t1"},
283
285
  set(),
284
- {"t1": "e1"},
285
286
  id="blocked",
286
287
  ),
287
288
  # A task cannot be replaced if it should not be optimized
@@ -291,11 +292,13 @@ def test_remove_tasks(monkeypatch, graph, kwargs, exp_removed):
291
292
  t2={"replace": "xxx"}, # but do_not_optimize
292
293
  t3={"replace": "e3"},
293
294
  ),
294
- {"do_not_optimize": {"t2"}},
295
+ {
296
+ "do_not_optimize": {"t2"},
297
+ "index_to_taskid": {"t1": "e1"},
298
+ },
295
299
  # expectations
296
300
  {"t1"},
297
301
  set(),
298
- {"t1": "e1"},
299
302
  id="do_not_optimize",
300
303
  ),
301
304
  # No tasks are replaced when strategy is 'never'
@@ -305,7 +308,6 @@ def test_remove_tasks(monkeypatch, graph, kwargs, exp_removed):
305
308
  # expectations
306
309
  set(),
307
310
  set(),
308
- {},
309
311
  id="never",
310
312
  ),
311
313
  # All replaceable tasks are replaced when strategy is 'replace'
@@ -315,11 +317,12 @@ def test_remove_tasks(monkeypatch, graph, kwargs, exp_removed):
315
317
  t2={"replace": "e2"},
316
318
  t3={"replace": "e3"},
317
319
  ),
318
- {},
320
+ {
321
+ "index_to_taskid": {"t1": "e1", "t2": "e2", "t3": "e3"},
322
+ },
319
323
  # expectations
320
324
  {"t1", "t2", "t3"},
321
325
  set(),
322
- {"t1": "e1", "t2": "e2", "t3": "e3"},
323
326
  id="all",
324
327
  ),
325
328
  # A task can be replaced with nothing
@@ -329,11 +332,12 @@ def test_remove_tasks(monkeypatch, graph, kwargs, exp_removed):
329
332
  t2={"replace": True},
330
333
  t3={"replace": True},
331
334
  ),
332
- {},
335
+ {
336
+ "index_to_taskid": {"t1": "e1"},
337
+ },
333
338
  # expectations
334
339
  {"t1"},
335
340
  {"t2", "t3"},
336
- {"t1": "e1"},
337
341
  id="tasks_removed",
338
342
  ),
339
343
  # A task which expires before a dependents deadline is not a valid replacement.
@@ -353,7 +357,6 @@ def test_remove_tasks(monkeypatch, graph, kwargs, exp_removed):
353
357
  # expectations
354
358
  set(),
355
359
  set(),
356
- {},
357
360
  id="deadline",
358
361
  ),
359
362
  ),
@@ -363,7 +366,6 @@ def test_replace_tasks(
363
366
  kwargs,
364
367
  exp_replaced,
365
368
  exp_removed,
366
- exp_label_to_taskid,
367
369
  ):
368
370
  """Tests the `replace_tasks` function.
369
371
 
@@ -378,6 +380,8 @@ def test_replace_tasks(
378
380
  kwargs.setdefault("params", {})
379
381
  kwargs.setdefault("do_not_optimize", set())
380
382
  kwargs.setdefault("label_to_taskid", {})
383
+ kwargs.setdefault("index_to_taskid", {})
384
+ kwargs.setdefault("taskid_to_status", {})
381
385
  kwargs.setdefault("removed_tasks", set())
382
386
  kwargs.setdefault("existing_tasks", {})
383
387
 
@@ -388,7 +392,6 @@ def test_replace_tasks(
388
392
  )
389
393
  assert got_replaced == exp_replaced
390
394
  assert kwargs["removed_tasks"] == exp_removed
391
- assert kwargs["label_to_taskid"] == exp_label_to_taskid
392
395
 
393
396
 
394
397
  @pytest.mark.parametrize(
@@ -47,6 +47,7 @@ def params():
47
47
  def test_index_search(responses, params, state, expires, expected):
48
48
  taskid = "abc"
49
49
  index_path = "foo.bar.latest"
50
+
50
51
  responses.add(
51
52
  responses.GET,
52
53
  f"{os.environ['TASKCLUSTER_ROOT_URL']}/api/index/v1/task/{index_path}",
@@ -66,9 +67,24 @@ def test_index_search(responses, params, state, expires, expected):
66
67
  status=200,
67
68
  )
68
69
 
70
+ label_to_taskid = {index_path: taskid}
71
+ taskid_to_status = {
72
+ taskid: {
73
+ "state": state,
74
+ "expires": expires,
75
+ }
76
+ }
77
+
69
78
  opt = IndexSearch()
70
79
  deadline = "2021-06-07T19:03:20.482Z"
71
- assert opt.should_replace_task({}, params, deadline, (index_path,)) == expected
80
+ assert (
81
+ opt.should_replace_task(
82
+ {}, params, deadline, ([index_path], label_to_taskid, taskid_to_status)
83
+ )
84
+ == expected
85
+ )
86
+ # test the non-batched variant as well
87
+ assert opt.should_replace_task({}, params, deadline, [index_path]) == expected
72
88
 
73
89
 
74
90
  @pytest.mark.parametrize(