scriptworker 63.1.0__tar.gz → 63.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 (30) hide show
  1. {scriptworker-63.1.0 → scriptworker-63.2.0}/HISTORY.rst +14 -0
  2. {scriptworker-63.1.0 → scriptworker-63.2.0}/PKG-INFO +1 -1
  3. {scriptworker-63.1.0 → scriptworker-63.2.0}/pyproject.toml +1 -1
  4. {scriptworker-63.1.0 → scriptworker-63.2.0}/src/scriptworker/artifacts.py +23 -0
  5. {scriptworker-63.1.0 → scriptworker-63.2.0}/src/scriptworker/constants.py +12 -1
  6. {scriptworker-63.1.0 → scriptworker-63.2.0}/src/scriptworker/context.py +6 -6
  7. {scriptworker-63.1.0 → scriptworker-63.2.0}/src/scriptworker/cot/verify.py +3 -6
  8. {scriptworker-63.1.0 → scriptworker-63.2.0}/src/scriptworker/log.py +13 -0
  9. {scriptworker-63.1.0 → scriptworker-63.2.0}/src/scriptworker/task.py +3 -2
  10. {scriptworker-63.1.0 → scriptworker-63.2.0}/src/scriptworker/worker.py +18 -2
  11. {scriptworker-63.1.0 → scriptworker-63.2.0}/.gitignore +0 -0
  12. {scriptworker-63.1.0 → scriptworker-63.2.0}/CONTRIBUTING.rst +0 -0
  13. {scriptworker-63.1.0 → scriptworker-63.2.0}/LICENSE +0 -0
  14. {scriptworker-63.1.0 → scriptworker-63.2.0}/README.rst +0 -0
  15. {scriptworker-63.1.0 → scriptworker-63.2.0}/renovate.json +0 -0
  16. {scriptworker-63.1.0 → scriptworker-63.2.0}/scripts/gen_ed25519_key.py +0 -0
  17. {scriptworker-63.1.0 → scriptworker-63.2.0}/scriptworker.yaml.tmpl +0 -0
  18. {scriptworker-63.1.0 → scriptworker-63.2.0}/src/scriptworker/__init__.py +0 -0
  19. {scriptworker-63.1.0 → scriptworker-63.2.0}/src/scriptworker/client.py +0 -0
  20. {scriptworker-63.1.0 → scriptworker-63.2.0}/src/scriptworker/config.py +0 -0
  21. {scriptworker-63.1.0 → scriptworker-63.2.0}/src/scriptworker/cot/__init__.py +0 -0
  22. {scriptworker-63.1.0 → scriptworker-63.2.0}/src/scriptworker/cot/generate.py +0 -0
  23. {scriptworker-63.1.0 → scriptworker-63.2.0}/src/scriptworker/data/cot_v1_schema.json +0 -0
  24. {scriptworker-63.1.0 → scriptworker-63.2.0}/src/scriptworker/data/scriptworker_task_schema.json +0 -0
  25. {scriptworker-63.1.0 → scriptworker-63.2.0}/src/scriptworker/ed25519.py +0 -0
  26. {scriptworker-63.1.0 → scriptworker-63.2.0}/src/scriptworker/exceptions.py +0 -0
  27. {scriptworker-63.1.0 → scriptworker-63.2.0}/src/scriptworker/github.py +0 -0
  28. {scriptworker-63.1.0 → scriptworker-63.2.0}/src/scriptworker/task_process.py +0 -0
  29. {scriptworker-63.1.0 → scriptworker-63.2.0}/src/scriptworker/utils.py +0 -0
  30. {scriptworker-63.1.0 → scriptworker-63.2.0}/tox.ini +0 -0
