scriptworker 60.0.0__tar.gz → 60.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 (42) hide show
  1. {scriptworker-60.0.0 → scriptworker-60.2.0}/HISTORY.rst +24 -0
  2. scriptworker-60.2.0/PKG-INFO +86 -0
  3. {scriptworker-60.0.0 → scriptworker-60.2.0}/setup.py +6 -0
  4. {scriptworker-60.0.0 → scriptworker-60.2.0}/src/scriptworker/artifacts.py +28 -2
  5. {scriptworker-60.0.0 → scriptworker-60.2.0}/src/scriptworker/client.py +2 -3
  6. {scriptworker-60.0.0 → scriptworker-60.2.0}/src/scriptworker/constants.py +27 -9
  7. {scriptworker-60.0.0 → scriptworker-60.2.0}/src/scriptworker/context.py +5 -3
  8. {scriptworker-60.0.0 → scriptworker-60.2.0}/src/scriptworker/cot/verify.py +44 -12
  9. {scriptworker-60.0.0 → scriptworker-60.2.0}/src/scriptworker/task.py +18 -2
  10. {scriptworker-60.0.0 → scriptworker-60.2.0}/src/scriptworker/utils.py +7 -0
  11. {scriptworker-60.0.0 → scriptworker-60.2.0}/src/scriptworker/version.py +1 -1
  12. {scriptworker-60.0.0 → scriptworker-60.2.0}/src/scriptworker/worker.py +2 -2
  13. scriptworker-60.2.0/src/scriptworker.egg-info/PKG-INFO +86 -0
  14. {scriptworker-60.0.0 → scriptworker-60.2.0}/src/scriptworker.egg-info/entry_points.txt +0 -1
  15. {scriptworker-60.0.0 → scriptworker-60.2.0}/version.json +2 -2
  16. scriptworker-60.0.0/PKG-INFO +0 -19
  17. scriptworker-60.0.0/src/scriptworker.egg-info/PKG-INFO +0 -19
  18. {scriptworker-60.0.0 → scriptworker-60.2.0}/CONTRIBUTING.rst +0 -0
  19. {scriptworker-60.0.0 → scriptworker-60.2.0}/LICENSE +0 -0
  20. {scriptworker-60.0.0 → scriptworker-60.2.0}/MANIFEST.in +0 -0
  21. {scriptworker-60.0.0 → scriptworker-60.2.0}/README.rst +0 -0
  22. {scriptworker-60.0.0 → scriptworker-60.2.0}/pyproject.toml +0 -0
  23. {scriptworker-60.0.0 → scriptworker-60.2.0}/requirements.txt +0 -0
  24. {scriptworker-60.0.0 → scriptworker-60.2.0}/scripts/gen_ed25519_key.py +0 -0
  25. {scriptworker-60.0.0 → scriptworker-60.2.0}/scriptworker.yaml.tmpl +0 -0
  26. {scriptworker-60.0.0 → scriptworker-60.2.0}/setup.cfg +0 -0
  27. {scriptworker-60.0.0 → scriptworker-60.2.0}/src/scriptworker/__init__.py +0 -0
  28. {scriptworker-60.0.0 → scriptworker-60.2.0}/src/scriptworker/config.py +0 -0
  29. {scriptworker-60.0.0 → scriptworker-60.2.0}/src/scriptworker/cot/__init__.py +0 -0
  30. {scriptworker-60.0.0 → scriptworker-60.2.0}/src/scriptworker/cot/generate.py +0 -0
  31. {scriptworker-60.0.0 → scriptworker-60.2.0}/src/scriptworker/data/cot_v1_schema.json +0 -0
  32. {scriptworker-60.0.0 → scriptworker-60.2.0}/src/scriptworker/ed25519.py +0 -0
  33. {scriptworker-60.0.0 → scriptworker-60.2.0}/src/scriptworker/exceptions.py +0 -0
  34. {scriptworker-60.0.0 → scriptworker-60.2.0}/src/scriptworker/github.py +0 -0
  35. {scriptworker-60.0.0 → scriptworker-60.2.0}/src/scriptworker/log.py +0 -0
  36. {scriptworker-60.0.0 → scriptworker-60.2.0}/src/scriptworker/task_process.py +0 -0
  37. {scriptworker-60.0.0 → scriptworker-60.2.0}/src/scriptworker.egg-info/SOURCES.txt +0 -0
  38. {scriptworker-60.0.0 → scriptworker-60.2.0}/src/scriptworker.egg-info/dependency_links.txt +0 -0
  39. {scriptworker-60.0.0 → scriptworker-60.2.0}/src/scriptworker.egg-info/not-zip-safe +0 -0
  40. {scriptworker-60.0.0 → scriptworker-60.2.0}/src/scriptworker.egg-info/requires.txt +0 -0
  41. {scriptworker-60.0.0 → scriptworker-60.2.0}/src/scriptworker.egg-info/top_level.txt +0 -0
  42. {scriptworker-60.0.0 → scriptworker-60.2.0}/tox.ini +0 -0
