scriptworker 62.2.3__tar.gz → 62.4.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 (30) hide show
  1. {scriptworker-62.2.3 → scriptworker-62.4.0}/HISTORY.rst +18 -0
  2. {scriptworker-62.2.3 → scriptworker-62.4.0}/PKG-INFO +2 -2
  3. {scriptworker-62.2.3 → scriptworker-62.4.0}/pyproject.toml +4 -6
  4. scriptworker-62.4.0/renovate.json +6 -0
  5. {scriptworker-62.2.3 → scriptworker-62.4.0}/scripts/gen_ed25519_key.py +1 -0
  6. {scriptworker-62.2.3 → scriptworker-62.4.0}/src/scriptworker/client.py +1 -0
  7. {scriptworker-62.2.3 → scriptworker-62.4.0}/src/scriptworker/config.py +1 -0
  8. {scriptworker-62.2.3 → scriptworker-62.4.0}/src/scriptworker/constants.py +8 -2
  9. {scriptworker-62.2.3 → scriptworker-62.4.0}/src/scriptworker/context.py +1 -0
  10. {scriptworker-62.2.3 → scriptworker-62.4.0}/src/scriptworker/cot/generate.py +1 -0
  11. {scriptworker-62.2.3 → scriptworker-62.4.0}/src/scriptworker/cot/verify.py +13 -19
  12. {scriptworker-62.2.3 → scriptworker-62.4.0}/src/scriptworker/ed25519.py +3 -4
  13. {scriptworker-62.2.3 → scriptworker-62.4.0}/src/scriptworker/github.py +31 -17
  14. {scriptworker-62.2.3 → scriptworker-62.4.0}/src/scriptworker/log.py +1 -0
  15. {scriptworker-62.2.3 → scriptworker-62.4.0}/src/scriptworker/utils.py +1 -0
  16. {scriptworker-62.2.3 → scriptworker-62.4.0}/src/scriptworker/worker.py +1 -0
  17. {scriptworker-62.2.3 → scriptworker-62.4.0}/tox.ini +4 -0
  18. {scriptworker-62.2.3 → scriptworker-62.4.0}/.gitignore +0 -0
  19. {scriptworker-62.2.3 → scriptworker-62.4.0}/CONTRIBUTING.rst +0 -0
  20. {scriptworker-62.2.3 → scriptworker-62.4.0}/LICENSE +0 -0
  21. {scriptworker-62.2.3 → scriptworker-62.4.0}/README.rst +0 -0
  22. {scriptworker-62.2.3 → scriptworker-62.4.0}/scriptworker.yaml.tmpl +0 -0
  23. {scriptworker-62.2.3 → scriptworker-62.4.0}/src/scriptworker/__init__.py +0 -0
  24. {scriptworker-62.2.3 → scriptworker-62.4.0}/src/scriptworker/artifacts.py +0 -0
  25. {scriptworker-62.2.3 → scriptworker-62.4.0}/src/scriptworker/cot/__init__.py +0 -0
  26. {scriptworker-62.2.3 → scriptworker-62.4.0}/src/scriptworker/data/cot_v1_schema.json +0 -0
  27. {scriptworker-62.2.3 → scriptworker-62.4.0}/src/scriptworker/data/scriptworker_task_schema.json +0 -0
  28. {scriptworker-62.2.3 → scriptworker-62.4.0}/src/scriptworker/exceptions.py +0 -0
  29. {scriptworker-62.2.3 → scriptworker-62.4.0}/src/scriptworker/task.py +0 -0
  30. {scriptworker-62.2.3 → scriptworker-62.4.0}/src/scriptworker/task_process.py +0 -0
@@ -4,6 +4,24 @@ Change Log
4
4
  All notable changes to this project will be documented in this file.
5
5
  This project adheres to `Semantic Versioning <http://semver.org/>`__.
6
6
 
