taskcluster-taskgraph 22.0.0__tar.gz → 23.1.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 (270) hide show
  1. {taskcluster_taskgraph-22.0.0 → taskcluster_taskgraph-23.1.0}/.pre-commit-config.yaml +2 -2
  2. {taskcluster_taskgraph-22.0.0 → taskcluster_taskgraph-23.1.0}/CHANGELOG.md +29 -0
  3. {taskcluster_taskgraph-22.0.0 → taskcluster_taskgraph-23.1.0}/PKG-INFO +1 -1
  4. taskcluster_taskgraph-23.1.0/docs/howto/define-schemas.rst +156 -0
  5. {taskcluster_taskgraph-22.0.0 → taskcluster_taskgraph-23.1.0}/docs/howto/index.rst +1 -0
  6. {taskcluster_taskgraph-22.0.0 → taskcluster_taskgraph-23.1.0}/docs/reference/migrations.rst +7 -0
  7. {taskcluster_taskgraph-22.0.0 → taskcluster_taskgraph-23.1.0}/pyproject.toml +1 -1
  8. taskcluster_taskgraph-23.1.0/renovate.json +6 -0
  9. {taskcluster_taskgraph-22.0.0 → taskcluster_taskgraph-23.1.0}/src/taskgraph/actions/registry.py +16 -1
  10. {taskcluster_taskgraph-22.0.0 → taskcluster_taskgraph-23.1.0}/src/taskgraph/actions/util.py +1 -1
  11. {taskcluster_taskgraph-22.0.0 → taskcluster_taskgraph-23.1.0}/src/taskgraph/config.py +3 -0
  12. {taskcluster_taskgraph-22.0.0 → taskcluster_taskgraph-23.1.0}/src/taskgraph/generator.py +14 -0
  13. {taskcluster_taskgraph-22.0.0 → taskcluster_taskgraph-23.1.0}/src/taskgraph/main.py +5 -0
  14. {taskcluster_taskgraph-22.0.0 → taskcluster_taskgraph-23.1.0}/src/taskgraph/parameters.py +95 -50
  15. {taskcluster_taskgraph-22.0.0 → taskcluster_taskgraph-23.1.0}/src/taskgraph/run-task/fetch-content +4 -3
  16. {taskcluster_taskgraph-22.0.0 → taskcluster_taskgraph-23.1.0}/src/taskgraph/taskgraph.py +3 -4
  17. {taskcluster_taskgraph-22.0.0 → taskcluster_taskgraph-23.1.0}/src/taskgraph/transforms/from_deps.py +40 -40
  18. {taskcluster_taskgraph-22.0.0 → taskcluster_taskgraph-23.1.0}/src/taskgraph/util/schema.py +96 -7
  19. {taskcluster_taskgraph-22.0.0 → taskcluster_taskgraph-23.1.0}/taskcluster/config.yml +1 -0
  20. {taskcluster_taskgraph-22.0.0 → taskcluster_taskgraph-23.1.0}/taskcluster/docker/index-task/package.json +1 -1
  21. {taskcluster_taskgraph-22.0.0 → taskcluster_taskgraph-23.1.0}/taskcluster/docker/index-task/yarn.lock +67 -74
  22. {taskcluster_taskgraph-22.0.0 → taskcluster_taskgraph-23.1.0}/taskcluster/kinds/test/linux.yml +20 -0
  23. {taskcluster_taskgraph-22.0.0 → taskcluster_taskgraph-23.1.0}/taskcluster/self_taskgraph/custom_parameters.py +9 -6
  24. {taskcluster_taskgraph-22.0.0 → taskcluster_taskgraph-23.1.0}/template/{{cookiecutter.project_name}}/taskcluster/config.yml +3 -3
  25. {taskcluster_taskgraph-22.0.0 → taskcluster_taskgraph-23.1.0}/test/conftest.py +18 -2
  26. {taskcluster_taskgraph-22.0.0 → taskcluster_taskgraph-23.1.0}/test/test_actions_registry.py +73 -0
  27. {taskcluster_taskgraph-22.0.0 → taskcluster_taskgraph-23.1.0}/test/test_generator.py +46 -3
  28. {taskcluster_taskgraph-22.0.0 → taskcluster_taskgraph-23.1.0}/test/test_main.py +67 -1
  29. {taskcluster_taskgraph-22.0.0 → taskcluster_taskgraph-23.1.0}/test/test_parameters.py +8 -13
  30. {taskcluster_taskgraph-22.0.0 → taskcluster_taskgraph-23.1.0}/test/test_scripts_fetch_content.py +80 -0
  31. {taskcluster_taskgraph-22.0.0 → taskcluster_taskgraph-23.1.0}/test/test_taskgraph.py +38 -3
  32. {taskcluster_taskgraph-22.0.0 → taskcluster_taskgraph-23.1.0}/test/test_util_schema.py +91 -5
  33. {taskcluster_taskgraph-22.0.0 → taskcluster_taskgraph-23.1.0}/uv.lock +1283 -608
  34. taskcluster_taskgraph-22.0.0/.github/dependabot.yml +0 -19
  35. {taskcluster_taskgraph-22.0.0 → taskcluster_taskgraph-23.1.0}/.codespell-ignore-words.txt +0 -0
  36. {taskcluster_taskgraph-22.0.0 → taskcluster_taskgraph-23.1.0}/.github/CODEOWNERS +0 -0
  37. {taskcluster_taskgraph-22.0.0 → taskcluster_taskgraph-23.1.0}/.github/workflows/codeql-analysis.yml +0 -0
  38. {taskcluster_taskgraph-22.0.0 → taskcluster_taskgraph-23.1.0}/.github/workflows/pre-commit-autoupdate.yml +0 -0
  39. {taskcluster_taskgraph-22.0.0 → taskcluster_taskgraph-23.1.0}/.github/workflows/pre-commit.yml +0 -0
  40. {taskcluster_taskgraph-22.0.0 → taskcluster_taskgraph-23.1.0}/.github/workflows/pypi-publish.yml +0 -0
  41. {taskcluster_taskgraph-22.0.0 → taskcluster_taskgraph-23.1.0}/.gitignore +0 -0
  42. {taskcluster_taskgraph-22.0.0 → taskcluster_taskgraph-23.1.0}/.readthedocs.yaml +0 -0
  43. {taskcluster_taskgraph-22.0.0 → taskcluster_taskgraph-23.1.0}/.taskcluster.yml +0 -0
  44. {taskcluster_taskgraph-22.0.0 → taskcluster_taskgraph-23.1.0}/.yamllint +0 -0
  45. {taskcluster_taskgraph-22.0.0 → taskcluster_taskgraph-23.1.0}/CODE_OF_CONDUCT.md +0 -0
  46. {taskcluster_taskgraph-22.0.0 → taskcluster_taskgraph-23.1.0}/CONTRIBUTING.rst +0 -0
  47. {taskcluster_taskgraph-22.0.0 → taskcluster_taskgraph-23.1.0}/LICENSE +0 -0
  48. {taskcluster_taskgraph-22.0.0 → taskcluster_taskgraph-23.1.0}/Makefile +0 -0
  49. {taskcluster_taskgraph-22.0.0 → taskcluster_taskgraph-23.1.0}/README.rst +0 -0
  50. {taskcluster_taskgraph-22.0.0 → taskcluster_taskgraph-23.1.0}/docs/concepts/index.rst +0 -0
  51. {taskcluster_taskgraph-22.0.0 → taskcluster_taskgraph-23.1.0}/docs/concepts/kind.rst +0 -0
  52. {taskcluster_taskgraph-22.0.0 → taskcluster_taskgraph-23.1.0}/docs/concepts/loading.rst +0 -0
  53. {taskcluster_taskgraph-22.0.0 → taskcluster_taskgraph-23.1.0}/docs/concepts/optimization.rst +0 -0
  54. {taskcluster_taskgraph-22.0.0 → taskcluster_taskgraph-23.1.0}/docs/concepts/overview.rst +0 -0
  55. {taskcluster_taskgraph-22.0.0 → taskcluster_taskgraph-23.1.0}/docs/concepts/scopes.rst +0 -0
  56. {taskcluster_taskgraph-22.0.0 → taskcluster_taskgraph-23.1.0}/docs/concepts/task-graphs.rst +0 -0
  57. {taskcluster_taskgraph-22.0.0 → taskcluster_taskgraph-23.1.0}/docs/concepts/transforms.rst +0 -0
  58. {taskcluster_taskgraph-22.0.0 → taskcluster_taskgraph-23.1.0}/docs/conf.py +0 -0
  59. {taskcluster_taskgraph-22.0.0 → taskcluster_taskgraph-23.1.0}/docs/contributing.rst +0 -0
  60. {taskcluster_taskgraph-22.0.0 → taskcluster_taskgraph-23.1.0}/docs/glossary.rst +0 -0
  61. {taskcluster_taskgraph-22.0.0 → taskcluster_taskgraph-23.1.0}/docs/howto/bootstrap-taskgraph.rst +0 -0
  62. {taskcluster_taskgraph-22.0.0 → taskcluster_taskgraph-23.1.0}/docs/howto/create-actions.rst +0 -0
  63. {taskcluster_taskgraph-22.0.0 → taskcluster_taskgraph-23.1.0}/docs/howto/create-tasks.rst +0 -0
  64. {taskcluster_taskgraph-22.0.0 → taskcluster_taskgraph-23.1.0}/docs/howto/debugging.rst +0 -0
  65. {taskcluster_taskgraph-22.0.0 → taskcluster_taskgraph-23.1.0}/docs/howto/docker.rst +0 -0
  66. {taskcluster_taskgraph-22.0.0 → taskcluster_taskgraph-23.1.0}/docs/howto/load-task-locally.rst +0 -0
  67. {taskcluster_taskgraph-22.0.0 → taskcluster_taskgraph-23.1.0}/docs/howto/resolve-keyed-by.rst +0 -0
  68. {taskcluster_taskgraph-22.0.0 → taskcluster_taskgraph-23.1.0}/docs/howto/run-locally.rst +0 -0
  69. {taskcluster_taskgraph-22.0.0 → taskcluster_taskgraph-23.1.0}/docs/howto/send-notifications.rst +0 -0
  70. {taskcluster_taskgraph-22.0.0 → taskcluster_taskgraph-23.1.0}/docs/howto/use-fetches.rst +0 -0
  71. {taskcluster_taskgraph-22.0.0 → taskcluster_taskgraph-23.1.0}/docs/index.rst +0 -0
  72. {taskcluster_taskgraph-22.0.0 → taskcluster_taskgraph-23.1.0}/docs/reference/cli.rst +0 -0
  73. {taskcluster_taskgraph-22.0.0 → taskcluster_taskgraph-23.1.0}/docs/reference/index.rst +0 -0
  74. {taskcluster_taskgraph-22.0.0 → taskcluster_taskgraph-23.1.0}/docs/reference/optimization-strategies.rst +0 -0
  75. {taskcluster_taskgraph-22.0.0 → taskcluster_taskgraph-23.1.0}/docs/reference/parameters.rst +0 -0
  76. {taskcluster_taskgraph-22.0.0 → taskcluster_taskgraph-23.1.0}/docs/reference/source/modules.rst +0 -0
  77. {taskcluster_taskgraph-22.0.0 → taskcluster_taskgraph-23.1.0}/docs/reference/source/taskgraph.actions.rst +0 -0
  78. {taskcluster_taskgraph-22.0.0 → taskcluster_taskgraph-23.1.0}/docs/reference/source/taskgraph.loader.rst +0 -0
  79. {taskcluster_taskgraph-22.0.0 → taskcluster_taskgraph-23.1.0}/docs/reference/source/taskgraph.optimize.rst +0 -0
  80. {taskcluster_taskgraph-22.0.0 → taskcluster_taskgraph-23.1.0}/docs/reference/source/taskgraph.rst +0 -0
  81. {taskcluster_taskgraph-22.0.0 → taskcluster_taskgraph-23.1.0}/docs/reference/source/taskgraph.transforms.rst +0 -0
  82. {taskcluster_taskgraph-22.0.0 → taskcluster_taskgraph-23.1.0}/docs/reference/source/taskgraph.transforms.run.rst +0 -0
  83. {taskcluster_taskgraph-22.0.0 → taskcluster_taskgraph-23.1.0}/docs/reference/source/taskgraph.util.rst +0 -0
  84. {taskcluster_taskgraph-22.0.0 → taskcluster_taskgraph-23.1.0}/docs/reference/transforms/chunking.rst +0 -0
  85. {taskcluster_taskgraph-22.0.0 → taskcluster_taskgraph-23.1.0}/docs/reference/transforms/from_deps.rst +0 -0
  86. {taskcluster_taskgraph-22.0.0 → taskcluster_taskgraph-23.1.0}/docs/reference/transforms/index.rst +0 -0
  87. {taskcluster_taskgraph-22.0.0 → taskcluster_taskgraph-23.1.0}/docs/reference/transforms/matrix.rst +0 -0
  88. {taskcluster_taskgraph-22.0.0 → taskcluster_taskgraph-23.1.0}/docs/reference/transforms/task_context.rst +0 -0
  89. {taskcluster_taskgraph-22.0.0 → taskcluster_taskgraph-23.1.0}/docs/tutorials/connecting-taskcluster.rst +0 -0
  90. {taskcluster_taskgraph-22.0.0 → taskcluster_taskgraph-23.1.0}/docs/tutorials/creating-a-task-graph.rst +0 -0
  91. {taskcluster_taskgraph-22.0.0 → taskcluster_taskgraph-23.1.0}/docs/tutorials/example-taskcluster.yml +0 -0
  92. {taskcluster_taskgraph-22.0.0 → taskcluster_taskgraph-23.1.0}/docs/tutorials/getting-started.rst +0 -0
  93. {taskcluster_taskgraph-22.0.0 → taskcluster_taskgraph-23.1.0}/docs/tutorials/index.rst +0 -0
  94. {taskcluster_taskgraph-22.0.0 → taskcluster_taskgraph-23.1.0}/make.bat +0 -0
  95. {taskcluster_taskgraph-22.0.0 → taskcluster_taskgraph-23.1.0}/packages/pytest-taskgraph/README.md +0 -0
  96. {taskcluster_taskgraph-22.0.0 → taskcluster_taskgraph-23.1.0}/packages/pytest-taskgraph/pyproject.toml +0 -0
  97. {taskcluster_taskgraph-22.0.0 → taskcluster_taskgraph-23.1.0}/packages/pytest-taskgraph/src/pytest_taskgraph/__init__.py +0 -0
  98. {taskcluster_taskgraph-22.0.0 → taskcluster_taskgraph-23.1.0}/packages/pytest-taskgraph/src/pytest_taskgraph/fixtures/gen.py +0 -0
  99. {taskcluster_taskgraph-22.0.0 → taskcluster_taskgraph-23.1.0}/packages/pytest-taskgraph/src/pytest_taskgraph/fixtures/vcs.py +0 -0
  100. {taskcluster_taskgraph-22.0.0 → taskcluster_taskgraph-23.1.0}/packages/sphinx-taskgraph/README.md +0 -0
  101. {taskcluster_taskgraph-22.0.0 → taskcluster_taskgraph-23.1.0}/packages/sphinx-taskgraph/pyproject.toml +0 -0
  102. {taskcluster_taskgraph-22.0.0 → taskcluster_taskgraph-23.1.0}/packages/sphinx-taskgraph/src/sphinx_taskgraph/__init__.py +0 -0
  103. {taskcluster_taskgraph-22.0.0 → taskcluster_taskgraph-23.1.0}/packages/sphinx-taskgraph/src/sphinx_taskgraph/autoschema.py +0 -0
  104. {taskcluster_taskgraph-22.0.0 → taskcluster_taskgraph-23.1.0}/src/taskgraph/__init__.py +0 -0
  105. {taskcluster_taskgraph-22.0.0 → taskcluster_taskgraph-23.1.0}/src/taskgraph/actions/__init__.py +0 -0
  106. {taskcluster_taskgraph-22.0.0 → taskcluster_taskgraph-23.1.0}/src/taskgraph/actions/add_new_jobs.py +0 -0
  107. {taskcluster_taskgraph-22.0.0 → taskcluster_taskgraph-23.1.0}/src/taskgraph/actions/cancel.py +0 -0
  108. {taskcluster_taskgraph-22.0.0 → taskcluster_taskgraph-23.1.0}/src/taskgraph/actions/cancel_all.py +0 -0
  109. {taskcluster_taskgraph-22.0.0 → taskcluster_taskgraph-23.1.0}/src/taskgraph/actions/rebuild_cached_tasks.py +0 -0
  110. {taskcluster_taskgraph-22.0.0 → taskcluster_taskgraph-23.1.0}/src/taskgraph/actions/retrigger.py +0 -0
  111. {taskcluster_taskgraph-22.0.0 → taskcluster_taskgraph-23.1.0}/src/taskgraph/create.py +0 -0
  112. {taskcluster_taskgraph-22.0.0 → taskcluster_taskgraph-23.1.0}/src/taskgraph/decision.py +0 -0
  113. {taskcluster_taskgraph-22.0.0 → taskcluster_taskgraph-23.1.0}/src/taskgraph/docker.py +0 -0
  114. {taskcluster_taskgraph-22.0.0 → taskcluster_taskgraph-23.1.0}/src/taskgraph/filter_tasks.py +0 -0
  115. {taskcluster_taskgraph-22.0.0 → taskcluster_taskgraph-23.1.0}/src/taskgraph/graph.py +0 -0
  116. {taskcluster_taskgraph-22.0.0 → taskcluster_taskgraph-23.1.0}/src/taskgraph/loader/__init__.py +0 -0
  117. {taskcluster_taskgraph-22.0.0 → taskcluster_taskgraph-23.1.0}/src/taskgraph/loader/default.py +0 -0
  118. {taskcluster_taskgraph-22.0.0 → taskcluster_taskgraph-23.1.0}/src/taskgraph/loader/transform.py +0 -0
  119. {taskcluster_taskgraph-22.0.0 → taskcluster_taskgraph-23.1.0}/src/taskgraph/morph.py +0 -0
  120. {taskcluster_taskgraph-22.0.0 → taskcluster_taskgraph-23.1.0}/src/taskgraph/optimize/__init__.py +0 -0
  121. {taskcluster_taskgraph-22.0.0 → taskcluster_taskgraph-23.1.0}/src/taskgraph/optimize/base.py +0 -0
  122. {taskcluster_taskgraph-22.0.0 → taskcluster_taskgraph-23.1.0}/src/taskgraph/optimize/strategies.py +0 -0
  123. {taskcluster_taskgraph-22.0.0 → taskcluster_taskgraph-23.1.0}/src/taskgraph/run-task/hgrc +0 -0
  124. {taskcluster_taskgraph-22.0.0 → taskcluster_taskgraph-23.1.0}/src/taskgraph/run-task/robustcheckout.py +0 -0
  125. {taskcluster_taskgraph-22.0.0 → taskcluster_taskgraph-23.1.0}/src/taskgraph/run-task/run-task +0 -0
  126. {taskcluster_taskgraph-22.0.0 → taskcluster_taskgraph-23.1.0}/src/taskgraph/target_tasks.py +0 -0
  127. {taskcluster_taskgraph-22.0.0 → taskcluster_taskgraph-23.1.0}/src/taskgraph/task.py +0 -0
  128. {taskcluster_taskgraph-22.0.0 → taskcluster_taskgraph-23.1.0}/src/taskgraph/transforms/__init__.py +0 -0
  129. {taskcluster_taskgraph-22.0.0 → taskcluster_taskgraph-23.1.0}/src/taskgraph/transforms/base.py +0 -0
  130. {taskcluster_taskgraph-22.0.0 → taskcluster_taskgraph-23.1.0}/src/taskgraph/transforms/cached_tasks.py +0 -0
  131. {taskcluster_taskgraph-22.0.0 → taskcluster_taskgraph-23.1.0}/src/taskgraph/transforms/chunking.py +0 -0
  132. {taskcluster_taskgraph-22.0.0 → taskcluster_taskgraph-23.1.0}/src/taskgraph/transforms/code_review.py +0 -0
  133. {taskcluster_taskgraph-22.0.0 → taskcluster_taskgraph-23.1.0}/src/taskgraph/transforms/docker_image.py +0 -0
  134. {taskcluster_taskgraph-22.0.0 → taskcluster_taskgraph-23.1.0}/src/taskgraph/transforms/fetch.py +0 -0
  135. {taskcluster_taskgraph-22.0.0 → taskcluster_taskgraph-23.1.0}/src/taskgraph/transforms/matrix.py +0 -0
  136. {taskcluster_taskgraph-22.0.0 → taskcluster_taskgraph-23.1.0}/src/taskgraph/transforms/notify.py +0 -0
  137. {taskcluster_taskgraph-22.0.0 → taskcluster_taskgraph-23.1.0}/src/taskgraph/transforms/run/__init__.py +0 -0
  138. {taskcluster_taskgraph-22.0.0 → taskcluster_taskgraph-23.1.0}/src/taskgraph/transforms/run/common.py +0 -0
  139. {taskcluster_taskgraph-22.0.0 → taskcluster_taskgraph-23.1.0}/src/taskgraph/transforms/run/index_search.py +0 -0
  140. {taskcluster_taskgraph-22.0.0 → taskcluster_taskgraph-23.1.0}/src/taskgraph/transforms/run/run_task.py +0 -0
  141. {taskcluster_taskgraph-22.0.0 → taskcluster_taskgraph-23.1.0}/src/taskgraph/transforms/run/toolchain.py +0 -0
  142. {taskcluster_taskgraph-22.0.0 → taskcluster_taskgraph-23.1.0}/src/taskgraph/transforms/task.py +0 -0
  143. {taskcluster_taskgraph-22.0.0 → taskcluster_taskgraph-23.1.0}/src/taskgraph/transforms/task_context.py +0 -0
  144. {taskcluster_taskgraph-22.0.0 → taskcluster_taskgraph-23.1.0}/src/taskgraph/util/__init__.py +0 -0
  145. {taskcluster_taskgraph-22.0.0 → taskcluster_taskgraph-23.1.0}/src/taskgraph/util/archive.py +0 -0
  146. {taskcluster_taskgraph-22.0.0 → taskcluster_taskgraph-23.1.0}/src/taskgraph/util/attributes.py +0 -0
  147. {taskcluster_taskgraph-22.0.0 → taskcluster_taskgraph-23.1.0}/src/taskgraph/util/cached_tasks.py +0 -0
  148. {taskcluster_taskgraph-22.0.0 → taskcluster_taskgraph-23.1.0}/src/taskgraph/util/caches.py +0 -0
  149. {taskcluster_taskgraph-22.0.0 → taskcluster_taskgraph-23.1.0}/src/taskgraph/util/copy.py +0 -0
  150. {taskcluster_taskgraph-22.0.0 → taskcluster_taskgraph-23.1.0}/src/taskgraph/util/dependencies.py +0 -0
  151. {taskcluster_taskgraph-22.0.0 → taskcluster_taskgraph-23.1.0}/src/taskgraph/util/docker.py +0 -0
  152. {taskcluster_taskgraph-22.0.0 → taskcluster_taskgraph-23.1.0}/src/taskgraph/util/hash.py +0 -0
  153. {taskcluster_taskgraph-22.0.0 → taskcluster_taskgraph-23.1.0}/src/taskgraph/util/json.py +0 -0
  154. {taskcluster_taskgraph-22.0.0 → taskcluster_taskgraph-23.1.0}/src/taskgraph/util/keyed_by.py +0 -0
  155. {taskcluster_taskgraph-22.0.0 → taskcluster_taskgraph-23.1.0}/src/taskgraph/util/parameterization.py +0 -0
  156. {taskcluster_taskgraph-22.0.0 → taskcluster_taskgraph-23.1.0}/src/taskgraph/util/path.py +0 -0
  157. {taskcluster_taskgraph-22.0.0 → taskcluster_taskgraph-23.1.0}/src/taskgraph/util/python_path.py +0 -0
  158. {taskcluster_taskgraph-22.0.0 → taskcluster_taskgraph-23.1.0}/src/taskgraph/util/readonlydict.py +0 -0
  159. {taskcluster_taskgraph-22.0.0 → taskcluster_taskgraph-23.1.0}/src/taskgraph/util/set_name.py +0 -0
  160. {taskcluster_taskgraph-22.0.0 → taskcluster_taskgraph-23.1.0}/src/taskgraph/util/shell.py +0 -0
  161. {taskcluster_taskgraph-22.0.0 → taskcluster_taskgraph-23.1.0}/src/taskgraph/util/taskcluster.py +0 -0
  162. {taskcluster_taskgraph-22.0.0 → taskcluster_taskgraph-23.1.0}/src/taskgraph/util/taskgraph.py +0 -0
  163. {taskcluster_taskgraph-22.0.0 → taskcluster_taskgraph-23.1.0}/src/taskgraph/util/templates.py +0 -0
  164. {taskcluster_taskgraph-22.0.0 → taskcluster_taskgraph-23.1.0}/src/taskgraph/util/time.py +0 -0
  165. {taskcluster_taskgraph-22.0.0 → taskcluster_taskgraph-23.1.0}/src/taskgraph/util/treeherder.py +0 -0
  166. {taskcluster_taskgraph-22.0.0 → taskcluster_taskgraph-23.1.0}/src/taskgraph/util/vcs.py +0 -0
  167. {taskcluster_taskgraph-22.0.0 → taskcluster_taskgraph-23.1.0}/src/taskgraph/util/verify.py +0 -0
  168. {taskcluster_taskgraph-22.0.0 → taskcluster_taskgraph-23.1.0}/src/taskgraph/util/workertypes.py +0 -0
  169. {taskcluster_taskgraph-22.0.0 → taskcluster_taskgraph-23.1.0}/src/taskgraph/util/yaml.py +0 -0
  170. {taskcluster_taskgraph-22.0.0 → taskcluster_taskgraph-23.1.0}/taskcluster/docker/REGISTRY +0 -0
  171. {taskcluster_taskgraph-22.0.0 → taskcluster_taskgraph-23.1.0}/taskcluster/docker/decision/Dockerfile +0 -0
  172. {taskcluster_taskgraph-22.0.0 → taskcluster_taskgraph-23.1.0}/taskcluster/docker/decision/HASH +0 -0
  173. {taskcluster_taskgraph-22.0.0 → taskcluster_taskgraph-23.1.0}/taskcluster/docker/decision/README.md +0 -0
  174. {taskcluster_taskgraph-22.0.0 → taskcluster_taskgraph-23.1.0}/taskcluster/docker/decision/VERSION +0 -0
  175. {taskcluster_taskgraph-22.0.0 → taskcluster_taskgraph-23.1.0}/taskcluster/docker/decision/system-setup.sh +0 -0
  176. {taskcluster_taskgraph-22.0.0 → taskcluster_taskgraph-23.1.0}/taskcluster/docker/fetch/Dockerfile +0 -0
  177. {taskcluster_taskgraph-22.0.0 → taskcluster_taskgraph-23.1.0}/taskcluster/docker/image_builder/README.rst +0 -0
  178. {taskcluster_taskgraph-22.0.0 → taskcluster_taskgraph-23.1.0}/taskcluster/docker/index-task/Dockerfile +0 -0
  179. {taskcluster_taskgraph-22.0.0 → taskcluster_taskgraph-23.1.0}/taskcluster/docker/index-task/README +0 -0
  180. {taskcluster_taskgraph-22.0.0 → taskcluster_taskgraph-23.1.0}/taskcluster/docker/index-task/insert-indexes.js +0 -0
  181. {taskcluster_taskgraph-22.0.0 → taskcluster_taskgraph-23.1.0}/taskcluster/docker/python/Dockerfile +0 -0
  182. {taskcluster_taskgraph-22.0.0 → taskcluster_taskgraph-23.1.0}/taskcluster/docker/run-task/Dockerfile +0 -0
  183. {taskcluster_taskgraph-22.0.0 → taskcluster_taskgraph-23.1.0}/taskcluster/docker/run-task/system-setup.sh +0 -0
  184. {taskcluster_taskgraph-22.0.0 → taskcluster_taskgraph-23.1.0}/taskcluster/docker/skopeo/Dockerfile +0 -0
  185. {taskcluster_taskgraph-22.0.0 → taskcluster_taskgraph-23.1.0}/taskcluster/docker/skopeo/policy.json +0 -0
  186. {taskcluster_taskgraph-22.0.0 → taskcluster_taskgraph-23.1.0}/taskcluster/docker/skopeo/push_image.sh +0 -0
  187. {taskcluster_taskgraph-22.0.0 → taskcluster_taskgraph-23.1.0}/taskcluster/kinds/check/kind.yml +0 -0
  188. {taskcluster_taskgraph-22.0.0 → taskcluster_taskgraph-23.1.0}/taskcluster/kinds/codecov/kind.yml +0 -0
  189. {taskcluster_taskgraph-22.0.0 → taskcluster_taskgraph-23.1.0}/taskcluster/kinds/complete/kind.yml +0 -0
  190. {taskcluster_taskgraph-22.0.0 → taskcluster_taskgraph-23.1.0}/taskcluster/kinds/doc/kind.yml +0 -0
  191. {taskcluster_taskgraph-22.0.0 → taskcluster_taskgraph-23.1.0}/taskcluster/kinds/docker-image/kind.yml +0 -0
  192. {taskcluster_taskgraph-22.0.0 → taskcluster_taskgraph-23.1.0}/taskcluster/kinds/fetch/kind.yml +0 -0
  193. {taskcluster_taskgraph-22.0.0 → taskcluster_taskgraph-23.1.0}/taskcluster/kinds/push-image/kind.yml +0 -0
  194. {taskcluster_taskgraph-22.0.0 → taskcluster_taskgraph-23.1.0}/taskcluster/kinds/test/kind.yml +0 -0
  195. {taskcluster_taskgraph-22.0.0 → taskcluster_taskgraph-23.1.0}/taskcluster/kinds/test/windows.yml +0 -0
  196. {taskcluster_taskgraph-22.0.0 → taskcluster_taskgraph-23.1.0}/taskcluster/scripts/codecov-upload.py +0 -0
  197. {taskcluster_taskgraph-22.0.0 → taskcluster_taskgraph-23.1.0}/taskcluster/self_taskgraph/__init__.py +0 -0
  198. {taskcluster_taskgraph-22.0.0 → taskcluster_taskgraph-23.1.0}/taskcluster/self_taskgraph/custom_target_tasks.py +0 -0
  199. {taskcluster_taskgraph-22.0.0 → taskcluster_taskgraph-23.1.0}/taskcluster/self_taskgraph/transforms/add_pr_route.py +0 -0
  200. {taskcluster_taskgraph-22.0.0 → taskcluster_taskgraph-23.1.0}/taskcluster/self_taskgraph/transforms/push_image.py +0 -0
  201. {taskcluster_taskgraph-22.0.0 → taskcluster_taskgraph-23.1.0}/taskcluster/test/params/main-repo-pull-request-untrusted.yml +0 -0
  202. {taskcluster_taskgraph-22.0.0 → taskcluster_taskgraph-23.1.0}/taskcluster/test/params/main-repo-pull-request.yml +0 -0
  203. {taskcluster_taskgraph-22.0.0 → taskcluster_taskgraph-23.1.0}/taskcluster/test/params/main-repo-push.yml +0 -0
  204. {taskcluster_taskgraph-22.0.0 → taskcluster_taskgraph-23.1.0}/taskcluster/test/params/main-repo-release-pytest-taskgraph.yml +0 -0
  205. {taskcluster_taskgraph-22.0.0 → taskcluster_taskgraph-23.1.0}/taskcluster/test/params/main-repo-release-taskcluster-taskgraph.yml +0 -0
  206. {taskcluster_taskgraph-22.0.0 → taskcluster_taskgraph-23.1.0}/template/cookiecutter.json +0 -0
  207. {taskcluster_taskgraph-22.0.0 → taskcluster_taskgraph-23.1.0}/template/hooks/post_gen_project.py +0 -0
  208. {taskcluster_taskgraph-22.0.0 → taskcluster_taskgraph-23.1.0}/template/{{cookiecutter.project_name}}/taskcluster/docker/linux/Dockerfile +0 -0
  209. {taskcluster_taskgraph-22.0.0 → taskcluster_taskgraph-23.1.0}/template/{{cookiecutter.project_name}}/taskcluster/kinds/docker-image/kind.yml +0 -0
  210. {taskcluster_taskgraph-22.0.0 → taskcluster_taskgraph-23.1.0}/template/{{cookiecutter.project_name}}/taskcluster/kinds/hello/kind.yml +0 -0
  211. {taskcluster_taskgraph-22.0.0 → taskcluster_taskgraph-23.1.0}/template/{{cookiecutter.project_name}}/taskcluster/{{cookiecutter.project_slug}}_taskgraph/transforms/hello.py +0 -0
  212. {taskcluster_taskgraph-22.0.0 → taskcluster_taskgraph-23.1.0}/template/{{cookiecutter.project_name}}/taskcluster.github.yml +0 -0
  213. {taskcluster_taskgraph-22.0.0 → taskcluster_taskgraph-23.1.0}/test/__init__.py +0 -0
  214. {taskcluster_taskgraph-22.0.0 → taskcluster_taskgraph-23.1.0}/test/automationrelevance.json +0 -0
  215. {taskcluster_taskgraph-22.0.0 → taskcluster_taskgraph-23.1.0}/test/data/task_context.yml +0 -0
  216. {taskcluster_taskgraph-22.0.0 → taskcluster_taskgraph-23.1.0}/test/data/taskcluster/config.yml +0 -0
  217. {taskcluster_taskgraph-22.0.0 → taskcluster_taskgraph-23.1.0}/test/data/taskcluster/docker/hello-world/Dockerfile +0 -0
  218. {taskcluster_taskgraph-22.0.0 → taskcluster_taskgraph-23.1.0}/test/data/taskcluster/docker/hello-world-tag/Dockerfile +0 -0
  219. {taskcluster_taskgraph-22.0.0 → taskcluster_taskgraph-23.1.0}/test/data/taskcluster/docker/hello-world-tag/REGISTRY +0 -0
  220. {taskcluster_taskgraph-22.0.0 → taskcluster_taskgraph-23.1.0}/test/data/taskcluster/docker/hello-world-tag/VERSION +0 -0
  221. {taskcluster_taskgraph-22.0.0 → taskcluster_taskgraph-23.1.0}/test/data/taskcluster/kinds/docker-image/kind.yml +0 -0
  222. {taskcluster_taskgraph-22.0.0 → taskcluster_taskgraph-23.1.0}/test/data/taskcluster/scripts/toolchain/run.ps1 +0 -0
  223. {taskcluster_taskgraph-22.0.0 → taskcluster_taskgraph-23.1.0}/test/data/taskcluster/scripts/toolchain/run.sh +0 -0
  224. {taskcluster_taskgraph-22.0.0 → taskcluster_taskgraph-23.1.0}/test/data/taskcluster/test_taskgraph/transforms/foo.py +0 -0
  225. {taskcluster_taskgraph-22.0.0 → taskcluster_taskgraph-23.1.0}/test/data/testmod/thing.py +0 -0
  226. {taskcluster_taskgraph-22.0.0 → taskcluster_taskgraph-23.1.0}/test/mockedopen.py +0 -0
  227. {taskcluster_taskgraph-22.0.0 → taskcluster_taskgraph-23.1.0}/test/test_actions_rebuild_cached_tasks.py +0 -0
  228. {taskcluster_taskgraph-22.0.0 → taskcluster_taskgraph-23.1.0}/test/test_config.py +0 -0
  229. {taskcluster_taskgraph-22.0.0 → taskcluster_taskgraph-23.1.0}/test/test_create.py +0 -0
  230. {taskcluster_taskgraph-22.0.0 → taskcluster_taskgraph-23.1.0}/test/test_decision.py +0 -0
  231. {taskcluster_taskgraph-22.0.0 → taskcluster_taskgraph-23.1.0}/test/test_docker.py +0 -0
  232. {taskcluster_taskgraph-22.0.0 → taskcluster_taskgraph-23.1.0}/test/test_graph.py +0 -0
  233. {taskcluster_taskgraph-22.0.0 → taskcluster_taskgraph-23.1.0}/test/test_morph.py +0 -0
  234. {taskcluster_taskgraph-22.0.0 → taskcluster_taskgraph-23.1.0}/test/test_optimize.py +0 -0
  235. {taskcluster_taskgraph-22.0.0 → taskcluster_taskgraph-23.1.0}/test/test_optimize_strategies.py +0 -0
  236. {taskcluster_taskgraph-22.0.0 → taskcluster_taskgraph-23.1.0}/test/test_scripts_run_task.py +0 -0
  237. {taskcluster_taskgraph-22.0.0 → taskcluster_taskgraph-23.1.0}/test/test_target_tasks.py +0 -0
  238. {taskcluster_taskgraph-22.0.0 → taskcluster_taskgraph-23.1.0}/test/test_transform_chunking.py +0 -0
  239. {taskcluster_taskgraph-22.0.0 → taskcluster_taskgraph-23.1.0}/test/test_transform_docker_image.py +0 -0
  240. {taskcluster_taskgraph-22.0.0 → taskcluster_taskgraph-23.1.0}/test/test_transform_task_context.py +0 -0
  241. {taskcluster_taskgraph-22.0.0 → taskcluster_taskgraph-23.1.0}/test/test_transforms_base.py +0 -0
  242. {taskcluster_taskgraph-22.0.0 → taskcluster_taskgraph-23.1.0}/test/test_transforms_cached_tasks.py +0 -0
  243. {taskcluster_taskgraph-22.0.0 → taskcluster_taskgraph-23.1.0}/test/test_transforms_fetch.py +0 -0
  244. {taskcluster_taskgraph-22.0.0 → taskcluster_taskgraph-23.1.0}/test/test_transforms_from_deps.py +0 -0
  245. {taskcluster_taskgraph-22.0.0 → taskcluster_taskgraph-23.1.0}/test/test_transforms_matrix.py +0 -0
  246. {taskcluster_taskgraph-22.0.0 → taskcluster_taskgraph-23.1.0}/test/test_transforms_notify.py +0 -0
  247. {taskcluster_taskgraph-22.0.0 → taskcluster_taskgraph-23.1.0}/test/test_transforms_run.py +0 -0
  248. {taskcluster_taskgraph-22.0.0 → taskcluster_taskgraph-23.1.0}/test/test_transforms_run_run_task.py +0 -0
  249. {taskcluster_taskgraph-22.0.0 → taskcluster_taskgraph-23.1.0}/test/test_transforms_run_toolchain.py +0 -0
  250. {taskcluster_taskgraph-22.0.0 → taskcluster_taskgraph-23.1.0}/test/test_transforms_task.py +0 -0
  251. {taskcluster_taskgraph-22.0.0 → taskcluster_taskgraph-23.1.0}/test/test_util_archive.py +0 -0
  252. {taskcluster_taskgraph-22.0.0 → taskcluster_taskgraph-23.1.0}/test/test_util_attributes.py +0 -0
  253. {taskcluster_taskgraph-22.0.0 → taskcluster_taskgraph-23.1.0}/test/test_util_cached_tasks.py +0 -0
  254. {taskcluster_taskgraph-22.0.0 → taskcluster_taskgraph-23.1.0}/test/test_util_copy.py +0 -0
  255. {taskcluster_taskgraph-22.0.0 → taskcluster_taskgraph-23.1.0}/test/test_util_dependencies.py +0 -0
  256. {taskcluster_taskgraph-22.0.0 → taskcluster_taskgraph-23.1.0}/test/test_util_docker.py +0 -0
  257. {taskcluster_taskgraph-22.0.0 → taskcluster_taskgraph-23.1.0}/test/test_util_json.py +0 -0
  258. {taskcluster_taskgraph-22.0.0 → taskcluster_taskgraph-23.1.0}/test/test_util_keyed_by.py +0 -0
  259. {taskcluster_taskgraph-22.0.0 → taskcluster_taskgraph-23.1.0}/test/test_util_parameterization.py +0 -0
  260. {taskcluster_taskgraph-22.0.0 → taskcluster_taskgraph-23.1.0}/test/test_util_path.py +0 -0
  261. {taskcluster_taskgraph-22.0.0 → taskcluster_taskgraph-23.1.0}/test/test_util_python_path.py +0 -0
  262. {taskcluster_taskgraph-22.0.0 → taskcluster_taskgraph-23.1.0}/test/test_util_readonlydict.py +0 -0
  263. {taskcluster_taskgraph-22.0.0 → taskcluster_taskgraph-23.1.0}/test/test_util_taskcluster.py +0 -0
  264. {taskcluster_taskgraph-22.0.0 → taskcluster_taskgraph-23.1.0}/test/test_util_templates.py +0 -0
  265. {taskcluster_taskgraph-22.0.0 → taskcluster_taskgraph-23.1.0}/test/test_util_time.py +0 -0
  266. {taskcluster_taskgraph-22.0.0 → taskcluster_taskgraph-23.1.0}/test/test_util_treeherder.py +0 -0
  267. {taskcluster_taskgraph-22.0.0 → taskcluster_taskgraph-23.1.0}/test/test_util_vcs.py +0 -0
  268. {taskcluster_taskgraph-22.0.0 → taskcluster_taskgraph-23.1.0}/test/test_util_verify.py +0 -0
  269. {taskcluster_taskgraph-22.0.0 → taskcluster_taskgraph-23.1.0}/test/test_util_workertypes.py +0 -0
  270. {taskcluster_taskgraph-22.0.0 → taskcluster_taskgraph-23.1.0}/test/test_util_yaml.py +0 -0