@@ -4,6 +4,20 @@ 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
+ 63.2.0 - 2026-06-18
8
+ -------------------
9
+
10
+ Added
11
+ ~~~~~
12
+ - When Chain of Trust verification fails, the log is also exposed as public/logs/live_backing.log
13
+ (https://bugzilla.mozilla.org/show_bug.cgi?id=2039779)
14
+ - Values for ESR 153 to scriptworker constants (https://bugzilla.mozilla.org/show_bug.cgi?id=2046216)
15
+
16
+ Fixed
17
+ ~~~~~
18
+ - The TASKCLUSTER_CREDENTIALS_FD feature uses a regular temporary file rather than a memfd, to
19
+ make things work again on macOS
20
+
7
21
  63.1.0 - 2026-05-26
8
22
  -------------------
9
23
 
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: scriptworker
3
- Version: 63.1.0
3
+ Version: 63.2.0
4
4
  Summary: Taskcluster scriptworker
5
5
  Author-email: Mozilla Release Engineering <releng@mozilla.com>
6
6
  License-File: LICENSE
@@ -1,6 +1,6 @@
1
1
  [project]
2
2
  name = "scriptworker"
3
- version = "63.1.0"
3
+ version = "63.2.0"
4
4
  description = "Taskcluster scriptworker"
5
5
  authors = [
6
6
  { name = "Mozilla Release Engineering", email = "releng@mozilla.com" },
@@ -183,6 +183,29 @@ async def create_artifact(context, path, target_path, content_type, content_enco
183
183
  raise ScriptWorkerRetryException("Bad status {}".format(resp.status))
184
184
 
185
185
 
186
+ async def create_link_artifact(context, target_path, link_to, content_type, expires=None):
187
+ """Create a Taskcluster link artifact that redirects to another artifact in the same task.
188
+
189
+ Args:
190
+ context (scriptworker.context.Context): the scriptworker context.
191
+ target_path (str): the artifact name to create (e.g., ``public/logs/live_backing.log``).
192
+ link_to (str): the artifact name to link to (e.g., ``public/logs/chain_of_trust.log``).
193
+ content_type (str): Content type (MIME type) of the linked artifact.
194
+ expires (str, optional): ISO datestring of when the artifact expires.
195
+ Defaults to ``context.task["expires"]``.
196
+
197
+ """
198
+ payload = {
199
+ "storageType": "link",
200
+ "expires": expires or get_expiration_arrow(context).isoformat(),
201
+ "contentType": content_type,
202
+ "artifact": link_to,
203
+ }
204
+ args = [get_task_id(context.claim_task), get_run_id(context.claim_task), target_path, payload]
205
+ log.info("Creating link artifact {} -> {}".format(target_path, link_to))
206
+ await context.temp_queue.createArtifact(*args)
207
+
208
+
186
209
  def _craft_artifact_put_headers(content_type, encoding=None):
187
210
  log.debug("{} {}".format(content_type, encoding))
188
211
  headers = {aiohttp.hdrs.CONTENT_TYPE: content_type}
@@ -559,6 +559,7 @@ DEFAULT_CONFIG: immutabledict[str, Any] = immutabledict(
559
559
  "project:releng:lando:repo:release": "release",
560
560
  "project:releng:lando:repo:esr115": "esr115",
561
561
  "project:releng:lando:repo:esr140": "esr140",
562
+ "project:releng:lando:repo:esr153": "esr153",
562
563
  }
563
564
  ),
564
565
  "glean": immutabledict({"project:mozilla:glean:releng:beetmover:bucket:maven-production": "glean-repo"}),
@@ -645,6 +646,7 @@ DEFAULT_CONFIG: immutabledict[str, Any] = immutabledict(
645
646
  "/releases/mozilla-release",
646
647
  "/releases/mozilla-esr115",
647
648
  "/releases/mozilla-esr140",
649
+ "/releases/mozilla-esr153",
648
650
  "/projects/birch",
649
651
  # Bug 1821839 hneiva using to test hardened signing
650
652
  "/projects/maple",
@@ -654,6 +656,7 @@ DEFAULT_CONFIG: immutabledict[str, Any] = immutabledict(
654
656
  "/releases/mozilla-release",
655
657
  "/releases/mozilla-esr115",
656
658
  "/releases/mozilla-esr140",
659
+ "/releases/mozilla-esr153",
657
660
  "/projects/birch",
658
661
  "/projects/maple",
659
662
  "/integration/autoland",
@@ -667,9 +670,11 @@ DEFAULT_CONFIG: immutabledict[str, Any] = immutabledict(
667
670
  ),
668
671
  "esr115": ("/releases/mozilla-esr115",),
669
672
  "esr140": ("/releases/mozilla-esr140",),
673
+ "esr153": ("/releases/mozilla-esr153",),
670
674
  "esr": (
671
675
  "/releases/mozilla-esr115",
672
676
  "/releases/mozilla-esr140",
677
+ "/releases/mozilla-esr153",
673
678
  ),
674
679
  "nightly": ("/mozilla-central",),
675
680
  "autoland": ("/integration/autoland",),
@@ -681,6 +686,7 @@ DEFAULT_CONFIG: immutabledict[str, Any] = immutabledict(
681
686
  "/releases/mozilla-release",
682
687
  "/releases/mozilla-esr115",
683
688
  "/releases/mozilla-esr140",
689
+ "/releases/mozilla-esr153",
684
690
  # bug 1845368: a permanent project branch for testing updates
685
691
  "/projects/pine",
686
692
  "/projects/larch",
@@ -693,6 +699,7 @@ DEFAULT_CONFIG: immutabledict[str, Any] = immutabledict(
693
699
  "/releases/mozilla-release",
694
700
  "/releases/mozilla-esr115",
695
701
  "/releases/mozilla-esr140",
702
+ "/releases/mozilla-esr153",
696
703
  ),
697
704
  "all-production-branches-and-autoland": (
698
705
  "/mozilla-central",
@@ -700,6 +707,7 @@ DEFAULT_CONFIG: immutabledict[str, Any] = immutabledict(
700
707
  "/releases/mozilla-release",
701
708
  "/releases/mozilla-esr115",
702
709
  "/releases/mozilla-esr140",
710
+ "/releases/mozilla-esr153",
703
711
  "/integration/autoland",
704
712
  ),
705
713
  }
@@ -728,6 +736,7 @@ DEFAULT_CONFIG: immutabledict[str, Any] = immutabledict(
728
736
  "/releases/comm-release",
729
737
  "/releases/comm-esr115",
730
738
  "/releases/comm-esr140",
739
+ "/releases/comm-esr153",
731
740
  ),
732
741
  "beta": ("/releases/comm-beta",),
733
742
  "release": ("/releases/comm-release",),
@@ -735,14 +744,16 @@ DEFAULT_CONFIG: immutabledict[str, Any] = immutabledict(
735
744
  "/releases/comm-release",
736
745
  "/releases/comm-esr115",
737
746
  "/releases/comm-esr140",
747
+ "/releases/comm-esr153",
738
748
  ),
739
- "esr": ("/releases/comm-esr115", "/releases/comm-esr140"),
749
+ "esr": ("/releases/comm-esr115", "/releases/comm-esr140", "/releases/comm-esr153"),
740
750
  "all-nightly-branches": (
741
751
  "/comm-central",
742
752
  "/releases/comm-beta",
743
753
  "/releases/comm-release",
744
754
  "/releases/comm-esr115",
745
755
  "/releases/comm-esr140",
756
+ "/releases/comm-esr153",
746
757
  ),
747
758
  "nightly": ("/comm-central",),
748
759
  }
@@ -59,7 +59,7 @@ class Context(object):
59
59
 
60
60
  config: Optional[Dict[str, Any]] = None
61
61
  credentials_timestamp: Optional[int] = None
62
- credentials_fd: int = -1
62
+ credentials_file = None
63
63
  proc: Optional[task_process.TaskProcess] = None
64
64
  queue: Optional[Queue] = None
65
65
  session: Optional[aiohttp.ClientSession] = None
@@ -99,8 +99,7 @@ class Context(object):
99
99
  if claim_task:
100
100
  self.task = claim_task["task"]
101
101
  self.verify_task()
102
- # flags=0 to let the child inherit this fd
103
- self.credentials_fd = os.memfd_create("scriptworker_temp_creds", flags=0)
102
+ self.credentials_file = tempfile.TemporaryFile()
104
103
  self.temp_credentials = claim_task["credentials"]
105
104
  path = os.path.join(self.config["work_dir"], "task.json")
106
105
  assert self.task
@@ -108,8 +107,9 @@ class Context(object):
108
107
  else:
109
108
  self.temp_credentials = None
110
109
  self.task = None
111
- os.close(self.credentials_fd)
112
- self.credentials_fd = -1
110
+ if self.credentials_file is not None:
111
+ self.credentials_file.close()
112
+ self.credentials_file = None
113
113
 
114
114
  def verify_task(self) -> None:
115
115
  """Run some task sanity checks on ``self.task``."""
@@ -201,7 +201,7 @@ class Context(object):
201
201
  if credentials:
202
202
  data = json.dumps(credentials, indent=2, sort_keys=True).encode("ascii")
203
203
  # use pwrite so we don't confuse the child by changing the file offset
204
- assert os.pwrite(self.credentials_fd, data, 0) == len(data)
204
+ assert os.pwrite(self.credentials_file.fileno(), data, 0) == len(data)
205
205
 
206
206
  def write_json(self, path: str, contents: Dict[str, Any], message: str) -> None:
207
207
  """Write json to disk.
@@ -41,7 +41,7 @@ from scriptworker.context import Context
41
41
  from scriptworker.ed25519 import ed25519_public_key_from_string, verify_ed25519_signature
42
42
  from scriptworker.exceptions import BaseDownloadError, CoTError, ScriptWorkerEd25519Error
43
43
  from scriptworker.github import GitHubRepository, extract_github_repo_full_name, extract_github_repo_owner_and_name, extract_github_repo_ssh_url
44
- from scriptworker.log import contextual_log_handler
44
+ from scriptworker.log import contextual_log_handler, get_chain_of_trust_log_filename
45
45
  from scriptworker.task import (
46
46
  get_action_callback_name,
47
47
  get_and_check_tasks_for,
@@ -729,10 +729,7 @@ async def download_cot_artifact(chain, task_id, path):
729
729
  link = chain.get_link(task_id)
730
730
  log.debug("Verifying {} is in {} cot artifacts...".format(path, task_id))
731
731
  if not link.cot:
732
- log.warning(
733
- 'Chain of Trust for "{}" in {} does not exist. See above log for more details. \
734
- Skipping download of this artifact'.format(path, task_id)
735
- )
732
+ log.warning(f'Chain of Trust for "{path}" in {task_id} does not exist. See above log for more details. Skipping download of this artifact.')
736
733
  return
737
734
 
738
735
  if path not in link.cot["artifacts"]:
@@ -2045,7 +2042,7 @@ async def verify_chain_of_trust(chain, *, check_task=False):
2045
2042
  CoTError: on failure
2046
2043
 
2047
2044
  """
2048
- log_path = os.path.join(chain.context.config["task_log_dir"], "chain_of_trust.log")
2045
+ log_path = get_chain_of_trust_log_filename(chain.context)
2049
2046
  scriptworker_log = logging.getLogger("scriptworker")
2050
2047
  with contextual_log_handler(
2051
2048
  chain.context,
@@ -115,6 +115,19 @@ def get_log_filename(context: Any) -> str:
115
115
  return os.path.join(context.config["task_log_dir"], "live_backing.log")
116
116
 
117
117
 
118
+ def get_chain_of_trust_log_filename(context: Any) -> str:
119
+ """Get the chain of trust verification log file path.
120
+
121
+ Args:
122
+ context (scriptworker.context.Context): the scriptworker context.
123
+
124
+ Returns:
125
+ string: chain of trust log file path
126
+
127
+ """
128
+ return os.path.join(context.config["task_log_dir"], "chain_of_trust.log")
129
+
130
+
118
131
  @contextmanager
119
132
  def get_log_filehandle(context: Any) -> Iterator[IO[str]]:
120
133
  """Open the log and error filehandles.
@@ -672,7 +672,8 @@ async def run_task(context, to_cancellable_process):
672
672
  env["TASK_ID"] = context.task_id or "None"
673
673
  env["RUN_ID"] = str(get_run_id(context.claim_task))
674
674
  env["TASKCLUSTER_ROOT_URL"] = context.config["taskcluster_root_url"]
675
- env["TASKCLUSTER_CREDENTIALS_FD"] = str(context.credentials_fd)
675
+ credentials_fd = context.credentials_file.fileno()
676
+ env["TASKCLUSTER_CREDENTIALS_FD"] = str(credentials_fd)
676
677
  kwargs = {
677
678
  "stdout": PIPE,
678
679
  "stderr": PIPE,
@@ -680,7 +681,7 @@ async def run_task(context, to_cancellable_process):
680
681
  "close_fds": True,
681
682
  "preexec_fn": lambda: os.setsid(),
682
683
  "env": env,
683
- "pass_fds": (context.credentials_fd,),
684
+ "pass_fds": (credentials_fd,),
684
685
  } # pragma: no branch
685
686
 
686
687
  timeout = get_task_maxruntime(context.task, context.config["task_max_timeout"])
@@ -8,6 +8,7 @@ Attributes:
8
8
 
9
9
  import asyncio
10
10
  import logging
11
+ import os
11
12
  import signal
12
13
  import socket
13
14
  import sys
@@ -17,12 +18,13 @@ from typing import Any
17
18
  import aiohttp
18
19
  import arrow
19
20
 
20
- from scriptworker.artifacts import upload_artifacts
21
+ from scriptworker.artifacts import create_link_artifact, upload_artifacts
21
22
  from scriptworker.config import get_context_from_cmdln
22
23
  from scriptworker.constants import STATUSES
23
24
  from scriptworker.cot.generate import generate_cot
24
25
  from scriptworker.cot.verify import ChainOfTrust, verify_chain_of_trust
25
26
  from scriptworker.exceptions import ScriptWorkerException, WorkerShutdownDuringTask
27
+ from scriptworker.log import get_chain_of_trust_log_filename
26
28
  from scriptworker.task import claim_work, complete_task, prepare_to_run_task, reclaim_task, run_task, worst_level
27
29
  from scriptworker.task_process import TaskProcess
28
30
  from scriptworker.utils import cleanup, filepaths_in_dir, scriptworker_session
@@ -53,7 +55,21 @@ async def do_run_task(context, run_cancellable, to_cancellable_process):
53
55
  try:
54
56
  if context.config["verify_chain_of_trust"]:
55
57
  chain = ChainOfTrust(context, context.config["cot_job_type"])
56
- await run_cancellable(verify_chain_of_trust(chain))
58
+ try:
59
+ await run_cancellable(verify_chain_of_trust(chain))
60
+ except Exception:
61
+ # Point live_backing.log at chain_of_trust.log so Treeherder parses the CoT failure
62
+ if os.path.exists(get_chain_of_trust_log_filename(context)):
63
+ try:
64
+ await create_link_artifact(
65
+ context,
66
+ target_path="public/logs/live_backing.log",
67
+ link_to="public/logs/chain_of_trust.log",
68
+ content_type="text/plain",
69
+ )
70
+ except Exception as link_exc:
71
+ log.exception("Failed to create live_backing.log link artifact: {}".format(link_exc))
72
+ raise
57
73
  status = await run_task(context, to_cancellable_process)
58
74
  generate_cot(context)
59
75
  except asyncio.CancelledError:
File without changes
File without changes
File without changes
File without changes