7
+ 62.4.0 - 2026-02-10
8
+ -------------------
9
+
10
+ Added
11
+ ~~~~~
12
+
13
+ - add repository type to jsone context for cron and hg-push tasks (#763)
14
+ - add support for python 3.14 (#744)
15
+ - `translations-1/images` is a valid pool for images tasks (#773)
16
+
17
+ 62.3.0 - 2026-01-06
18
+ -------------------
19
+
20
+ Added
21
+ ~~~~~
22
+
23
+ - `translations-1/decision` is a valid pool for decision tasks
24
+
7
25
  62.2.3 - 2025-12-05
8
26
  -------------------
9
27
 
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: scriptworker
3
- Version: 62.2.3
3
+ Version: 62.4.0
4
4
  Summary: Taskcluster scriptworker
5
5
  Author-email: Mozilla Release Engineering <releng@mozilla.com>
6
6
  License-File: LICENSE
@@ -9,9 +9,9 @@ Classifier: Natural Language :: English
9
9
  Classifier: Programming Language :: Python :: 3.11
10
10
  Classifier: Programming Language :: Python :: 3.12
11
11
  Classifier: Programming Language :: Python :: 3.13
12
+ Classifier: Programming Language :: Python :: 3.14
12
13
  Requires-Python: >=3.11
13
14
  Requires-Dist: aiohttp>=3
14
- Requires-Dist: aiomemoizettl
15
15
  Requires-Dist: arrow>=1.0
16
16
  Requires-Dist: cryptography>=2.6.1
17
17
  Requires-Dist: dictdiffer
@@ -1,6 +1,6 @@
1
1
  [project]
2
2
  name = "scriptworker"
3
- version = "62.2.3"
3
+ version = "62.4.0"
4
4
  description = "Taskcluster scriptworker"
5
5
  authors = [
6
6
  { name = "Mozilla Release Engineering", email = "releng@mozilla.com" },
@@ -13,10 +13,10 @@ classifiers = [
13
13
  "Programming Language :: Python :: 3.11",
14
14
  "Programming Language :: Python :: 3.12",
15
15
  "Programming Language :: Python :: 3.13",
16
+ "Programming Language :: Python :: 3.14",
16
17
  ]
17
18
  dependencies = [
18
19
  "aiohttp>=3",
19
- "aiomemoizettl",
20
20
  "arrow>=1.0",
21
21
  "cryptography>=2.6.1",
22
22
  "dictdiffer",
@@ -35,8 +35,7 @@ docs = [
35
35
  "commonmark",
36
36
  "myst-parser",
37
37
  "recommonmark",
38
- # https://github.com/readthedocs/readthedocs.org/issues/10279
39
- "Sphinx<7",
38
+ "Sphinx",
40
39
  "sphinx_rtd_theme",
41
40
  ]
42
41
  dev = [
@@ -50,9 +49,8 @@ dev = [
50
49
  "flake8_docstrings",
51
50
  "isort>=5",
52
51
  "mock",
53
- "pydocstyle==3.0.0",
54
52
  "pytest",
55
- "pytest-asyncio<1.0",
53
+ "pytest-asyncio",
56
54
  "pytest-mock",
57
55
  "pytest-random-order",
58
56
  "mypy",
@@ -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
+ }
@@ -5,6 +5,7 @@ This script doesn't currently reuse the functions in `scriptworker.ed25519`, for
5
5
  easier standalone use. It could easily be a `console_script` though.
6
6
 
7
7
  """
8
+
8
9
  from __future__ import print_function
9
10
 
10
11
  import base64
@@ -9,6 +9,7 @@ Attributes:
9
9
  log (logging.Logger): the log object for the module
10
10
 
11
11
  """
12
+
12
13
  import asyncio
13
14
  import logging
14
15
  import os
@@ -7,6 +7,7 @@ Attributes:
7
7
  credentials, if they aren't in the config file or environment.
8
8
 
9
9
  """
10
+
10
11
  import argparse
11
12
  import logging
12
13
  import os
@@ -216,7 +216,10 @@ DEFAULT_CONFIG: immutabledict[str, Any] = immutabledict(
216
216
  "comm-2/decision-gcp",
217
217
  "comm-3/decision-gcp",
218
218
  ),
219
- "translations": ("translations-1/decision-gcp",),
219
+ "translations": (
220
+ "translations-1/decision",
221
+ "translations-1/decision-gcp",
222
+ ),
220
223
  "xpi": (
221
224
  "xpi-1/decision",
222
225
  "xpi-3/decision",
@@ -289,7 +292,10 @@ DEFAULT_CONFIG: immutabledict[str, Any] = immutabledict(
289
292
  "comm-2/images-gcp",
290
293
  "comm-3/images-gcp",
291
294
  ),
292
- "translations": ("translations-1/images-gcp",),
295
+ "translations": (
296
+ "translations-1/images",
297
+ "translations-1/images-gcp",
298
+ ),
293
299
  "xpi": (
294
300
  "xpi-1/images",
295
301
  "xpi-3/images",
@@ -10,6 +10,7 @@ Attributes:
10
10
  DEFAULT_MAX_CONCURRENT_DOWNLOADS (int): default max concurrent downloads
11
11
 
12
12
  """
13
+
13
14
  import asyncio
14
15
  import json
15
16
  import logging
@@ -5,6 +5,7 @@ Attributes:
5
5
  log (logging.Logger): the log object for this module.
6
6
 
7
7
  """
8
+
8
9
  import logging
9
10
  import os
10
11
 
@@ -719,12 +719,8 @@ async def download_cot_artifact(chain, task_id, path):
719
719
  link = chain.get_link(task_id)
720
720
  log.debug("Verifying {} is in {} cot artifacts...".format(path, task_id))
721
721
  if not link.cot:
722
- log.warning(
723
- 'Chain of Trust for "{}" in {} does not exist. See above log for more details. \
724
- Skipping download of this artifact'.format(
725
- path, task_id
726
- )
727
- )
722
+ log.warning('Chain of Trust for "{}" in {} does not exist. See above log for more details. \
723
+ Skipping download of this artifact'.format(path, task_id))
728
724
  return
729
725
 
730
726
  if path not in link.cot["artifacts"]:
@@ -1175,7 +1171,7 @@ async def _get_additional_git_cron_jsone_context(decision_link):
1175
1171
  "sender": {"login": user},
1176
1172
  },
1177
1173
  # Taskgraph cron contexts mirror hg-push contexts
1178
- "repository": {"url": repo, "project": repo_name, "level": await get_scm_level(decision_link.context, repo_name)},
1174
+ "repository": {"url": repo, "project": repo_name, "level": await get_scm_level(decision_link.context, repo_name), "type": "git"},
1179
1175
  "push": {"revision": revision, "branch": branch},
1180
1176
  }
1181
1177
 
@@ -1363,8 +1359,10 @@ async def populate_jsone_context(chain, parent_link, decision_link, tasks_for):
1363
1359
  jsone_context.update(await _get_additional_hg_action_jsone_context(parent_link, decision_link))
1364
1360
  elif tasks_for == "hg-push":
1365
1361
  jsone_context.update(await _get_additional_hg_push_jsone_context(parent_link, decision_link))
1362
+ jsone_context["repository"]["type"] = "hg"
1366
1363
  elif tasks_for == "cron":
1367
1364
  jsone_context.update(await _get_additional_hg_cron_jsone_context(parent_link, decision_link))
1365
+ jsone_context["repository"]["type"] = "hg"
1368
1366
  else:
1369
1367
  raise CoTError('Unknown tasks_for "{}" for hg cot_product "{}"!'.format(tasks_for, chain.context.config["cot_product"]))
1370
1368
  else:
@@ -2101,12 +2099,11 @@ def verify_cot_cmdln(args=None, event_loop=None):
2101
2099
  ``sys.argv[1:]`` . Defaults to None.
2102
2100
 
2103
2101
  event_loop (asyncio.events.AbstractEventLoop): the event loop to use.
2104
- If ``None``, use ``asyncio.get_event_loop()``. Defaults to ``None``.
2102
+ If ``None``, use ``asyncio.new_event_loop()``. Defaults to ``None``.
2105
2103
 
2106
2104
  """
2107
2105
  args = args or sys.argv[1:]
2108
- parser = argparse.ArgumentParser(
2109
- description="""Verify a given task's chain of trust.
2106
+ parser = argparse.ArgumentParser(description="""Verify a given task's chain of trust.
2110
2107
 
2111
2108
  Given a task's `task_id`, get its task definition, then trace its chain of
2112
2109
  trust back to the tree. This doesn't verify chain of trust artifact signatures,
@@ -2118,8 +2115,7 @@ To use, first either set your taskcluster creds in your env http://bit.ly/2eDMa6
2118
2115
  or in the CREDS_FILES http://bit.ly/2fVMu0A
2119
2116
 
2120
2117
  If you are verifying against a private github repo, please also set in environment
2121
- SCRIPTWORKER_GITHUB_OAUTH_TOKEN to an OAUTH token with read permissions to the repo"""
2122
- )
2118
+ SCRIPTWORKER_GITHUB_OAUTH_TOKEN to an OAUTH token with read permissions to the repo""")
2123
2119
  parser.add_argument("task_id", help="the task id to test")
2124
2120
  parser.add_argument("--task-type", help="the task type to test", choices=sorted(get_valid_task_types().keys()), required=True)
2125
2121
  parser.add_argument("--cleanup", help="clean up the temp dir afterwards", dest="cleanup", action="store_true", default=False)
@@ -2133,7 +2129,7 @@ SCRIPTWORKER_GITHUB_OAUTH_TOKEN to an OAUTH token with read permissions to the r
2133
2129
  level = logging.DEBUG if opts.verbose else logging.INFO
2134
2130
  log.setLevel(level)
2135
2131
  logging.basicConfig(level=level)
2136
- event_loop = event_loop or asyncio.get_event_loop()
2132
+ event_loop = event_loop or asyncio.new_event_loop()
2137
2133
  if not opts.cleanup:
2138
2134
  log.info("Artifacts will be in {}".format(tmp))
2139
2135
  try:
@@ -2182,19 +2178,17 @@ def create_test_workdir(args=None, event_loop=None):
2182
2178
  ``sys.argv[1:]`` . Defaults to None.
2183
2179
 
2184
2180
  event_loop (asyncio.events.AbstractEventLoop): the event loop to use.
2185
- If ``None``, use ``asyncio.get_event_loop()``. Defaults to ``None``.
2181
+ If ``None``, use ``asyncio.new_event_loop()``. Defaults to ``None``.
2186
2182
 
2187
2183
  """
2188
2184
  args = args or sys.argv[1:]
2189
- parser = argparse.ArgumentParser(
2190
- description="""Populate a test `work_dir`.
2185
+ parser = argparse.ArgumentParser(description="""Populate a test `work_dir`.
2191
2186
 
2192
2187
  Given a scriptworker task's `task_id`, get its task definition, write it to
2193
2188
  `./work/task.json`, then download its `upstreamArtifacts` and put them in
2194
2189
  `./work/cot/TASK_ID/PATH`.
2195
2190
 
2196
- This is helpful in manually testing a *script run."""
2197
- )
2191
+ This is helpful in manually testing a *script run.""")
2198
2192
  parser.add_argument("--path", help="relative path to the work_dir", default="work")
2199
2193
  parser.add_argument("--overwrite", help="overwrite an existing work_dir", action="store_true")
2200
2194
  parser.add_argument("task_id", help="the task id to test")
@@ -2209,6 +2203,6 @@ This is helpful in manually testing a *script run."""
2209
2203
  sys.exit(1)
2210
2204
  rm(opts.path)
2211
2205
  makedirs(opts.path)
2212
- event_loop = event_loop or asyncio.get_event_loop()
2206
+ event_loop = event_loop or asyncio.new_event_loop()
2213
2207
  event_loop.run_until_complete(_async_create_test_workdir(opts.task_id, opts.path))
2214
2208
  log.info("Done.")
@@ -5,6 +5,7 @@ Attributes:
5
5
  log (logging.Logger): the log object for the module
6
6
 
7
7
  """
8
+
8
9
  import argparse
9
10
  import base64
10
11
  import functools
@@ -140,13 +141,11 @@ def verify_ed25519_signature_cmdln(args=None, exception=SystemExit):
140
141
 
141
142
  """
142
143
  args = args or sys.argv[1:]
143
- parser = argparse.ArgumentParser(
144
- description="""Verify an ed25519 signature from the command line.
144
+ parser = argparse.ArgumentParser(description="""Verify an ed25519 signature from the command line.
145
145
 
146
146
  Given a file and its detached signature, verify that it has been signed with
147
147
  a valid key. This key can be specified on the command line; otherwise we'll
148
- default to ``config['ed25519_public_keys']``."""
149
- )
148
+ default to ``config['ed25519_public_keys']``.""")
150
149
  parser.add_argument("--pubkey", help="path to a base64-encoded ed25519 pubkey, optional")
151
150
  parser.add_argument("file_path")
152
151
  parser.add_argument("sig_path")
@@ -1,9 +1,9 @@
1
1
  """GitHub helper functions."""
2
2
 
3
+ import asyncio
3
4
  import logging
4
5
  import re
5
6
 
6
- from aiomemoizettl import memoize_ttl
7
7
  from github3 import GitHub
8
8
  from github3.exceptions import GitHubException
9
9
 
@@ -136,24 +136,38 @@ class GitHubRepository:
136
136
  return html_text != ""
137
137
 
138
138
 
139
- # TODO Use memoize_ttl() as decorator once https://github.com/michalc/aiomemoizettl/issues/2 is done
140
- async def _fetch_github_branch_commits_data_helper(context, repo_html_url, revision):
141
- url = "/".join((repo_html_url.rstrip("/"), "branch_commits", revision))
142
- log.info('Cache does not exist for URL "{}" (in this context), fetching it...'.format(url))
143
- html_text = await retry_request(context, url)
144
- return html_text.strip()
139
+ _BRANCH_COMMITS_CACHE_TTL_IN_SECONDS = 10 * 60 # 10 minutes
140
+ _BRANCH_COMMITS_CACHE = {}
145
141
 
146
142
 
147
- # XXX memoize_ttl() uses all function parameters to create a key that stores its cache.
148
- # This means new contexts cannot use the memoized value, even though they're calling the same
149
- # repo and revision. jlorenzo tried to take the context out of the memoize_ttl() call, but
150
- # whenever the cache is invalidated, request() doesn't work anymore because the session carried
151
- # by the context has been long closed.
152
- # Therefore, the defined TTL has 2 purposes:
153
- # a. it memoizes calls for the time of a single cot_verify() run
154
- # b. it clears the cache automatically, so we don't have to manually invalidate it.
155
- _BRANCH_COMMITS_CACHE_TTL_IN_SECONDS = 10 * 60 # 10 minutes
156
- _fetch_github_branch_commits_data = memoize_ttl(_fetch_github_branch_commits_data_helper, get_ttl=lambda _: _BRANCH_COMMITS_CACHE_TTL_IN_SECONDS)
143
+ async def _fetch_github_branch_commits_data(context, repo_html_url, revision):
144
+ # Include context identity because different contexts carry
145
+ # different HTTP sessions that may be closed independently.
146
+ cache_key = (id(context), repo_html_url, revision)
147
+
148
+ if cache_key in _BRANCH_COMMITS_CACHE:
149
+ return await _BRANCH_COMMITS_CACHE[cache_key]
150
+
151
+ future = asyncio.get_running_loop().create_future()
152
+ _BRANCH_COMMITS_CACHE[cache_key] = future
153
+
154
+ try:
155
+ url = "/".join((repo_html_url.rstrip("/"), "branch_commits", revision))
156
+ html_text = await retry_request(context, url)
157
+ result = html_text.strip()
158
+ future.set_result(result)
159
+ asyncio.get_running_loop().call_later(
160
+ _BRANCH_COMMITS_CACHE_TTL_IN_SECONDS,
161
+ _BRANCH_COMMITS_CACHE.pop,
162
+ cache_key,
163
+ None,
164
+ )
165
+ except BaseException as e:
166
+ _BRANCH_COMMITS_CACHE.pop(cache_key, None)
167
+ future.set_exception(e)
168
+ raise
169
+
170
+ return result
157
171
 
158
172
 
159
173
  def is_github_url(url):
@@ -5,6 +5,7 @@ Attributes:
5
5
  log (logging.Logger): the log object for this module.
6
6
 
7
7
  """
8
+
8
9
  import asyncio
9
10
  import logging
10
11
  import logging.handlers
@@ -5,6 +5,7 @@ Attributes:
5
5
  log (logging.Logger): the log object for the module
6
6
 
7
7
  """
8
+
8
9
  import asyncio
9
10
  import functools
10
11
  import hashlib
@@ -5,6 +5,7 @@ Attributes:
5
5
  log (logging.Logger): the log object for the module.
6
6
 
7
7
  """
8
+
8
9
  import asyncio
9
10
  import logging
10
11
  import signal
@@ -48,6 +48,10 @@ commands=
48
48
  commands=
49
49
  py.test -k test_verify_production_cot --random-order-bucket=none
50
50
 
51
+ [testenv:py314-cot]
52
+ commands=
53
+ py.test -k test_verify_production_cot --random-order-bucket=none
54
+
51
55
  [testenv:check]
52
56
  skip_install = true
53
57
  commands =
File without changes
File without changes
File without changes