@@ -13,13 +13,13 @@ repos:
13
13
  exclude: template
14
14
  - id: check-added-large-files
15
15
  - repo: https://github.com/astral-sh/ruff-pre-commit
16
- rev: v0.15.8
16
+ rev: v0.15.12
17
17
  hooks:
18
18
  - id: ruff
19
19
  args: [--fix, --exit-non-zero-on-fix]
20
20
  - id: ruff-format
21
21
  - repo: https://github.com/astral-sh/uv-pre-commit
22
- rev: 0.11.2
22
+ rev: 0.11.8
23
23
  hooks:
24
24
  - id: uv-lock
25
25
  - repo: https://github.com/adrienverge/yamllint.git
@@ -1,5 +1,34 @@
1
1
  # Change Log
2
2
 
3
+ ## [23.1.0] - 2026-05-26
4
+
5
+ ### Added
6
+
7
+ - Updated worker types in the `.taskcluster.yml` template for the d2g migration
8
+
9
+ ### Fixed
10
+
11
+ - `TaskGraph.from_json` now skips dependencies pointing at external taskIds (e.g. from replaced or cached tasks) when rebuilding edges, instead of producing edges to non-existent graph nodes
12
+ - Schema validation errors now display a clean message instead of a noisy traceback
13
+ - Pinned `taskcluster-client` to work around an upstream issue
14
+ - `fetch_graph_and_labels` now handles `HTTPError` instances where `response` is `None`
15
+
16
+ ## [23.0.0] - 2026-05-04
17
+
18
+ ### Added
19
+
20
+ - Support for defining schemas as dicts (in addition to the class-based approach)
21
+ - New `disabled_actions` graph configuration to prevent specific actions from being generated
22
+
23
+ ### Changed
24
+
25
+ - BREAKING CHANGE: Converted parameter schema from voluptuous to msgspec
26
+
27
+ ### Fixed
28
+
29
+ - `taskgraph full -J --tasks <regex>` now displays full dependencies instead of a filtered subset
30
+ - `fetch-content` now percent-encodes artifact names, fixing downloads of artifacts containing spaces or other special characters
31
+
3
32
  ## [22.0.0] - 2026-04-16
