scriptworker 63.5.0__tar.gz → 63.6.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.
- {scriptworker-63.5.0 → scriptworker-63.6.0}/HISTORY.rst +10 -0
- {scriptworker-63.5.0 → scriptworker-63.6.0}/PKG-INFO +2 -2
- {scriptworker-63.5.0 → scriptworker-63.6.0}/pyproject.toml +2 -2
- {scriptworker-63.5.0 → scriptworker-63.6.0}/scriptworker.yaml.tmpl +9 -3
- {scriptworker-63.5.0 → scriptworker-63.6.0}/src/scriptworker/constants.py +18 -2
- {scriptworker-63.5.0 → scriptworker-63.6.0}/src/scriptworker/cot/verify.py +35 -30
- {scriptworker-63.5.0 → scriptworker-63.6.0}/src/scriptworker/github.py +104 -15
- {scriptworker-63.5.0 → scriptworker-63.6.0}/src/scriptworker/task.py +1 -1
- {scriptworker-63.5.0 → scriptworker-63.6.0}/.gitignore +0 -0
- {scriptworker-63.5.0 → scriptworker-63.6.0}/CONTRIBUTING.rst +0 -0
- {scriptworker-63.5.0 → scriptworker-63.6.0}/LICENSE +0 -0
- {scriptworker-63.5.0 → scriptworker-63.6.0}/README.rst +0 -0
- {scriptworker-63.5.0 → scriptworker-63.6.0}/renovate.json +0 -0
- {scriptworker-63.5.0 → scriptworker-63.6.0}/scripts/gen_ed25519_key.py +0 -0
- {scriptworker-63.5.0 → scriptworker-63.6.0}/src/scriptworker/__init__.py +0 -0
- {scriptworker-63.5.0 → scriptworker-63.6.0}/src/scriptworker/artifacts.py +0 -0
- {scriptworker-63.5.0 → scriptworker-63.6.0}/src/scriptworker/client.py +0 -0
- {scriptworker-63.5.0 → scriptworker-63.6.0}/src/scriptworker/config.py +0 -0
- {scriptworker-63.5.0 → scriptworker-63.6.0}/src/scriptworker/context.py +0 -0
- {scriptworker-63.5.0 → scriptworker-63.6.0}/src/scriptworker/cot/__init__.py +0 -0
- {scriptworker-63.5.0 → scriptworker-63.6.0}/src/scriptworker/cot/generate.py +0 -0
- {scriptworker-63.5.0 → scriptworker-63.6.0}/src/scriptworker/data/cot_v1_schema.json +0 -0
- {scriptworker-63.5.0 → scriptworker-63.6.0}/src/scriptworker/data/scriptworker_task_schema.json +0 -0
- {scriptworker-63.5.0 → scriptworker-63.6.0}/src/scriptworker/ed25519.py +0 -0
- {scriptworker-63.5.0 → scriptworker-63.6.0}/src/scriptworker/exceptions.py +0 -0
- {scriptworker-63.5.0 → scriptworker-63.6.0}/src/scriptworker/log.py +0 -0
- {scriptworker-63.5.0 → scriptworker-63.6.0}/src/scriptworker/task_process.py +0 -0
- {scriptworker-63.5.0 → scriptworker-63.6.0}/src/scriptworker/utils.py +0 -0
- {scriptworker-63.5.0 → scriptworker-63.6.0}/src/scriptworker/worker.py +0 -0
- {scriptworker-63.5.0 → scriptworker-63.6.0}/tox.ini +0 -0
|
@@ -4,6 +4,16 @@ 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.6.0 - 2026-10-06
|
|
8
|
+
-------------------
|
|
9
|
+
|
|
10
|
+
Added
|
|
11
|
+
~~~~~
|
|
12
|
+
|
|
13
|
+
- Accept `github-push` decision tasks for the Firefox CoT product
|
|
14
|
+
- Add `comm-autoland` as a trusted project
|
|
15
|
+
- Obtain a repository scoped Github token from the Taskcluster Auth service
|
|
16
|
+
|
|
7
17
|
63.5.0 - 2026-09-23
|
|
8
18
|
-------------------
|
|
9
19
|
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.5
|
|
2
2
|
Name: scriptworker
|
|
3
|
-
Version: 63.
|
|
3
|
+
Version: 63.6.0
|
|
4
4
|
Summary: Taskcluster scriptworker
|
|
5
5
|
Author-email: Mozilla Release Engineering <releng@mozilla.com>
|
|
6
6
|
License-File: LICENSE
|
|
@@ -21,7 +21,7 @@ Requires-Dist: json-e>=2.5.0
|
|
|
21
21
|
Requires-Dist: jsonschema[format-nongpl]
|
|
22
22
|
Requires-Dist: pyyaml
|
|
23
23
|
Requires-Dist: taskcluster-taskgraph
|
|
24
|
-
Requires-Dist: taskcluster>=
|
|
24
|
+
Requires-Dist: taskcluster>=106
|
|
25
25
|
Description-Content-Type: text/x-rst
|
|
26
26
|
|
|
27
27
|
===================
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
[project]
|
|
2
2
|
name = "scriptworker"
|
|
3
|
-
version = "63.
|
|
3
|
+
version = "63.6.0"
|
|
4
4
|
description = "Taskcluster scriptworker"
|
|
5
5
|
authors = [
|
|
6
6
|
{ name = "Mozilla Release Engineering", email = "releng@mozilla.com" },
|
|
@@ -25,7 +25,7 @@ dependencies = [
|
|
|
25
25
|
"jsonschema[format-nongpl]",
|
|
26
26
|
"json-e>=2.5.0",
|
|
27
27
|
"PyYAML",
|
|
28
|
-
"taskcluster>=
|
|
28
|
+
"taskcluster>=106",
|
|
29
29
|
"taskcluster-taskgraph",
|
|
30
30
|
]
|
|
31
31
|
|
|
@@ -45,9 +45,15 @@ verify_cot_signature: false
|
|
|
45
45
|
# Chain of Trust job type, e.g. signing
|
|
46
46
|
cot_job_type: scriptworker
|
|
47
47
|
cot_product: firefox
|
|
48
|
-
|
|
49
|
-
#
|
|
50
|
-
|
|
48
|
+
|
|
49
|
+
# Scriptworker first tries to obtain a repository scoped token from Taskcluster's auth service,
|
|
50
|
+
# using the Github App registered under this name.
|
|
51
|
+
# github_app_name: read
|
|
52
|
+
#
|
|
53
|
+
# `github_oauth_token` is used as a fallback if that fails (e.g. missing scopes or app not
|
|
54
|
+
# configured). Without either, calls to the Github API are unauthenticated and limited to 60 an
|
|
55
|
+
# hour. See https://developer.github.com/v3/#rate-limiting
|
|
56
|
+
# github_oauth_token: somegithubtoken
|
|
51
57
|
|
|
52
58
|
|
|
53
59
|
#-----------------------------------------------------------------------------------------------
|
|
@@ -79,7 +79,12 @@ DEFAULT_CONFIG: immutabledict[str, Any] = immutabledict(
|
|
|
79
79
|
"max_chain_length": 20,
|
|
80
80
|
# Calls to Github API are limited to 60 an hour. Using an API token allows to raise the limit to
|
|
81
81
|
# 5000 per hour. https://developer.github.com/v3/#rate-limiting
|
|
82
|
+
# Scriptworker first tries to obtain a repository scoped token from Taskcluster's auth
|
|
83
|
+
# service, falling back to this token if that fails.
|
|
82
84
|
"github_oauth_token": "",
|
|
85
|
+
# The name of the Github App registered with Taskcluster's auth service, used to obtain
|
|
86
|
+
# a repository scoped token via `auth.githubRepoToken`.
|
|
87
|
+
"github_app_name": "read",
|
|
83
88
|
# ed25519 settings
|
|
84
89
|
"ed25519_private_key_path": "...",
|
|
85
90
|
"ed25519_public_keys": immutabledict(
|
|
@@ -142,7 +147,8 @@ DEFAULT_CONFIG: immutabledict[str, Any] = immutabledict(
|
|
|
142
147
|
"adhoc": "github",
|
|
143
148
|
"app-services": "github",
|
|
144
149
|
"enterprise": "github",
|
|
145
|
-
|
|
150
|
+
# Firefox is migrating from hg.mozilla.org to Github (bug 2070846)
|
|
151
|
+
"firefox": ("hg", "github"),
|
|
146
152
|
"glean": "github",
|
|
147
153
|
"mobile": "github",
|
|
148
154
|
"mozillavpn": "github",
|
|
@@ -408,6 +414,7 @@ DEFAULT_CONFIG: immutabledict[str, Any] = immutabledict(
|
|
|
408
414
|
"netlocs": ("hg.mozilla.org",),
|
|
409
415
|
"path_regexes": (
|
|
410
416
|
r"^(?P<path>/comm-central)(/|$)",
|
|
417
|
+
r"^(?P<path>/integration/comm-autoland)(/|$)",
|
|
411
418
|
r"^(?P<path>/releases/comm-(beta|release|esr\d+))(/|$)",
|
|
412
419
|
),
|
|
413
420
|
}
|
|
@@ -492,7 +499,15 @@ DEFAULT_CONFIG: immutabledict[str, Any] = immutabledict(
|
|
|
492
499
|
"github-release",
|
|
493
500
|
"pr-action",
|
|
494
501
|
),
|
|
495
|
-
"firefox": (
|
|
502
|
+
"firefox": (
|
|
503
|
+
"action",
|
|
504
|
+
"cron",
|
|
505
|
+
"github-pull-request",
|
|
506
|
+
"github-pull-request-untrusted",
|
|
507
|
+
"github-push",
|
|
508
|
+
"hg-push",
|
|
509
|
+
"pr-action",
|
|
510
|
+
),
|
|
496
511
|
"glean": (
|
|
497
512
|
"action",
|
|
498
513
|
"cron",
|
|
@@ -826,6 +841,7 @@ DEFAULT_CONFIG: immutabledict[str, Any] = immutabledict(
|
|
|
826
841
|
"/releases/comm-esr153",
|
|
827
842
|
),
|
|
828
843
|
"nightly": ("/comm-central",),
|
|
844
|
+
"autoland": ("/integration/comm-autoland",),
|
|
829
845
|
}
|
|
830
846
|
),
|
|
831
847
|
"translations": immutabledict(
|
|
@@ -20,7 +20,6 @@ import tempfile
|
|
|
20
20
|
from copy import deepcopy
|
|
21
21
|
from urllib.parse import urlparse
|
|
22
22
|
|
|
23
|
-
import aiohttp
|
|
24
23
|
import dictdiffer
|
|
25
24
|
import jsone
|
|
26
25
|
from immutabledict import immutabledict
|
|
@@ -45,6 +44,7 @@ from scriptworker.github import (
|
|
|
45
44
|
extract_github_repo_full_name,
|
|
46
45
|
extract_github_repo_owner_and_name,
|
|
47
46
|
extract_github_repo_ssh_url,
|
|
47
|
+
is_github_url,
|
|
48
48
|
)
|
|
49
49
|
from scriptworker.log import contextual_log_handler, get_chain_of_trust_log_filename
|
|
50
50
|
from scriptworker.task import (
|
|
@@ -67,6 +67,7 @@ from scriptworker.task import (
|
|
|
67
67
|
get_triggered_by,
|
|
68
68
|
get_worker_pool_id,
|
|
69
69
|
is_action,
|
|
70
|
+
is_github_task,
|
|
70
71
|
is_try_or_pull_request,
|
|
71
72
|
retry_get_task_definition,
|
|
72
73
|
)
|
|
@@ -1126,7 +1127,7 @@ async def _get_additional_github_releases_jsone_context(decision_link):
|
|
|
1126
1127
|
repo_owner, repo_name = extract_github_repo_owner_and_name(repo_url)
|
|
1127
1128
|
tag_name = get_revision(task, source_env_prefix)
|
|
1128
1129
|
|
|
1129
|
-
github_repo = GitHubRepository(repo_owner, repo_name
|
|
1130
|
+
github_repo = GitHubRepository(context, repo_owner, repo_name)
|
|
1130
1131
|
release_data = await github_repo.get_release(tag_name)
|
|
1131
1132
|
|
|
1132
1133
|
# The release data expose by the API[1] is not the same as the original event[2]. That's why
|
|
@@ -1208,17 +1209,16 @@ async def _get_additional_github_pull_request_jsone_context(decision_link):
|
|
|
1208
1209
|
repo_url = repo_url.replace("git@github.com:", "ssh://github.com/", 1)
|
|
1209
1210
|
repo_owner, repo_name = extract_github_repo_owner_and_name(repo_url)
|
|
1210
1211
|
pull_request_number = get_pull_request_number(task, source_env_prefix)
|
|
1211
|
-
token = context.config["github_oauth_token"]
|
|
1212
1212
|
|
|
1213
|
-
github_repo = GitHubRepository(repo_owner, repo_name
|
|
1214
|
-
repo_definition = github_repo.
|
|
1213
|
+
github_repo = GitHubRepository(context, repo_owner, repo_name)
|
|
1214
|
+
repo_definition = await github_repo.get_definition()
|
|
1215
1215
|
|
|
1216
1216
|
# We need to query the repository where the pull request was made to extract
|
|
1217
1217
|
# pull request data. The pull request could be created on the same repo as
|
|
1218
1218
|
# the commit, or an upstream repo. We can compare the base and head repo URLs
|
|
1219
1219
|
# to infer where the pull request lives.
|
|
1220
1220
|
if repo_definition["fork"] and base_repo_url != repo_url:
|
|
1221
|
-
github_repo = GitHubRepository(
|
|
1221
|
+
github_repo = GitHubRepository(context, repo_definition["parent"]["owner"]["login"], repo_definition["parent"]["name"])
|
|
1222
1222
|
|
|
1223
1223
|
pull_request_data = await github_repo.get_pull_request(pull_request_number)
|
|
1224
1224
|
# Even though pull_request_data['head']['repo']['pushed_at'] does exist,
|
|
@@ -1253,7 +1253,7 @@ async def _get_additional_github_push_jsone_context(decision_link):
|
|
|
1253
1253
|
repo_owner, repo_name = extract_github_repo_owner_and_name(repo_url)
|
|
1254
1254
|
commit_hash = get_revision(task, source_env_prefix)
|
|
1255
1255
|
|
|
1256
|
-
github_repo = GitHubRepository(repo_owner, repo_name
|
|
1256
|
+
github_repo = GitHubRepository(context, repo_owner, repo_name)
|
|
1257
1257
|
commit_data = await github_repo.get_commit(commit_hash)
|
|
1258
1258
|
|
|
1259
1259
|
committer = commit_data["committer"] or {}
|
|
@@ -1351,7 +1351,18 @@ async def populate_jsone_context(chain, parent_link, decision_link, tasks_for):
|
|
|
1351
1351
|
"taskId": None,
|
|
1352
1352
|
}
|
|
1353
1353
|
|
|
1354
|
-
|
|
1354
|
+
cot_product_type = chain.context.config["cot_product_type"]
|
|
1355
|
+
# A tuple becomes a list when the config round-trips through json/yaml.
|
|
1356
|
+
if isinstance(cot_product_type, (tuple, list)):
|
|
1357
|
+
# Products migrating between VCSes support several types (bug 2070846).
|
|
1358
|
+
# tasks_for like `action` and `cron` exist on both hg and github, so
|
|
1359
|
+
# determine the type from the decision task itself.
|
|
1360
|
+
detected_type = "github" if is_github_task(decision_link.task) else "hg"
|
|
1361
|
+
if detected_type not in cot_product_type:
|
|
1362
|
+
raise CoTError('cot_product_type "{}" is not enabled for cot_product "{}"!'.format(detected_type, chain.context.config["cot_product"]))
|
|
1363
|
+
cot_product_type = detected_type
|
|
1364
|
+
|
|
1365
|
+
if cot_product_type == "github":
|
|
1355
1366
|
if tasks_for == "github-release":
|
|
1356
1367
|
jsone_context.update(await _get_additional_github_releases_jsone_context(decision_link))
|
|
1357
1368
|
elif tasks_for == "cron":
|
|
@@ -1364,7 +1375,7 @@ async def populate_jsone_context(chain, parent_link, decision_link, tasks_for):
|
|
|
1364
1375
|
jsone_context.update(await _get_additional_github_push_jsone_context(decision_link))
|
|
1365
1376
|
else:
|
|
1366
1377
|
raise CoTError('Unknown tasks_for "{}" for github cot_product "{}"!'.format(tasks_for, chain.context.config["cot_product"]))
|
|
1367
|
-
elif
|
|
1378
|
+
elif cot_product_type == "hg":
|
|
1368
1379
|
source_url = get_source_url(decision_link)
|
|
1369
1380
|
project = await get_project(chain.context, source_url)
|
|
1370
1381
|
jsone_context["repository"] = {
|
|
@@ -1384,9 +1395,7 @@ async def populate_jsone_context(chain, parent_link, decision_link, tasks_for):
|
|
|
1384
1395
|
else:
|
|
1385
1396
|
raise CoTError('Unknown tasks_for "{}" for hg cot_product "{}"!'.format(tasks_for, chain.context.config["cot_product"]))
|
|
1386
1397
|
else:
|
|
1387
|
-
raise CoTError(
|
|
1388
|
-
'Unknown cot_product_type "{}" for cot_product "{}"!'.format(chain.context.config["cot_product_type"], chain.context.config["cot_product"])
|
|
1389
|
-
)
|
|
1398
|
+
raise CoTError('Unknown cot_product_type "{}" for cot_product "{}"!'.format(cot_product_type, chain.context.config["cot_product"]))
|
|
1390
1399
|
|
|
1391
1400
|
log.debug("{} json-e context:".format(parent_link.name))
|
|
1392
1401
|
# format_json() breaks on lambda values; use pprint.pformat here.
|
|
@@ -1407,16 +1416,11 @@ def build_taskcluster_yml_url(link):
|
|
|
1407
1416
|
"""
|
|
1408
1417
|
source_env_prefix = link.context.config["source_env_prefix"]
|
|
1409
1418
|
repo_url = get_repo(link.task, source_env_prefix)
|
|
1410
|
-
repo_url = repo_url.replace("git@github.com:", "ssh://github.com/", 1)
|
|
1411
1419
|
revision = get_revision(link.task, source_env_prefix)
|
|
1412
1420
|
repo_parts = urlparse(repo_url)
|
|
1413
|
-
if repo_parts.netloc
|
|
1414
|
-
user, repo_name = extract_github_repo_owner_and_name(repo_url)
|
|
1415
|
-
url = f"https://raw.githubusercontent.com/{user}/{repo_name}/{revision}/.taskcluster.yml"
|
|
1416
|
-
elif repo_parts.netloc == "hg.mozilla.org":
|
|
1417
|
-
url = f"{repo_parts.scheme}://{repo_parts.netloc}{repo_parts.path}/raw-file/{revision}/.taskcluster.yml"
|
|
1418
|
-
else:
|
|
1421
|
+
if repo_parts.netloc != "hg.mozilla.org":
|
|
1419
1422
|
raise CoTError("Unsupported VCS server!")
|
|
1423
|
+
url = f"{repo_parts.scheme}://{repo_parts.netloc}{repo_parts.path}/raw-file/{revision}/.taskcluster.yml"
|
|
1420
1424
|
log.debug(f"{link.name} .taskcluster.yml is at {url}")
|
|
1421
1425
|
return url
|
|
1422
1426
|
|
|
@@ -1438,19 +1442,20 @@ async def get_in_tree_template(link):
|
|
|
1438
1442
|
|
|
1439
1443
|
"""
|
|
1440
1444
|
context = link.context
|
|
1441
|
-
|
|
1442
|
-
repo_url = get_repo(link.task,
|
|
1445
|
+
source_env_prefix = context.config["source_env_prefix"]
|
|
1446
|
+
repo_url = get_repo(link.task, source_env_prefix)
|
|
1447
|
+
repo_url = repo_url.replace("git@github.com:", "ssh://github.com/", 1)
|
|
1443
1448
|
|
|
1444
|
-
|
|
1445
|
-
|
|
1446
|
-
|
|
1447
|
-
|
|
1448
|
-
|
|
1449
|
-
|
|
1450
|
-
|
|
1449
|
+
if is_github_url(repo_url):
|
|
1450
|
+
revision = get_revision(link.task, source_env_prefix)
|
|
1451
|
+
repo_owner, repo_name = extract_github_repo_owner_and_name(repo_url)
|
|
1452
|
+
github_repo = GitHubRepository(context, repo_owner, repo_name)
|
|
1453
|
+
content = await github_repo.get_file_contents(".taskcluster.yml", ref=revision)
|
|
1454
|
+
return load_json_or_yaml(content, file_type="yaml")
|
|
1455
|
+
|
|
1456
|
+
source_url = build_taskcluster_yml_url(link)
|
|
1451
1457
|
url_hash = hashlib.sha1(source_url.encode("ascii")).hexdigest()
|
|
1452
|
-
|
|
1453
|
-
return tmpl
|
|
1458
|
+
return await load_json_or_yaml_from_url(context, source_url, os.path.join(context.config["work_dir"], "{}_taskcluster.yml".format(url_hash)))
|
|
1454
1459
|
|
|
1455
1460
|
|
|
1456
1461
|
def _get_action_from_actions_json(all_actions, callback_name):
|
|
@@ -1,12 +1,16 @@
|
|
|
1
1
|
"""GitHub helper functions."""
|
|
2
2
|
|
|
3
3
|
import asyncio
|
|
4
|
+
import hashlib
|
|
4
5
|
import logging
|
|
5
6
|
import re
|
|
7
|
+
from pathlib import Path
|
|
6
8
|
from urllib.parse import unquote, urlparse
|
|
7
9
|
|
|
8
10
|
from github3 import GitHub
|
|
9
11
|
from github3.exceptions import GitHubException
|
|
12
|
+
from taskcluster.aio import Auth
|
|
13
|
+
from taskcluster.exceptions import TaskclusterFailure
|
|
10
14
|
|
|
11
15
|
from scriptworker.exceptions import ConfigError
|
|
12
16
|
from scriptworker.utils import get_parts_of_url_path, get_single_item_from_sequence, retry_async_decorator, retry_request, retry_sync
|
|
@@ -24,30 +28,110 @@ log = logging.getLogger(__name__)
|
|
|
24
28
|
class GitHubRepository:
|
|
25
29
|
"""Wrapper around GitHub API. Used to access public data."""
|
|
26
30
|
|
|
27
|
-
|
|
28
|
-
|
|
31
|
+
GITHUB_PERMISSIONS = {"contents": "read", "metadata": "read", "pull_requests": "read"}
|
|
32
|
+
|
|
33
|
+
def __init__(self, context, owner, repo_name):
|
|
34
|
+
"""Store the repository coordinates. The github3 repository object is built lazily.
|
|
29
35
|
|
|
30
36
|
Args:
|
|
31
|
-
|
|
37
|
+
context (scriptworker.context.Context): the scriptworker context
|
|
38
|
+
owner (str): the owner of the repository
|
|
32
39
|
repo_name (str): the name of the repository
|
|
33
|
-
|
|
40
|
+
|
|
41
|
+
"""
|
|
42
|
+
self._context = context
|
|
43
|
+
self._owner = owner
|
|
44
|
+
self._repo_name = repo_name
|
|
45
|
+
self._repository_cache = None
|
|
46
|
+
self._repository_lock = asyncio.Lock()
|
|
47
|
+
|
|
48
|
+
async def _get_repository(self):
|
|
49
|
+
"""Build and cache the github3 repository object.
|
|
34
50
|
|
|
35
51
|
Returns:
|
|
36
|
-
|
|
52
|
+
github3.repos.repo.Repository: the github3 repository object
|
|
37
53
|
|
|
38
54
|
"""
|
|
39
|
-
|
|
40
|
-
|
|
55
|
+
async with self._repository_lock:
|
|
56
|
+
if self._repository_cache is None:
|
|
57
|
+
token = await self._get_token(self._context, self._owner, self._repo_name)
|
|
58
|
+
github = retry_sync(GitHub, kwargs={"token": token}, sleeptime_kwargs=_GITHUB_LIBRARY_SLEEP_TIME_KWARGS)
|
|
59
|
+
self._repository_cache = retry_sync(github.repository, args=(self._owner, self._repo_name), sleeptime_kwargs=_GITHUB_LIBRARY_SLEEP_TIME_KWARGS)
|
|
60
|
+
|
|
61
|
+
return self._repository_cache
|
|
62
|
+
|
|
63
|
+
async def _get_token(self, context, owner, repo_name):
|
|
64
|
+
"""Get a repository scoped GitHub token from Taskcluster's auth service.
|
|
65
|
+
|
|
66
|
+
Falls back to ``context.config["github_oauth_token"]`` if the auth service call
|
|
67
|
+
fails, e.g. because of missing scopes.
|
|
68
|
+
|
|
69
|
+
Args:
|
|
70
|
+
context (scriptworker.context.Context): the scriptworker context
|
|
71
|
+
owner (str): the owner of the repository
|
|
72
|
+
repo_name (str): the name of the repository
|
|
41
73
|
|
|
42
|
-
|
|
43
|
-
|
|
74
|
+
Returns:
|
|
75
|
+
str: the scoped GitHub token, or the fallback token
|
|
76
|
+
|
|
77
|
+
"""
|
|
78
|
+
if not context.credentials:
|
|
79
|
+
return context.config.get("github_oauth_token", "")
|
|
80
|
+
|
|
81
|
+
try:
|
|
82
|
+
auth = Auth(options={"rootUrl": context.config["taskcluster_root_url"], "credentials": context.credentials})
|
|
83
|
+
response = await auth.githubRepoToken(
|
|
84
|
+
context.config["github_app_name"], owner, payload={"repositories": [repo_name], "permissions": self.GITHUB_PERMISSIONS}
|
|
85
|
+
)
|
|
86
|
+
return response["token"]
|
|
87
|
+
except TaskclusterFailure as e:
|
|
88
|
+
# TODO When opening a PR from a fork, we're guaranteed to hit this
|
|
89
|
+
# fallback as the task won't have auth service scopes for the repo
|
|
90
|
+
# fork. We'll need to improve this before we can stop depending on
|
|
91
|
+
# `github_oauth_token`.
|
|
92
|
+
log.warning(f"Could not obtain Github token from Taskcluster for {owner}/{repo_name}, falling back to `github_oauth_token`: {e}")
|
|
93
|
+
return context.config.get("github_oauth_token", "")
|
|
94
|
+
|
|
95
|
+
async def get_definition(self):
|
|
44
96
|
"""Fetch the definition of the repository, exposed by the GitHub API.
|
|
45
97
|
|
|
46
98
|
Returns:
|
|
47
99
|
dict: a representation of the repo definition
|
|
48
100
|
|
|
49
101
|
"""
|
|
50
|
-
|
|
102
|
+
repository = await self._get_repository()
|
|
103
|
+
return repository.as_dict()
|
|
104
|
+
|
|
105
|
+
@retry_async_decorator(retry_exceptions=GitHubException)
|
|
106
|
+
async def get_file_contents(self, path, ref=None):
|
|
107
|
+
"""Fetch the decoded contents of a file in the repository.
|
|
108
|
+
|
|
109
|
+
Args:
|
|
110
|
+
path (str): the path to the file, relative to the repository root
|
|
111
|
+
ref (str, optional): the commit/branch/tag to read the file from.
|
|
112
|
+
Defaults to the repository's default branch. Results for full
|
|
113
|
+
commit hashes are cached on disk in `work_dir`.
|
|
114
|
+
|
|
115
|
+
Returns:
|
|
116
|
+
str: the decoded contents of the file
|
|
117
|
+
|
|
118
|
+
"""
|
|
119
|
+
# Files at a full commit hash are immutable, so they're safe to cache on disk.
|
|
120
|
+
cache_path = None
|
|
121
|
+
if ref and _is_git_full_hash(ref):
|
|
122
|
+
cache_key = hashlib.sha1(f"{self._owner}/{self._repo_name}@{ref}:{path}".encode("utf-8")).hexdigest()
|
|
123
|
+
cache_path = Path(self._context.config["work_dir"]) / ".cache" / "github_files" / cache_key
|
|
124
|
+
if cache_path.exists():
|
|
125
|
+
return cache_path.read_text(encoding="utf-8")
|
|
126
|
+
|
|
127
|
+
repository = await self._get_repository()
|
|
128
|
+
contents = repository.file_contents(path, ref=ref)
|
|
129
|
+
decoded = contents.decoded.decode("utf-8")
|
|
130
|
+
|
|
131
|
+
if cache_path:
|
|
132
|
+
cache_path.parent.mkdir(parents=True, exist_ok=True)
|
|
133
|
+
cache_path.write_text(decoded, encoding="utf-8")
|
|
134
|
+
return decoded
|
|
51
135
|
|
|
52
136
|
@retry_async_decorator(retry_exceptions=GitHubException)
|
|
53
137
|
async def get_commit(self, commit_hash):
|
|
@@ -60,7 +144,8 @@ class GitHubRepository:
|
|
|
60
144
|
dict: a representation of the commit
|
|
61
145
|
|
|
62
146
|
"""
|
|
63
|
-
|
|
147
|
+
repository = await self._get_repository()
|
|
148
|
+
return repository.commit(commit_hash).as_dict()
|
|
64
149
|
|
|
65
150
|
@retry_async_decorator(retry_exceptions=GitHubException)
|
|
66
151
|
async def get_pull_request(self, pull_request_number):
|
|
@@ -73,7 +158,8 @@ class GitHubRepository:
|
|
|
73
158
|
dict: a representation of the pull request
|
|
74
159
|
|
|
75
160
|
"""
|
|
76
|
-
|
|
161
|
+
repository = await self._get_repository()
|
|
162
|
+
return repository.pull_request(pull_request_number).as_dict()
|
|
77
163
|
|
|
78
164
|
@retry_async_decorator(retry_exceptions=GitHubException)
|
|
79
165
|
async def get_release(self, tag_name):
|
|
@@ -86,7 +172,8 @@ class GitHubRepository:
|
|
|
86
172
|
dict: a representation of the tag
|
|
87
173
|
|
|
88
174
|
"""
|
|
89
|
-
|
|
175
|
+
repository = await self._get_repository()
|
|
176
|
+
return repository.release_from_tag(tag_name).as_dict()
|
|
90
177
|
|
|
91
178
|
@retry_async_decorator(retry_exceptions=GitHubException)
|
|
92
179
|
async def get_tag_hash(self, tag_name):
|
|
@@ -99,8 +186,9 @@ class GitHubRepository:
|
|
|
99
186
|
str: the commit hash linked by the tag
|
|
100
187
|
|
|
101
188
|
"""
|
|
189
|
+
repository = await self._get_repository()
|
|
102
190
|
tag_object = get_single_item_from_sequence(
|
|
103
|
-
sequence=
|
|
191
|
+
sequence=repository.tags(),
|
|
104
192
|
condition=lambda tag: tag.name == tag_name,
|
|
105
193
|
no_item_error_message='No tag "{}" exist'.format(tag_name),
|
|
106
194
|
too_many_item_error_message='Too many tags "{}" found'.format(tag_name),
|
|
@@ -129,7 +217,8 @@ class GitHubRepository:
|
|
|
129
217
|
if not _is_git_full_hash(revision):
|
|
130
218
|
revision = await self.get_tag_hash(tag_name=revision)
|
|
131
219
|
|
|
132
|
-
|
|
220
|
+
repository = await self._get_repository()
|
|
221
|
+
html_text = await _fetch_github_branch_commits_data(context, repository.html_url, revision)
|
|
133
222
|
|
|
134
223
|
# https://github.com/{repo_owner}/{repo_name}/branch_commits/{revision} just returns some \n
|
|
135
224
|
# when the commit hasn't landed on the origin repo. Otherwise, some HTML data is returned - it
|
|
@@ -550,7 +550,7 @@ async def is_pull_request(context, task):
|
|
|
550
550
|
if not revision and can_skip:
|
|
551
551
|
continue
|
|
552
552
|
|
|
553
|
-
github_repository = GitHubRepository(repo_owner, repo_name
|
|
553
|
+
github_repository = GitHubRepository(context, repo_owner, repo_name)
|
|
554
554
|
conditions.append(not await github_repository.has_commit_landed_on_repository(context, revision))
|
|
555
555
|
|
|
556
556
|
return any(conditions)
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{scriptworker-63.5.0 → scriptworker-63.6.0}/src/scriptworker/data/scriptworker_task_schema.json
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|