@@ -4,6 +4,30 @@ 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
+ 60.2.0 - 2024-07-12
8
+ -------------------
9
+
10
+ Added
11
+ ~~~~~
12
+ - Set User-Agent in http requests (#661)
13
+ - Support github-pull-request-untrusted in CoT verification (https://bugzilla.mozilla.org/show_bug.cgi?id=1906748)
14
+
15
+ Fixed
16
+ ~~~~~
17
+ - Pull project from the default branch in projects.yml (#665)
18
+
19
+ 60.1.0 - 2024-06-24
20
+ -------------------
21
+
22
+ Added
23
+ ~~~~~
24
+ - Values for Firefox Translations training repository scriptworker constants
25
+ - Support for deferring upstream artifact selection until runtime
26
+
27
+ Fixed
28
+ ~~~~~
29
+ - Ensure GitHub pull request head sha is set correctly during chain of trust verification
30
+
7
31
  60.0.0 - 2024-05-27
8
32
  -------------------
9
33
 
@@ -0,0 +1,86 @@
1
+ Metadata-Version: 2.1
2
+ Name: scriptworker
3
+ Version: 60.2.0
4
+ Summary: TaskCluster Script Worker
5
+ Home-page: https://github.com/mozilla-releng/scriptworker
6
+ Author: Mozilla Release Engineering
7
+ Author-email: release+python@mozilla.com
8
+ License: MPL 2.0
9
+ Classifier: Intended Audience :: Developers
10
+ Classifier: Natural Language :: English
11
+ Classifier: Programming Language :: Python :: 3.8
12
+ Classifier: Programming Language :: Python :: 3.9
13
+ Classifier: Programming Language :: Python :: 3.10
14
+ Requires-Python: >=3.7
15
+ License-File: LICENSE
16
+
17
+ ===================
18
+ Scriptworker Readme
19
+ ===================
20
+
21
+ .. image:: https://travis-ci.org/mozilla-releng/scriptworker.svg?branch=master
22
+ :target: https://travis-ci.org/mozilla-releng/scriptworker
23
+
24
+ .. image:: https://coveralls.io/repos/github/mozilla-releng/scriptworker/badge.svg?branch=master
25
+ :target: https://coveralls.io/github/mozilla-releng/scriptworker?branch=master
26
+
27
+ .. image:: https://readthedocs.org/projects/scriptworker/badge/?version=latest
28
+ :target: http://scriptworker.readthedocs.io/en/latest/?badge=latest
29
+ :alt: Documentation Status
30
+
31
+ Scriptworker implements the `TaskCluster worker model`_, then launches a pre-defined script.
32
+
33
+ .. _TaskCluster worker model: https://firefox-ci-tc.services.mozilla.com/docs/reference/platform/queue/worker-interaction
34
+
35
+ This worker was designed for `Releng processes`_ that need specific, limited, and pre-defined capabilities.
36
+
37
+ .. _Releng processes: https://bugzilla.mozilla.org/show_bug.cgi?id=1245837
38
+
39
+ Free software: MPL2 License
40
+
41
+ -----
42
+ Usage
43
+ -----
44
+ * Create a config file. By default scriptworker will look in ``./scriptworker.yaml``, but this config path can be specified as the first and only commandline argument. There is an `example config file`_, and all config items are specified in `scriptworker.constants.DEFAULT_CONFIG`_.
45
+
46
+ .. _example config file: https://github.com/mozilla-releng/scriptworker/blob/master/scriptworker.yaml.tmpl
47
+ .. _scriptworker.constants.DEFAULT_CONFIG: https://github.com/mozilla-releng/scriptworker/blob/master/src/scriptworker/constants.py
48
+
49
+ Credentials can live in ``./scriptworker.yaml``, ``./secrets.json``, ``~/.scriptworker``.
50
+
51
+ * Launch: ``scriptworker [config_path]``
52
+
53
+ -------
54
+ Testing
55
+ -------
56
+
57
+ Without integration tests install tox, then
58
+
59
+ ``NO_CREDENTIALS_TESTS=1 tox -e py36``
60
+
61
+ Without any tests connecting to the net, then ``NO_TESTS_OVER_WIRE=1 tox -e py36``
62
+
63
+ With integration tests, first create a client in the Taskcluster UI with the scopes::
64
+
65
+ queue:cancel-task:test-dummy-scheduler/*
66
+ queue:claim-work:test-dummy-provisioner/dummy-worker-*
67
+ queue:create-task:lowest:test-dummy-provisioner/dummy-worker-*
68
+ queue:define-task:test-dummy-provisioner/dummy-worker-*
69
+ queue:get-artifact:SampleArtifacts/_/X.txt
70
+ queue:scheduler-id:test-dummy-scheduler
71
+ queue:schedule-task:test-dummy-scheduler/*
72
+ queue:task-group-id:test-dummy-scheduler/*
73
+ queue:worker-id:test-dummy-workers/dummy-worker-*
74
+
75
+ Then generate a no priviledge personal access token in Github for the scriptworker_github_token (to avoid rate limiting) and create a ``./secrets.json`` or ``~/.scriptworker`` that looks like::
76
+
77
+ {
78
+ "integration_credentials": {
79
+ "clientId": "...",
80
+ "accessToken": "...",
81
+ }
82
+ "scriptworker_github_token": "..."
83
+ }
84
+
85
+
86
+ then to run all tests: ``tox``
@@ -7,6 +7,8 @@ import sys
7
7
  from setuptools import setup
8
8
  from setuptools.command.test import test as TestCommand
9
9
 
10
+ project_dir = os.path.abspath(os.path.dirname(__file__))
11
+
10
12
  if {"register", "upload"}.intersection(set(sys.argv)):
11
13
  print(
12
14
  " ***** WARNING *****\n"
@@ -39,6 +41,9 @@ with open(PATH) as filehandle:
39
41
  with open(os.path.join(os.path.abspath(os.path.dirname(__file__)), "requirements.txt")) as f:
40
42
  install_requires = f.readlines()
41
43
 
44
+ with open(os.path.join(project_dir, "README.rst")) as fh:
45
+ long_description = fh.read()
46
+
42
47
 
43
48
  class Tox(TestCommand):
44
49
  """http://bit.ly/1T0dwvG"""
@@ -71,6 +76,7 @@ setup(
71
76
  name="scriptworker",
72
77
  version=VERSION,
73
78
  description="TaskCluster Script Worker",
79
+ long_description=long_description,
74
80
  author="Mozilla Release Engineering",
75
81
  author_email="release+python@mozilla.com",
76
82
  url="https://github.com/mozilla-releng/scriptworker",
@@ -6,6 +6,7 @@ in S3.
6
6
  """
7
7
 
8
8
  import asyncio
9
+ import fnmatch
9
10
  import gzip
10
11
  import logging
11
12
  import mimetypes
@@ -15,6 +16,7 @@ from pathlib import Path
15
16
  import aiohttp
16
17
  import arrow
17
18
  import async_timeout
19
+ from taskcluster.exceptions import TaskclusterFailure
18
20
 
19
21
  from scriptworker.client import validate_artifact_url
20
22
  from scriptworker.exceptions import DownloadError, ScriptWorkerRetryException, ScriptWorkerTaskException
@@ -221,6 +223,16 @@ def get_artifact_url(context, task_id, path):
221
223
  return url
222
224
 
223
225
 
226
+ # list_latest_artifacts {{{1
227
+ async def list_latest_artifacts(queue, task_id, exception=TaskclusterFailure):
228
+ return await queue.listLatestArtifacts(task_id)
229
+
230
+
231
+ async def retry_list_latest_artifacts(queue, task_id, exception=TaskclusterFailure, **kwargs):
232
+ kwargs.setdefault("retry_exceptions", tuple(set([TaskclusterFailure, exception])))
233
+ return await retry_async(list_latest_artifacts, args=(queue, task_id), kwargs={"exception": exception}, **kwargs)
234
+
235
+
224
236
  # get_expiration_arrow {{{1
225
237
  def get_expiration_arrow(context):
226
238
  """Return an arrow matching `context.task['expires']`.
@@ -321,8 +333,12 @@ def get_upstream_artifacts_full_paths_per_task_id(context):
321
333
  for task_id, paths in task_ids_and_relative_paths:
322
334
  for path in paths:
323
335
  try:
324
- path_to_add = get_and_check_single_upstream_artifact_full_path(context, task_id, path)
325
- add_enumerable_item_to_dict(dict_=upstream_artifacts_full_paths_per_task_id, key=task_id, item=path_to_add)
336
+ if "*" in path:
337
+ for path_to_add in get_artifacts_matching_glob(context, task_id, path):
338
+ add_enumerable_item_to_dict(dict_=upstream_artifacts_full_paths_per_task_id, key=task_id, item=path_to_add)
339
+ else:
340
+ path_to_add = get_and_check_single_upstream_artifact_full_path(context, task_id, path)
341
+ add_enumerable_item_to_dict(dict_=upstream_artifacts_full_paths_per_task_id, key=task_id, item=path_to_add)
326
342
  except ScriptWorkerTaskException:
327
343
  if path in optional_artifacts_per_task_id.get(task_id, []):
328
344
  log.warning('Optional artifact "{}" of task "{}" not found'.format(path, task_id))
@@ -417,3 +433,13 @@ def assert_is_parent(path, parent_dir):
417
433
  p2 = Path(os.path.realpath(parent_dir))
418
434
  if p1 != p2 and p2 not in p1.parents:
419
435
  raise ScriptWorkerTaskException("{} is not under {}!".format(p1, p2))
436
+
437
+
438
+ def get_artifacts_matching_glob(context, task_id, pattern):
439
+ parent_dir = os.path.abspath(os.path.join(context.config["work_dir"], "cot", task_id))
440
+ matching = []
441
+ for root, _, files in os.walk(parent_dir):
442
+ for f in files:
443
+ if fnmatch.fnmatch(f, pattern):
444
+ matching.append(os.path.join(root, f))
445
+ return matching
@@ -17,13 +17,12 @@ from asyncio import AbstractEventLoop
17
17
  from typing import Any, Awaitable, Callable, Dict, List, Match, NoReturn, Optional, Tuple
18
18
  from urllib.parse import unquote
19
19
 
20
- import aiohttp
21
20
  import jsonschema
22
21
 
23
22
  from scriptworker.constants import STATUSES
24
23
  from scriptworker.context import Context
25
24
  from scriptworker.exceptions import ScriptWorkerException, ScriptWorkerTaskException, TaskVerificationError
26
- from scriptworker.utils import load_json_or_yaml, match_url_regex
25
+ from scriptworker.utils import load_json_or_yaml, match_url_regex, scriptworker_session
27
26
 
28
27
  log = logging.getLogger(__name__)
29
28
 
@@ -199,7 +198,7 @@ def _init_logging(context: Any) -> None:
199
198
 
200
199
 
201
200
  async def _handle_asyncio_loop(async_main: Callable[[Any], Awaitable[None]], context: Any) -> None:
202
- async with aiohttp.ClientSession() as session:
201
+ async with scriptworker_session() as session:
203
202
  context.session = session
204
203
  try:
205
204
  await async_main(context)
@@ -140,6 +140,7 @@ DEFAULT_CONFIG: immutabledict[str, Any] = immutabledict(
140
140
  "xpi": "github",
141
141
  "adhoc": "github",
142
142
  "scriptworker": "github",
143
+ "translations": "github",
143
144
  }
144
145
  )
145
146
  }
@@ -172,6 +173,7 @@ DEFAULT_CONFIG: immutabledict[str, Any] = immutabledict(
172
173
  "xpi": ("xpi-1/decision", "xpi-3/decision", "xpi-1/decision-gcp", "xpi-3/decision-gcp"),
173
174
  "adhoc": ("adhoc-1/decision", "adhoc-3/decision", "adhoc-1/decision-gcp", "adhoc-3/decision-gcp"),
174
175
  "scriptworker": ("scriptworker-1/decision", "scriptworker-3/decision", "scriptworker-1/decision-gcp", "scriptworker-3/decision-gcp"),
176
+ "translations": ("translations-1/decision-gcp",),
175
177
  }
176
178
  )
177
179
  }
@@ -190,6 +192,7 @@ DEFAULT_CONFIG: immutabledict[str, Any] = immutabledict(
190
192
  "xpi": ("xpi-1/images", "xpi-3/images", "xpi-1/images-gcp", "xpi-3/images-gcp"),
191
193
  "adhoc": ("adhoc-1/images", "adhoc-3/images", "adhoc-1/images-gcp", "adhoc-3/images-gcp"),
192
194
  "scriptworker": ("scriptworker-1/images", "scriptworker-3/images", "scriptworker-1/images-gcp", "scriptworker-3/images-gcp"),
195
+ "translations": ("translations-1/images-gcp",),
193
196
  }
194
197
  )
195
198
  }
@@ -277,6 +280,15 @@ DEFAULT_CONFIG: immutabledict[str, Any] = immutabledict(
277
280
  }
278
281
  ),
279
282
  ),
283
+ "translations": (
284
+ immutabledict(
285
+ {
286
+ "schemes": ("https", "ssh"),
287
+ "netlocs": ("github.com",),
288
+ "path_regexes": tuple([r"^(?P<path>/mozilla/firefox-translations-training)(/|.git|$)"]),
289
+ }
290
+ ),
291
+ ),
280
292
  }
281
293
  )
282
294
  },
@@ -288,10 +300,8 @@ DEFAULT_CONFIG: immutabledict[str, Any] = immutabledict(
288
300
  "mobile": (
289
301
  "action",
290
302
  "cron",
291
- # On staging releases, level 1 docker images may be built in the pull-request graph
292
303
  "github-pull-request",
293
- # Similarly, docker images can be built on regular push. This is usually the case
294
- # for level 3 images
304
+ "github-pull-request-untrusted",
295
305
  "github-push",
296
306
  "github-release",
297
307
  ),
@@ -299,26 +309,21 @@ DEFAULT_CONFIG: immutabledict[str, Any] = immutabledict(
299
309
  "app-services": (
300
310
  "action",
301
311
  "cron",
302
- # On staging releases, level 1 docker images may be built in the pull-request graph
303
312
  "github-pull-request",
304
- # Similarly, docker images can be built on regular push. This is usually the case
305
- # for level 3 images
306
313
  "github-push",
307
314
  "github-release",
308
315
  ),
309
316
  "glean": (
310
317
  "action",
311
318
  "cron",
312
- # On staging releases, level 1 docker images may be built in the pull-request graph
313
319
  "github-pull-request",
314
- # Similarly, docker images can be built on regular push. This is usually the case
315
- # for level 3 images
316
320
  "github-push",
317
321
  "github-release",
318
322
  ),
319
323
  "xpi": ("action", "cron", "github-pull-request", "github-push", "github-release"),
320
324
  "adhoc": ("action", "github-pull-request", "github-push"),
321
325
  "scriptworker": ("action", "cron", "github-pull-request", "github-push", "github-release"),
326
+ "translations": ("action", "github-pull-request", "github-push"),
322
327
  }
323
328
  )
324
329
  },
@@ -334,6 +339,7 @@ DEFAULT_CONFIG: immutabledict[str, Any] = immutabledict(
334
339
  "xpi": "mozilla-extensions",
335
340
  "adhoc": "mozilla-releng",
336
341
  "scriptworker": "mozilla-releng",
342
+ "translations": "mozilla",
337
343
  }
338
344
  )
339
345
  },
@@ -422,6 +428,11 @@ DEFAULT_CONFIG: immutabledict[str, Any] = immutabledict(
422
428
  "project:scriptworker:pypi:production": "all-production-repos",
423
429
  }
424
430
  ),
431
+ "translations": immutabledict(
432
+ {
433
+ "project:translations:releng:beetmover:bucket:release": "translations-repo",
434
+ }
435
+ ),
425
436
  }
426
437
  )
427
438
  },
@@ -517,6 +528,11 @@ DEFAULT_CONFIG: immutabledict[str, Any] = immutabledict(
517
528
  "all-production-repos": ("/mozilla-releng/scriptworker", "/mozilla-releng/scriptworker-scripts"),
518
529
  }
519
530
  ),
531
+ "translations": immutabledict(
532
+ {
533
+ "translations-repo": ("/mozilla/firefox-translations-training",),
534
+ }
535
+ ),
520
536
  }
521
537
  )
522
538
  },
@@ -533,6 +549,7 @@ DEFAULT_CONFIG: immutabledict[str, Any] = immutabledict(
533
549
  "xpi": "any", # all allowed
534
550
  "adhoc": "any", # all allowed
535
551
  "scriptworker": ("decision", "action", "docker-image"),
552
+ "translations": "any", # all allowed
536
553
  }
537
554
  )
538
555
  },
@@ -548,6 +565,7 @@ DEFAULT_CONFIG: immutabledict[str, Any] = immutabledict(
548
565
  "xpi": "XPI",
549
566
  "adhoc": "ADHOC",
550
567
  "scriptworker": "SCRIPTWORKER",
568
+ "translations": "FIREFOX_TRANSLATIONS_TRAINING",
551
569
  }
552
570
  )
553
571
  },
@@ -24,7 +24,7 @@ from taskcluster.aio import Queue
24
24
 
25
25
  from scriptworker import task_process
26
26
  from scriptworker.exceptions import CoTError
27
- from scriptworker.utils import load_json_or_yaml_from_url, makedirs
27
+ from scriptworker.utils import load_json_or_yaml_from_url, makedirs, scriptworker_session
28
28
 
29
29
  log = logging.getLogger(__name__)
30
30
 
@@ -108,7 +108,9 @@ class Context(object):
108
108
  task_id: str = upstream_artifact["taskId"]
109
109
  for path in upstream_artifact["paths"]:
110
110
  if os.path.isabs(path) or ".." in path:
111
- raise CoTError("upstreamArtifacts taskId {} has illegal path {}!".format(task_id, path))
111
+ raise CoTError(f"upstreamArtifacts taskId {task_id} has illegal path {path}!")
112
+ if "*" in path and not upstream_artifact.get("optional", False):
113
+ raise CoTError(f"upstreamArtifacts taskId {task_id} has globbed path {path} as a non-optional artifact!")
112
114
 
113
115
  @property
114
116
  def credentials(self) -> Optional[Dict[str, Any]]:
@@ -146,7 +148,7 @@ class Context(object):
146
148
  """
147
149
  assert self.config
148
150
  if credentials:
149
- session = self.session or aiohttp.ClientSession(loop=self.event_loop)
151
+ session = self.session or scriptworker_session(loop=self.event_loop)
150
152
  return Queue(options={"credentials": credentials, "rootUrl": self.config["taskcluster_root_url"]}, session=session)
151
153
  return None
152
154
 
@@ -9,6 +9,7 @@ Attributes:
9
9
 
10
10
  import argparse
11
11
  import asyncio
12
+ import fnmatch
12
13
  import logging
13
14
  import os
14
15
  import pprint
@@ -23,7 +24,13 @@ import jsone
23
24
  from immutabledict import immutabledict
24
25
  from taskcluster.aio import Queue
25
26
 
26
- from scriptworker.artifacts import download_artifacts, get_artifact_url, get_optional_artifacts_per_task_id, get_single_upstream_artifact_full_path
27
+ from scriptworker.artifacts import (
28
+ download_artifacts,
29
+ get_artifact_url,
30
+ get_optional_artifacts_per_task_id,
31
+ get_single_upstream_artifact_full_path,
32
+ retry_list_latest_artifacts,
33
+ )
27
34
  from scriptworker.config import apply_product_config, read_worker_creds
28
35
  from scriptworker.constants import DEFAULT_CONFIG
29
36
  from scriptworker.context import Context
@@ -39,6 +46,7 @@ from scriptworker.task import (
39
46
  get_branch,
40
47
  get_commit_message,
41
48
  get_decision_task_id,
49
+ get_head_revision,
42
50
  get_parent_task_id,
43
51
  get_project,
44
52
  get_provisioner_id,
@@ -70,6 +78,7 @@ from scriptworker.utils import (
70
78
  read_from_file,
71
79
  remove_empty_keys,
72
80
  rm,
81
+ scriptworker_session,
73
82
  write_to_file,
74
83
  )
75
84
  from scriptworker.version import __version_string__
@@ -761,14 +770,28 @@ async def download_cot_artifacts(chain):
761
770
 
762
771
  mandatory_artifact_tasks = []
763
772
  optional_artifact_tasks = []
773
+ latest_artifacts = {}
764
774
  for task_id, paths in all_artifacts_per_task_id.items():
765
775
  for path in paths:
766
- coroutine = asyncio.ensure_future(download_cot_artifact(chain, task_id, path))
776
+ if "*" in path:
777
+ # Paths with wildcards in them indicate that the concrete
778
+ # artifact names aren't known when the task definition is
779
+ # created. For these cases, we need to fetch the list of
780
+ # artifacts from the completed tasks and then determine
781
+ # which are needed based on the pattern given.
782
+ if not latest_artifacts.get(task_id):
783
+ latest_artifacts[task_id] = (await retry_list_latest_artifacts(chain.context.queue, task_id))["artifacts"]
784
+ coroutines = []
785
+ for artifact in latest_artifacts[task_id]:
786
+ if fnmatch.fnmatch(artifact["name"], path):
787
+ coroutines.append(asyncio.ensure_future(download_cot_artifact(chain, task_id, artifact["name"])))
788
+ else:
789
+ coroutines = [asyncio.ensure_future(download_cot_artifact(chain, task_id, path))]
767
790
 
768
791
  if is_artifact_optional(chain, task_id, path):
769
- optional_artifact_tasks.append(coroutine)
792
+ optional_artifact_tasks.extend(coroutines)
770
793
  else:
771
- mandatory_artifact_tasks.append(coroutine)
794
+ mandatory_artifact_tasks.extend(coroutines)
772
795
 
773
796
  mandatory_artifacts_paths = await raise_future_exceptions(mandatory_artifact_tasks)
774
797
  succeeded_optional_artifacts_paths, failed_optional_artifacts = await get_results_and_future_exceptions(optional_artifact_tasks)
@@ -1020,12 +1043,17 @@ async def get_scm_level(context, project):
1020
1043
  """
1021
1044
  await context.populate_projects()
1022
1045
  config = context.projects[project]
1023
- if "access" in config:
1046
+ if config["repo_type"] == "hg":
1024
1047
  return config["access"].replace("scm_level_", "")
1025
- elif "level" in config:
1026
- return str(config["level"])
1027
- else:
1028
- raise ValueError("Can't find level for project {}".format(project))
1048
+ elif config["repo_type"] == "git":
1049
+ # TODO: we should be using the branch that the task is actually
1050
+ # being run on
1051
+ default_branch = config.get("default_branch", "main")
1052
+ for branch in config["branches"]:
1053
+ if branch["name"] == default_branch:
1054
+ return str(branch["level"])
1055
+
1056
+ raise ValueError("Can't find level for project {}".format(project))
1029
1057
 
1030
1058
 
1031
1059
  async def _get_additional_hg_action_jsone_context(parent_link, decision_link):
@@ -1193,6 +1221,10 @@ async def _get_additional_github_pull_request_jsone_context(decision_link):
1193
1221
  pull_request_data["head"]["updated_at"] = get_push_date_time(task, source_env_prefix)
1194
1222
  # Similarly, the base branch may get new commits by the time a PR job runs
1195
1223
  pull_request_data["base"]["sha"] = get_base_revision(task, source_env_prefix)
1224
+ # The head sha may also be different. This commonly happens in pull requests that
1225
+ # have tasks that have cached tasks from an earlier revision of the PR, or even
1226
+ # another PR altogether, upstream of one from the latest revision of the PR.
1227
+ pull_request_data["head"]["sha"] = get_head_revision(task, source_env_prefix)
1196
1228
 
1197
1229
  return {
1198
1230
  "event": {
@@ -1319,7 +1351,7 @@ async def populate_jsone_context(chain, parent_link, decision_link, tasks_for):
1319
1351
  jsone_context.update(await _get_additional_git_cron_jsone_context(decision_link))
1320
1352
  elif tasks_for == "action":
1321
1353
  jsone_context.update(await _get_additional_git_action_jsone_context(decision_link, parent_link))
1322
- elif tasks_for == "github-pull-request":
1354
+ elif tasks_for in ("github-pull-request", "github-pull-request-untrusted"):
1323
1355
  jsone_context.update(await _get_additional_github_pull_request_jsone_context(decision_link))
1324
1356
  elif tasks_for == "github-push":
1325
1357
  jsone_context.update(await _get_additional_github_push_jsone_context(decision_link))
@@ -2044,7 +2076,7 @@ async def verify_chain_of_trust(chain, *, check_task=False):
2044
2076
 
2045
2077
  # verify_cot_cmdln {{{1
2046
2078
  async def _async_verify_cot_cmdln(opts, tmp):
2047
- async with aiohttp.ClientSession() as session:
2079
+ async with scriptworker_session() as session:
2048
2080
  context = Context()
2049
2081
  context.session = session
2050
2082
  context.config = dict(deepcopy(DEFAULT_CONFIG))
@@ -2120,7 +2152,7 @@ SCRIPTWORKER_GITHUB_OAUTH_TOKEN to an OAUTH token with read permissions to the r
2120
2152
 
2121
2153
  # create_test_workdir {{{1
2122
2154
  async def _async_create_test_workdir(task_id, path, queue=None):
2123
- async with aiohttp.ClientSession() as session:
2155
+ async with scriptworker_session() as session:
2124
2156
  context = Context()
2125
2157
  context.session = session
2126
2158
  context.config = dict(deepcopy(DEFAULT_CONFIG))
@@ -230,6 +230,22 @@ def get_base_revision(task, source_env_prefix):
230
230
  return _extract_from_env_in_payload(task, source_env_prefix + "_BASE_REV")
231
231
 
232
232
 
233
+ def get_head_revision(task, source_env_prefix):
234
+ """Get the base revision for a task.
235
+
236
+ Args:
237
+ obj (ChainOfTrust or LinkOfTrust): the trust object to inspect
238
+ source_env_prefix (str): The environment variable prefix that is used
239
+ to get repository information.
240
+
241
+ Returns:
242
+ str: the revision.
243
+ None: if not defined for this task.
244
+
245
+ """
246
+ return _extract_from_env_in_payload(task, source_env_prefix + "_HEAD_REV")
247
+
248
+
233
249
  def get_branch(task, source_env_prefix):
234
250
  """Get the branch on top of which the graph was made.
235
251
 
@@ -487,7 +503,7 @@ async def is_pull_request(context, task):
487
503
 
488
504
  This checks for the following things::
489
505
 
490
- * ``task.extra.env.tasks_for`` == "github-pull-request"
506
+ * ``task.extra.env.tasks_for`` is "github-pull-request" or "github-pull-request-untrusted"
491
507
  * ``task.payload.env.MOBILE_HEAD_REPOSITORY`` doesn't come from an official repo
492
508
  * ``task.metadata.source`` doesn't come from an official repo, either
493
509
  * The last 2 items are landed on the official repo
@@ -509,7 +525,7 @@ async def is_pull_request(context, task):
509
525
  metadata_source_url = task["metadata"].get("source", "")
510
526
  repo_from_source_url, revision_from_source_url = extract_github_repo_and_revision_from_source_url(metadata_source_url)
511
527
 
512
- conditions = [tasks_for == "github-pull-request"]
528
+ conditions = [tasks_for in ("github-pull-request", "github-pull-request-untrusted")]
513
529
  urls_revisions_and_can_skip = ((repo_url_from_payload, revision_from_payload, True), (repo_from_source_url, revision_from_source_url, False))
514
530
  for repo_url, revision, can_skip in urls_revisions_and_can_skip:
515
531
  # XXX In the case of scriptworker tasks, neither the repo nor the revision is defined
@@ -25,6 +25,7 @@ import async_timeout
25
25
  import yaml
26
26
  from taskcluster.client import createTemporaryCredentials
27
27
 
28
+ import scriptworker.version
28
29
  from scriptworker.exceptions import Download404, DownloadError, ScriptWorkerException, ScriptWorkerRetryException, ScriptWorkerTaskException
29
30
 
30
31
  if TYPE_CHECKING:
@@ -37,6 +38,12 @@ else:
37
38
  log = logging.getLogger(__name__)
38
39
 
39
40
 
41
+ # scriptworker_session {{{1
42
+ def scriptworker_session(*args, **kwargs):
43
+ kwargs.setdefault("headers", {}).setdefault("User-Agent", f"scriptworker {scriptworker.version.__version_string__}")
44
+ return aiohttp.ClientSession(*args, **kwargs)
45
+
46
+
40
47
  # request {{{1
41
48
  async def request(context, url, timeout=60, method="get", good=(200,), retry=tuple(range(500, 512)), return_type="text", **kwargs):
42
49
  """Async aiohttp request wrapper.
@@ -54,7 +54,7 @@ def get_version_string(version: Union[ShortVerType, LongVerType]) -> str:
54
54
 
55
55
  # 1}}}
56
56
  # Semantic versioning 2.0.0 http://semver.org/
57
- __version__ = (60, 0, 0)
57
+ __version__ = (60, 2, 0)
58
58
  __version_string__ = get_version_string(__version__)
59
59
 
60
60
 
@@ -25,7 +25,7 @@ from scriptworker.cot.verify import ChainOfTrust, verify_chain_of_trust
25
25
  from scriptworker.exceptions import ScriptWorkerException, WorkerShutdownDuringTask
26
26
  from scriptworker.task import claim_work, complete_task, prepare_to_run_task, reclaim_task, run_task, worst_level
27
27
  from scriptworker.task_process import TaskProcess
28
- from scriptworker.utils import cleanup, filepaths_in_dir
28
+ from scriptworker.utils import cleanup, filepaths_in_dir, scriptworker_session
29
29
 
30
30
  log = logging.getLogger(__name__)
31
31
 
@@ -210,7 +210,7 @@ async def async_main(context, credentials):
210
210
  Args:
211
211
  context (scriptworker.context.Context): the scriptworker context.
212
212
  """
213
- async with aiohttp.ClientSession() as session:
213
+ async with scriptworker_session() as session:
214
214
  context.session = session
215
215
  context.credentials = credentials
216
216
  await run_tasks(context)
@@ -0,0 +1,86 @@
1
+ Metadata-Version: 2.1
2
+ Name: scriptworker
3
+ Version: 60.2.0
4
+ Summary: TaskCluster Script Worker
5
+ Home-page: https://github.com/mozilla-releng/scriptworker
6
+ Author: Mozilla Release Engineering
7
+ Author-email: release+python@mozilla.com
8
+ License: MPL 2.0
9
+ Classifier: Intended Audience :: Developers
10
+ Classifier: Natural Language :: English
11
+ Classifier: Programming Language :: Python :: 3.8
12
+ Classifier: Programming Language :: Python :: 3.9
13
+ Classifier: Programming Language :: Python :: 3.10
14
+ Requires-Python: >=3.7
15
+ License-File: LICENSE
16
+
17
+ ===================
18
+ Scriptworker Readme
19
+ ===================
20
+
21
+ .. image:: https://travis-ci.org/mozilla-releng/scriptworker.svg?branch=master
22
+ :target: https://travis-ci.org/mozilla-releng/scriptworker
23
+
24
+ .. image:: https://coveralls.io/repos/github/mozilla-releng/scriptworker/badge.svg?branch=master
25
+ :target: https://coveralls.io/github/mozilla-releng/scriptworker?branch=master
26
+
27
+ .. image:: https://readthedocs.org/projects/scriptworker/badge/?version=latest
28
+ :target: http://scriptworker.readthedocs.io/en/latest/?badge=latest
29
+ :alt: Documentation Status
30
+
31
+ Scriptworker implements the `TaskCluster worker model`_, then launches a pre-defined script.
32
+
33
+ .. _TaskCluster worker model: https://firefox-ci-tc.services.mozilla.com/docs/reference/platform/queue/worker-interaction
34
+
35
+ This worker was designed for `Releng processes`_ that need specific, limited, and pre-defined capabilities.
36
+
37
+ .. _Releng processes: https://bugzilla.mozilla.org/show_bug.cgi?id=1245837
38
+
39
+ Free software: MPL2 License
40
+
41
+ -----
42
+ Usage
43
+ -----
44
+ * Create a config file. By default scriptworker will look in ``./scriptworker.yaml``, but this config path can be specified as the first and only commandline argument. There is an `example config file`_, and all config items are specified in `scriptworker.constants.DEFAULT_CONFIG`_.
45
+
46
+ .. _example config file: https://github.com/mozilla-releng/scriptworker/blob/master/scriptworker.yaml.tmpl
47
+ .. _scriptworker.constants.DEFAULT_CONFIG: https://github.com/mozilla-releng/scriptworker/blob/master/src/scriptworker/constants.py
48
+
49
+ Credentials can live in ``./scriptworker.yaml``, ``./secrets.json``, ``~/.scriptworker``.
50
+
51
+ * Launch: ``scriptworker [config_path]``
52
+
53
+ -------
54
+ Testing
55
+ -------
56
+
57
+ Without integration tests install tox, then
58
+
59
+ ``NO_CREDENTIALS_TESTS=1 tox -e py36``
60
+
61
+ Without any tests connecting to the net, then ``NO_TESTS_OVER_WIRE=1 tox -e py36``
62
+
63
+ With integration tests, first create a client in the Taskcluster UI with the scopes::
64
+
65
+ queue:cancel-task:test-dummy-scheduler/*
66
+ queue:claim-work:test-dummy-provisioner/dummy-worker-*
67
+ queue:create-task:lowest:test-dummy-provisioner/dummy-worker-*
68
+ queue:define-task:test-dummy-provisioner/dummy-worker-*
69
+ queue:get-artifact:SampleArtifacts/_/X.txt
70
+ queue:scheduler-id:test-dummy-scheduler
71
+ queue:schedule-task:test-dummy-scheduler/*
72
+ queue:task-group-id:test-dummy-scheduler/*
73
+ queue:worker-id:test-dummy-workers/dummy-worker-*
74
+
75
+ Then generate a no priviledge personal access token in Github for the scriptworker_github_token (to avoid rate limiting) and create a ``./secrets.json`` or ``~/.scriptworker`` that looks like::
76
+
77
+ {
78
+ "integration_credentials": {
79
+ "clientId": "...",
80
+ "accessToken": "...",
81
+ }
82
+ "scriptworker_github_token": "..."
83
+ }
84
+
85
+
86
+ then to run all tests: ``tox``
@@ -3,4 +3,3 @@ create_test_workdir = scriptworker.cot.verify:create_test_workdir
3
3
  scriptworker = scriptworker.worker:main
4
4
  verify_cot = scriptworker.cot.verify:verify_cot_cmdln
5
5
  verify_ed25519_signature = scriptworker.ed25519:verify_ed25519_signature_cmdln
6
-
@@ -1,8 +1,8 @@
1
1
  {
2
2
  "version":[
3
3
  60,
4
- 0,
4
+ 2,
5
5
  0
6
6
  ],
7
- "version_string":"60.0.0"
7
+ "version_string":"60.2.0"
8
8
  }
@@ -1,19 +0,0 @@
1
- Metadata-Version: 2.1
2
- Name: scriptworker
3
- Version: 60.0.0
4
- Summary: TaskCluster Script Worker
5
- Home-page: https://github.com/mozilla-releng/scriptworker
6
- Author: Mozilla Release Engineering
7
- Author-email: release+python@mozilla.com
8
- License: MPL 2.0
9
- Platform: UNKNOWN
10
- Classifier: Intended Audience :: Developers
11
- Classifier: Natural Language :: English
12
- Classifier: Programming Language :: Python :: 3.8
13
- Classifier: Programming Language :: Python :: 3.9
14
- Classifier: Programming Language :: Python :: 3.10
15
- Requires-Python: >=3.7
16
- License-File: LICENSE
17
-
18
- UNKNOWN
19
-
@@ -1,19 +0,0 @@
1
- Metadata-Version: 2.1
2
- Name: scriptworker
3
- Version: 60.0.0
4
- Summary: TaskCluster Script Worker
5
- Home-page: https://github.com/mozilla-releng/scriptworker
6
- Author: Mozilla Release Engineering
7
- Author-email: release+python@mozilla.com
8
- License: MPL 2.0
9
- Platform: UNKNOWN
10
- Classifier: Intended Audience :: Developers
11
- Classifier: Natural Language :: English
12
- Classifier: Programming Language :: Python :: 3.8
13
- Classifier: Programming Language :: Python :: 3.9
14
- Classifier: Programming Language :: Python :: 3.10
15
- Requires-Python: >=3.7
16
- License-File: LICENSE
17
-
18
- UNKNOWN
19
-
File without changes
File without changes
File without changes
File without changes
File without changes