4
33
 
5
34
  ### Fixed
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: taskcluster-taskgraph
3
- Version: 22.0.0
3
+ Version: 23.1.0
4
4
  Summary: Build taskcluster taskgraphs
5
5
  Project-URL: Repository, https://github.com/taskcluster/taskgraph
6
6
  Project-URL: Issues, https://github.com/taskcluster/taskgraph/issues
@@ -0,0 +1,156 @@
1
+ Define Schemas
2
+ ==============
3
+
4
+ :doc:`Transforms </concepts/transforms>` can define schemas to validate task data at each step of the
5
+ pipeline. Taskgraph uses `msgspec`_ under the hood, and provides a
6
+ :class:`~taskgraph.util.schema.Schema` base class that integrates with
7
+ :meth:`TransformSequence.add_validate() <taskgraph.transforms.base.TransformSequence.add_validate>`.
8
+
9
+ There are two ways to define a schema: the **class based** approach and the
10
+ **dict based** approach. Both produce equivalent results; which one you prefer
11
+ is a matter of style.
12
+
13
+ .. _msgspec: https://jcristharif.com/msgspec/
14
+
15
+
16
+ Class Based Schemas
17
+ -------------------
18
+
19
+ Subclass :class:`~taskgraph.util.schema.Schema` and declare fields as class
20
+ attributes with type annotations:
21
+
22
+ .. code-block:: python
23
+
24
+ from typing import Optional
25
+ from taskgraph.transforms.base import TransformSequence
26
+ from taskgraph.util.schema import Schema
27
+
28
+ class MySubConfig(Schema):
29
+ total_num: int
30
+ fields: list[str] = []
31
+
32
+ class MySchema(Schema, forbid_unknown_fields=False):
33
+ config: Optional[MySubConfig] = None
34
+
35
+ transforms = TransformSequence()
36
+ transforms.add_validate(MySchema)
37
+
38
+ A few things to note:
39
+
40
+ - Field names use ``snake_case`` in Python but are **automatically renamed to
41
+ ``kebab-case``** in YAML. So ``total_num`` in Python matches
42
+ ``total-num`` in YAML.
43
+ - ``Optional[T]`` fields default to ``None`` unless you supply an explicit
44
+ default.
45
+ - Fields without a default are **required**.
46
+ - ``forbid_unknown_fields=True`` (the default) causes validation to fail if the
47
+ task data contains keys that are not declared in the schema. Set it to
48
+ ``False`` on outer schemas so that fields belonging to later transforms are
49
+ not rejected.
50
+
51
+
52
+ Dict Based Schemas
53
+ ------------------
54
+
55
+ Call :meth:`Schema.from_dict() <taskgraph.util.schema.Schema.from_dict>` with a
56
+ dictionary mapping field names to ``type`` or ``(type, default)`` tuples:
57
+
58
+ .. code-block:: python
59
+
60
+ from typing import Optional, Union
61
+ from taskgraph.transforms.base import TransformSequence
62
+ from taskgraph.util.schema import Schema
63
+
64
+ MySchema = Schema.from_dict(
65
+ {
66
+ "config": Schema.from_dict(
67
+ {
68
+ "total-num": int,
69
+ "fields": list[str] = []
70
+ },
71
+ optional=True,
72
+ ),
73
+ },
74
+ forbid_unknown_fields=False,
75
+ )
76
+
77
+ transforms = TransformSequence()
78
+ transforms.add_validate(MySchema)
79
+
80
+ This example is equivalent to the first example. One advantage with the dict based approach
81
+ is that you can write keys in **kebab-case** directly.
82
+
83
+ Field specifications follow these rules:
84
+
85
+ - A bare type (e.g. ``str``) means the field is required.
86
+ - ``Optional[T]`` means the field is optional and defaults to ``None``.
87
+ - A ``(type, default)`` tuple supplies an explicit default, e.g.
88
+ ``(list[str], [])``.
89
+
90
+ Keyword arguments to ``from_dict`` are forwarded to ``msgspec.defstruct``.
91
+ The most commonly used ones are ``name`` (for better error messages) and
92
+ ``forbid_unknown_fields``.
93
+
94
+ .. note::
95
+ ``Schema.from_dict`` does **not** apply ``rename="kebab"`` automatically,
96
+ because you can express the kebab-case names directly in the dict keys.
97
+ Underscores in dict keys stay as underscores and dashes become valid
98
+ kebab-case field names.
99
+
100
+
101
+ Nesting Schemas
102
+ ---------------
103
+
104
+ Both approaches support nesting:
105
+
106
+ .. code-block:: python
107
+
108
+ # Class-based nesting
109
+ class Inner(Schema):
110
+ value: str
111
+
112
+ class Outer(Schema, forbid_unknown_fields=False, kw_only=True):
113
+ inner: Optional[Inner] = None
114
+
115
+ # Dict-based nesting
116
+ Outer = Schema.from_dict(
117
+ {
118
+ "inner": Schema.from_dict({"value": str}, optional=True),
119
+ },
120
+ forbid_unknown_fields=False,
121
+ )
122
+
123
+ Pass ``optional=True`` to ``from_dict`` to make the whole nested schema
124
+ optional. This is necessary as function calls are not allowed in type
125
+ annotations, so ``Optional[Schema.from_dict(...)]`` is not valid Python.
126
+
127
+
128
+ Mutually Exclusive Fields
129
+ -------------------------
130
+
131
+ Use the ``exclusive`` keyword to declare groups of fields where at most one
132
+ may be set at a time:
133
+
134
+ .. code-block:: python
135
+
136
+ # Class-based
137
+ class MySchema(Schema, exclusive=[["field_a", "field_b"]]):
138
+ field_a: Optional[str] = None
139
+ field_b: Optional[str] = None
140
+
141
+ # Dict-based
142
+ MySchema = Schema.from_dict(
143
+ {
144
+ "field-a": Optional[str],
145
+ "field-b": Optional[str],
146
+ },
147
+ exclusive=[["field_a", "field_b"]],
148
+ )
149
+
150
+ ``exclusive`` takes a list of groups, where each group is a list of field
151
+ names (Python ``snake_case``). A validation error is raised if more than one
152
+ field in a group is set.
153
+
154
+ .. note::
155
+ When using ``exclusive`` with the dict-based approach, refer to fields by
156
+ their Python attribute names (``snake_case``), not their YAML keys.
@@ -10,6 +10,7 @@ A collection of how-to guides.
10
10
  run-locally
11
11
  debugging
12
12
  bootstrap-taskgraph
13
+ define-schemas
13
14
  resolve-keyed-by
14
15
  use-fetches
15
16
  docker
@@ -3,6 +3,13 @@ Migration Guide
3
3
 
4
4
  This page can help when migrating Taskgraph across major versions.
5
5
 
6
+ 22.x -> 23.x
7
+ ------------
8
+
9
+ * :func:`taskgraph.parameters.extend_parameters_schema` now requires a msgspec
10
+ ``Schema`` subclass instead of a voluptuous schema dict. Convert any custom
11
+ parameter schemas to msgspec.
12
+
6
13
  21.x -> 22.x
7
14
  ------------
8
15
 
@@ -1,7 +1,7 @@
1
1
  ### Project
2
2
  [project]
3
3
  name = "taskcluster-taskgraph"
4
- version = "22.0.0"
4
+ version = "23.1.0"
5
5
  description = "Build taskcluster taskgraphs"
6
6
  readme = "README.rst"
7
7
  authors = [
@@ -0,0 +1,6 @@
1
+ {
2
+ "$schema": "https://docs.renovatebot.com/renovate-schema.json",
3
+ "extends": [
4
+ "github>mozilla-releng/reps:renovate-preset.json5"
5
+ ]
6
+ }
@@ -273,12 +273,27 @@ def render_actions_json(parameters, graph_config, decision_task_id):
273
273
  artifact.
274
274
  """
275
275
  assert isinstance(parameters, Parameters), "requires instance of Parameters"
276
+
277
+ all_actions = _get_actions(graph_config)
278
+ disabled = set(graph_config["taskgraph"].get("disabled-actions") or [])
279
+ known_cb_names = {action.cb_name for action in all_actions}
280
+ unknown = disabled - known_cb_names
281
+ if unknown:
282
+ raise ValueError(
283
+ "Unknown action(s) in `taskgraph.disabled-actions`: "
284
+ f"{sorted(unknown)}. Known actions: {sorted(known_cb_names)}"
285
+ )
286
+
276
287
  actions = []
277
- for action in sorted(_get_actions(graph_config), key=lambda action: action.order):
288
+ for action in sorted(all_actions, key=lambda action: action.order):
289
+ if action.cb_name in disabled:
290
+ continue
291
+
278
292
  action = action.action_builder(parameters, graph_config, decision_task_id)
279
293
  if action:
280
294
  assert is_json(action), "action must be a JSON compatible object"
281
295
  actions.append(action)
296
+
282
297
  return {
283
298
  "version": 1,
284
299
  "variables": {},
@@ -85,7 +85,7 @@ def fetch_graph_and_labels(parameters, graph_config, task_group_id=None):
85
85
  run_label_to_id = get_artifact(task_id, "public/label-to-taskid.json")
86
86
  label_to_taskid.update(run_label_to_id) # type: ignore
87
87
  except HTTPError as e:
88
- if e.response.status_code != 404:
88
+ if e.response is None or e.response.status_code != 404:
89
89
  raise
90
90
  logger.debug(f"No label-to-taskid.json found for {task_id}: {e}")
91
91
 
@@ -57,6 +57,9 @@ class TaskgraphConfig(Schema):
57
57
  index_path_regexes: Optional[list[str]] = None
58
58
  # Configuration related to the 'run' transforms.
59
59
  run: Optional[RunConfig] = None
60
+ # List of action `cb_name`s to omit from `actions.json`,
61
+ # e.g. `["retrigger", "retrigger-disabled", "rerun"]`.
62
+ disabled_actions: Optional[list[str]] = None
60
63
 
61
64
  def __post_init__(self):
62
65
  # Validate repositories has at least 1 entry (was All(..., Length(min=1)))
@@ -27,6 +27,7 @@ from .task import Task
27
27
  from .taskgraph import TaskGraph
28
28
  from .transforms.base import TransformConfig, TransformSequence
29
29
  from .util.python_path import find_object
30
+ from .util.schema import SchemaValidationError
30
31
  from .util.verify import verifications
31
32
  from .util.yaml import load_yaml
32
33
 
@@ -303,6 +304,9 @@ class TaskGraphGenerator:
303
304
  },
304
305
  self._write_artifacts,
305
306
  )
307
+ except SchemaValidationError as exc:
308
+ logger.error(f"Error loading tasks for kind {kind_name}:\n{exc}")
309
+ raise
306
310
  except Exception:
307
311
  logger.exception(f"Error loading tasks for kind {kind_name}:")
308
312
  raise
@@ -358,6 +362,16 @@ class TaskGraphGenerator:
358
362
  for future in done:
359
363
  if exc := future.exception():
360
364
  executor.shutdown(wait=False, cancel_futures=True)
365
+ kind_name = futures_to_kind.get(future, "?")
366
+ if isinstance(exc, SchemaValidationError):
367
+ logger.error(
368
+ f"Error loading tasks for kind {kind_name}:\n{exc}"
369
+ )
370
+ else:
371
+ logger.error(
372
+ f"Error loading tasks for kind {kind_name}:",
373
+ exc_info=(type(exc), exc, exc.__traceback__),
374
+ )
361
375
  raise exc
362
376
  kind = futures_to_kind.pop(future)
363
377
  futures.remove(future)
@@ -22,6 +22,8 @@ from urllib.parse import urlparse
22
22
  import appdirs
23
23
  import yaml
24
24
 
25
+ from taskgraph.util.schema import SchemaValidationError
26
+
25
27
  Command = namedtuple("Command", ["func", "args", "kwargs", "defaults"])
26
28
  commands = {}
27
29
 
@@ -1207,6 +1209,9 @@ def main(args=sys.argv[1:]):
1207
1209
  args = parser.parse_args(args)
1208
1210
  try:
1209
1211
  return args.command(vars(args))
1212
+ except SchemaValidationError:
1213
+ # Message was already logged; skip the traceback for user input errors.
1214
+ sys.exit(1)
1210
1215
  except Exception:
1211
1216
  traceback.print_exc()
1212
1217
  sys.exit(1)
@@ -10,16 +10,17 @@ from datetime import datetime
10
10
  from io import BytesIO
11
11
  from pprint import pformat
12
12
  from subprocess import CalledProcessError
13
+ from typing import Optional, Union
13
14
  from unittest.mock import Mock
14
15
  from urllib.parse import urlparse
15
16
  from urllib.request import urlopen
16
17
 
17
18
  import mozilla_repo_urls
18
- from voluptuous import ALLOW_EXTRA, Any, Optional, Required, Schema
19
+ import msgspec
19
20
 
20
21
  from taskgraph.util import json, yaml
21
22
  from taskgraph.util.readonlydict import ReadOnlyDict
22
- from taskgraph.util.schema import validate_schema
23
+ from taskgraph.util.schema import Schema, validate_schema
23
24
  from taskgraph.util.taskcluster import find_task_id, get_artifact_url
24
25
  from taskgraph.util.vcs import get_repository
25
26
 
@@ -30,43 +31,50 @@ class ParameterMismatch(Exception):
30
31
 
31
32
  #: Schema for base parameters.
32
33
  #: Please keep this list sorted and in sync with docs/reference/parameters.rst
33
- base_schema = Schema(
34
+ base_schema = Schema.from_dict(
34
35
  {
35
- Required("base_repository"): str,
36
- Optional("base_ref"): str,
37
- Required("base_rev"): str,
38
- Required("build_date"): int,
39
- Required("build_number"): int,
40
- Required("do_not_optimize"): [str],
41
- Required("enable_always_target"): Any(bool, [str]),
42
- Required("existing_tasks"): {str: str},
43
- Required("files_changed"): [str],
44
- Required("filters"): [str],
45
- Required("head_ref"): str,
46
- Required("head_repository"): str,
47
- Required("head_rev"): str,
48
- Required("head_tag"): str,
49
- Required("level"): str,
50
- Required("moz_build_date"): str,
51
- Required("next_version"): Any(str, None),
52
- Required("optimize_strategies"): Any(str, None),
53
- Required("optimize_target_tasks"): bool,
54
- Required("owner"): str,
55
- Required("project"): str,
56
- Required("pushdate"): int,
57
- Required("pushlog_id"): str,
58
- Required("repository_type"): str,
36
+ "base_repository": str,
37
+ "base_ref": Optional[str],
38
+ "base_rev": str,
39
+ "build_date": int,
40
+ "build_number": int,
41
+ "do_not_optimize": list[str],
42
+ "enable_always_target": Union[bool, list[str]],
43
+ "existing_tasks": dict[str, str],
44
+ "files_changed": list[str],
45
+ "filters": list[str],
46
+ "head_ref": str,
47
+ "head_repository": str,
48
+ "head_rev": str,
49
+ "head_tag": str,
50
+ "level": str,
51
+ "moz_build_date": str,
52
+ "next_version": Optional[str],
53
+ "optimize_strategies": Optional[str],
54
+ "optimize_target_tasks": bool,
55
+ "owner": str,
56
+ "project": str,
57
+ "pushdate": int,
58
+ "pushlog_id": str,
59
+ "repository_type": str,
59
60
  # target-kinds is not included, since it should never be
60
61
  # used at run-time
61
- Required("target_tasks_method"): str,
62
- Required("tasks_for"): str,
63
- Required("version"): Any(str, None),
64
- Optional("code-review"): {
65
- Required("phabricator-build-target"): str,
66
- },
67
- }
62
+ "target_tasks_method": str,
63
+ "tasks_for": str,
64
+ "version": Optional[str],
65
+ "code-review": Schema.from_dict(
66
+ {"phabricator-build-target": str},
67
+ name="CodeReviewConfig",
68
+ optional=True,
69
+ ),
70
+ },
71
+ name="BaseParametersSchema",
72
+ forbid_unknown_fields=False,
73
+ kw_only=True,
68
74
  )
69
75
 
76
+ _parameter_extensions: list = []
77
+
70
78
 
71
79
  def get_contents(path):
72
80
  with open(path) as fh:
@@ -83,11 +91,21 @@ def _get_defaults(repo_root=None):
83
91
  repo_path = repo_root or os.getcwd()
84
92
  try:
85
93
  repo = get_repository(repo_path)
86
- except RuntimeError:
87
- # Use fake values if no repo is detected.
88
- repo = Mock(branch="", head_rev="", tool="git")
94
+ # Resolve git-backed attributes eagerly so any subprocess failures
95
+ # (e.g. Windows "dubious ownership" when safe.directory isn't honored)
96
+ # are caught by the except below instead of escaping later.
97
+ branch = repo.branch
98
+ head_rev = repo.head_rev
99
+ tool = repo.tool
100
+ files_changed = repo.get_changed_files("AM")
101
+ except (RuntimeError, CalledProcessError):
102
+ # Use fake values if no repo is detected or git refuses to operate.
103
+ repo = Mock()
89
104
  repo.get_url.return_value = ""
90
- repo.get_changed_files.return_value = []
105
+ branch = ""
106
+ head_rev = ""
107
+ tool = "git"
108
+ files_changed = []
91
109
 
92
110
  try:
93
111
  repo_url = repo.get_url()
@@ -110,11 +128,11 @@ def _get_defaults(repo_root=None):
110
128
  "do_not_optimize": [],
111
129
  "enable_always_target": True,
112
130
  "existing_tasks": {},
113
- "files_changed": lambda: repo.get_changed_files("AM"),
131
+ "files_changed": files_changed,
114
132
  "filters": ["target_tasks_method"],
115
- "head_ref": repo.branch or repo.head_rev,
133
+ "head_ref": branch or head_rev,
116
134
  "head_repository": repo_url,
117
- "head_rev": repo.head_rev,
135
+ "head_rev": head_rev,
118
136
  "head_tag": "",
119
137
  "level": "3",
120
138
  "moz_build_date": datetime.now().strftime("%Y%m%d%H%M%S"),
@@ -125,7 +143,7 @@ def _get_defaults(repo_root=None):
125
143
  "project": project,
126
144
  "pushdate": int(time.time()),
127
145
  "pushlog_id": "0",
128
- "repository_type": repo.tool,
146
+ "repository_type": tool,
129
147
  "target_tasks_method": "default",
130
148
  "tasks_for": "",
131
149
  "version": get_version(repo_path),
@@ -143,19 +161,27 @@ def extend_parameters_schema(schema, defaults_fn=None):
143
161
  graph-configuration.
144
162
 
145
163
  Args:
146
- schema (Schema): The voluptuous.Schema object used to describe extended
147
- parameters.
164
+ schema: A msgspec ``Schema`` subclass describing extended parameters.
148
165
  defaults_fn (function): A function which takes no arguments and returns a
149
166
  dict mapping parameter name to default value in the
150
167
  event strict=False (optional).
151
168
  """
152
- global base_schema
153
169
  global defaults_functions
154
- base_schema = base_schema.extend(schema)
170
+ if not (isinstance(schema, type) and issubclass(schema, msgspec.Struct)):
171
+ raise TypeError(
172
+ "extend_parameters_schema requires a msgspec Schema subclass; "
173
+ f"got {type(schema).__name__}"
174
+ )
175
+ _parameter_extensions.append(schema)
155
176
  if defaults_fn:
156
177
  defaults_functions.append(defaults_fn)
157
178
 
158
179
 
180
+ def _schema_key_names(schema) -> set:
181
+ """Return the data-level field names declared by a parameters schema."""
182
+ return {f.encode_name for f in msgspec.structs.fields(schema)}
183
+
184
+
159
185
  class Parameters(ReadOnlyDict):
160
186
  """An immutable dictionary with nicer KeyError messages on failure"""
161
187
 
@@ -214,11 +240,30 @@ class Parameters(ReadOnlyDict):
214
240
  return kwargs
215
241
 
216
242
  def check(self):
217
- schema = (
218
- base_schema if self.strict else base_schema.extend({}, extra=ALLOW_EXTRA)
219
- )
243
+ data = dict(self.copy())
220
244
  try:
221
- validate_schema(schema, self.copy(), "Invalid parameters:")
245
+ # Validate core fields against just the subset of data owned by the
246
+ # base schema. Extension keys are validated separately below, and a
247
+ # strict-mode check rejects anything unknown to either.
248
+ base_keys = _schema_key_names(base_schema)
249
+ base_data = {k: v for k, v in data.items() if k in base_keys}
250
+ validate_schema(base_schema, base_data, "Invalid parameters:")
251
+
252
+ # Validate each registered extension against the keys it declares.
253
+ allowed = set(base_keys)
254
+ for ext in _parameter_extensions:
255
+ ext_keys = _schema_key_names(ext)
256
+ allowed |= ext_keys
257
+ ext_data = {k: data[k] for k in ext_keys if k in data}
258
+ validate_schema(ext, ext_data, "Invalid parameters:")
259
+
260
+ # Strict mode: reject any data key not covered by base or extensions.
261
+ if self.strict:
262
+ unknown = sorted(set(data) - allowed)
263
+ if unknown:
264
+ raise Exception(
265
+ "Invalid parameters:\nunknown keys: " + ", ".join(unknown)
266
+ )
222
267
  except Exception as e:
223
268
  raise ParameterMismatch(str(e))
224
269
 
@@ -626,7 +626,7 @@ def fetch_and_extract(url, dest_dir, extract=True, sha256=None, size=None):
626
626
  the destination directory.
627
627
  """
628
628
 
629
- basename = urllib.parse.urlparse(url).path.split("/")[-1]
629
+ basename = urllib.parse.unquote(urllib.parse.urlparse(url).path.split("/")[-1])
630
630
  dest_path = dest_dir / basename
631
631
 
632
632
  download_to_path(url, dest_path, sha256=sha256, size=size)
@@ -920,16 +920,17 @@ def command_task_artifacts(args):
920
920
  sha256 = None
921
921
  if fetch.get("verify-hash"):
922
922
  sha256 = get_hash(fetch, root_url)
923
+ encoded_artifact = urllib.parse.quote(fetch["artifact"])
923
924
  if fetch["artifact"].startswith("public/"):
924
925
  path = "task/{task}/artifacts/{artifact}".format(
925
- task=fetch["task"], artifact=fetch["artifact"]
926
+ task=fetch["task"], artifact=encoded_artifact
926
927
  )
927
928
  url = api(root_url, "queue", "v1", path)
928
929
  else:
929
930
  url = ("{proxy_url}/api/queue/v1/task/{task}/artifacts/{artifact}").format(
930
931
  proxy_url=os.environ["TASKCLUSTER_PROXY_URL"],
931
932
  task=fetch["task"],
932
- artifact=fetch["artifact"],
933
+ artifact=encoded_artifact,
933
934
  )
934
935
  downloads.append((url, extdir, fetch["extract"], sha256))
935
936
 
@@ -44,13 +44,10 @@ class TaskGraph:
44
44
 
45
45
  def to_json(self):
46
46
  "Return a JSON-able object representing the task graph, as documented"
47
- named_links_dict = self.graph.named_links_dict()
48
47
  # this dictionary may be keyed by label or by taskid, so let's just call it 'key'
49
48
  tasks = {}
50
49
  for key in self.graph.visit_postorder():
51
50
  tasks[key] = self.tasks[key].to_json()
52
- # overwrite dependencies with the information in the taskgraph's edges.
53
- tasks[key]["dependencies"] = named_links_dict.get(key, {})
54
51
  return tasks
55
52
 
56
53
  @classmethod
@@ -66,6 +63,8 @@ class TaskGraph:
66
63
  if "task_id" in value:
67
64
  tasks[key].task_id = value["task_id"]
68
65
  for depname, dep in value["dependencies"].items():
69
- edges.add((key, dep, depname))
66
+ # Task filtering can cause dependencies to be removed from the graph.
67
+ if dep in tasks_dict:
68
+ edges.add((key, dep, depname))
70
69
  task_graph = cls(tasks, Graph(frozenset(tasks), edges))
71
70
  return tasks, task_graph