scriptworker 63.3.0__tar.gz → 63.4.1__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.3.0 → scriptworker-63.4.1}/HISTORY.rst +15 -0
- {scriptworker-63.3.0 → scriptworker-63.4.1}/PKG-INFO +2 -2
- {scriptworker-63.3.0 → scriptworker-63.4.1}/pyproject.toml +1 -1
- {scriptworker-63.3.0 → scriptworker-63.4.1}/src/scriptworker/artifacts.py +34 -4
- {scriptworker-63.3.0 → scriptworker-63.4.1}/src/scriptworker/cot/verify.py +19 -5
- {scriptworker-63.3.0 → scriptworker-63.4.1}/.gitignore +0 -0
- {scriptworker-63.3.0 → scriptworker-63.4.1}/CONTRIBUTING.rst +0 -0
- {scriptworker-63.3.0 → scriptworker-63.4.1}/LICENSE +0 -0
- {scriptworker-63.3.0 → scriptworker-63.4.1}/README.rst +0 -0
- {scriptworker-63.3.0 → scriptworker-63.4.1}/renovate.json +0 -0
- {scriptworker-63.3.0 → scriptworker-63.4.1}/scripts/gen_ed25519_key.py +0 -0
- {scriptworker-63.3.0 → scriptworker-63.4.1}/scriptworker.yaml.tmpl +0 -0
- {scriptworker-63.3.0 → scriptworker-63.4.1}/src/scriptworker/__init__.py +0 -0
- {scriptworker-63.3.0 → scriptworker-63.4.1}/src/scriptworker/client.py +0 -0
- {scriptworker-63.3.0 → scriptworker-63.4.1}/src/scriptworker/config.py +0 -0
- {scriptworker-63.3.0 → scriptworker-63.4.1}/src/scriptworker/constants.py +0 -0
- {scriptworker-63.3.0 → scriptworker-63.4.1}/src/scriptworker/context.py +0 -0
- {scriptworker-63.3.0 → scriptworker-63.4.1}/src/scriptworker/cot/__init__.py +0 -0
- {scriptworker-63.3.0 → scriptworker-63.4.1}/src/scriptworker/cot/generate.py +0 -0
- {scriptworker-63.3.0 → scriptworker-63.4.1}/src/scriptworker/data/cot_v1_schema.json +0 -0
- {scriptworker-63.3.0 → scriptworker-63.4.1}/src/scriptworker/data/scriptworker_task_schema.json +0 -0
- {scriptworker-63.3.0 → scriptworker-63.4.1}/src/scriptworker/ed25519.py +0 -0
- {scriptworker-63.3.0 → scriptworker-63.4.1}/src/scriptworker/exceptions.py +0 -0
- {scriptworker-63.3.0 → scriptworker-63.4.1}/src/scriptworker/github.py +0 -0
- {scriptworker-63.3.0 → scriptworker-63.4.1}/src/scriptworker/log.py +0 -0
- {scriptworker-63.3.0 → scriptworker-63.4.1}/src/scriptworker/task.py +0 -0
- {scriptworker-63.3.0 → scriptworker-63.4.1}/src/scriptworker/task_process.py +0 -0
- {scriptworker-63.3.0 → scriptworker-63.4.1}/src/scriptworker/utils.py +0 -0
- {scriptworker-63.3.0 → scriptworker-63.4.1}/src/scriptworker/worker.py +0 -0
- {scriptworker-63.3.0 → scriptworker-63.4.1}/tox.ini +0 -0
|
@@ -4,6 +4,21 @@ 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.4.1 - 2026-08-11
|
|
8
|
+
-------------------
|
|
9
|
+
|
|
10
|
+
Fixed
|
|
11
|
+
~~~~~
|
|
12
|
+
|
|
13
|
+
- Allow enterprise tasks to have comm source repositories
|
|
14
|
+
|
|
15
|
+
63.4.0 - 2026-08-11
|
|
16
|
+
-------------------
|
|
17
|
+
|
|
18
|
+
Added
|
|
19
|
+
~~~~~
|
|
20
|
+
- set Content-MD5 header to avoid silent data corruption uploading artifacts (#815)
|
|
21
|
+
|
|
7
22
|
63.3.0 - 2026-08-05
|
|
8
23
|
-------------------
|
|
9
24
|
|
|
@@ -6,6 +6,7 @@ in S3.
|
|
|
6
6
|
"""
|
|
7
7
|
|
|
8
8
|
import asyncio
|
|
9
|
+
import base64
|
|
9
10
|
import fnmatch
|
|
10
11
|
import gzip
|
|
11
12
|
import logging
|
|
@@ -21,7 +22,15 @@ from taskcluster.exceptions import TaskclusterFailure
|
|
|
21
22
|
from scriptworker.client import validate_artifact_url
|
|
22
23
|
from scriptworker.exceptions import DownloadError, ScriptWorkerRetryException, ScriptWorkerTaskException
|
|
23
24
|
from scriptworker.task import get_decision_task_id, get_run_id, get_task_id
|
|
24
|
-
from scriptworker.utils import
|
|
25
|
+
from scriptworker.utils import (
|
|
26
|
+
add_enumerable_item_to_dict,
|
|
27
|
+
download_file,
|
|
28
|
+
get_hash,
|
|
29
|
+
get_loggable_url,
|
|
30
|
+
raise_future_exceptions,
|
|
31
|
+
retry_async,
|
|
32
|
+
semaphore_wrapper,
|
|
33
|
+
)
|
|
25
34
|
|
|
26
35
|
log = logging.getLogger(__name__)
|
|
27
36
|
|
|
@@ -163,6 +172,8 @@ async def create_artifact(context, path, target_path, content_type, content_enco
|
|
|
163
172
|
payload = {"storageType": storage_type, "expires": expires or get_expiration_arrow(context).isoformat(), "contentType": content_type}
|
|
164
173
|
args = [get_task_id(context.claim_task), get_run_id(context.claim_task), target_path, payload]
|
|
165
174
|
|
|
175
|
+
content_md5 = await asyncio.to_thread(get_content_md5, path)
|
|
176
|
+
|
|
166
177
|
tc_response = await context.temp_queue.createArtifact(*args)
|
|
167
178
|
skip_auto_headers = [aiohttp.hdrs.CONTENT_TYPE]
|
|
168
179
|
loggable_url = get_loggable_url(tc_response["putUrl"])
|
|
@@ -172,7 +183,7 @@ async def create_artifact(context, path, target_path, content_type, content_enco
|
|
|
172
183
|
async with context.session.put(
|
|
173
184
|
tc_response["putUrl"],
|
|
174
185
|
data=fh,
|
|
175
|
-
headers=_craft_artifact_put_headers(content_type, content_encoding),
|
|
186
|
+
headers=_craft_artifact_put_headers(content_type, content_md5, content_encoding),
|
|
176
187
|
skip_auto_headers=skip_auto_headers,
|
|
177
188
|
compress=False,
|
|
178
189
|
) as resp:
|
|
@@ -180,6 +191,12 @@ async def create_artifact(context, path, target_path, content_type, content_enco
|
|
|
180
191
|
response_text = await resp.text()
|
|
181
192
|
log.info(response_text)
|
|
182
193
|
if resp.status not in (200, 204):
|
|
194
|
+
if content_md5 is not None and "BadDigest" in (response_text or ""):
|
|
195
|
+
log.error(
|
|
196
|
+
"{} was corrupted in transit: the storage backend rejected the body as not matching Content-MD5 {}. Retrying.".format(
|
|
197
|
+
target_path, content_md5
|
|
198
|
+
)
|
|
199
|
+
)
|
|
183
200
|
raise ScriptWorkerRetryException("Bad status {}".format(resp.status))
|
|
184
201
|
|
|
185
202
|
|
|
@@ -206,9 +223,22 @@ async def create_link_artifact(context, target_path, link_to, content_type, expi
|
|
|
206
223
|
await context.temp_queue.createArtifact(*args)
|
|
207
224
|
|
|
208
225
|
|
|
209
|
-
def
|
|
226
|
+
def get_content_md5(path):
|
|
227
|
+
"""Get the base64-encoded md5 digest of a file, formatted for ``Content-MD5``.
|
|
228
|
+
|
|
229
|
+
Args:
|
|
230
|
+
path (str): the path to the file to digest.
|
|
231
|
+
|
|
232
|
+
Returns:
|
|
233
|
+
str: the base64-encoded md5 digest.
|
|
234
|
+
|
|
235
|
+
"""
|
|
236
|
+
return base64.b64encode(bytes.fromhex(get_hash(path, hash_alg="md5"))).decode("ascii")
|
|
237
|
+
|
|
238
|
+
|
|
239
|
+
def _craft_artifact_put_headers(content_type, content_md5, encoding=None):
|
|
210
240
|
log.debug("{} {}".format(content_type, encoding))
|
|
211
|
-
headers = {aiohttp.hdrs.CONTENT_TYPE: content_type}
|
|
241
|
+
headers = {aiohttp.hdrs.CONTENT_TYPE: content_type, aiohttp.hdrs.CONTENT_MD5: content_md5}
|
|
212
242
|
|
|
213
243
|
if encoding is not None:
|
|
214
244
|
headers[aiohttp.hdrs.CONTENT_ENCODING] = encoding
|
|
@@ -1952,12 +1952,26 @@ def get_source_url(obj):
|
|
|
1952
1952
|
log.debug("Getting source url for {} {}...".format(obj.name, obj.task_id))
|
|
1953
1953
|
repo = get_repo(obj.task, source_env_prefix=source_env_prefix)
|
|
1954
1954
|
source = task["metadata"]["source"]
|
|
1955
|
-
|
|
1956
|
-
|
|
1957
|
-
|
|
1958
|
-
|
|
1955
|
+
cot_product = obj.context.config["cot_product"]
|
|
1956
|
+
match_repo = repo and verify_repo_matches_url(repo, source)
|
|
1957
|
+
if cot_product != "enterprise":
|
|
1958
|
+
if repo and not match_repo:
|
|
1959
|
+
raise CoTError(
|
|
1960
|
+
"{name} {task_id}: {source_env_prefix} {repo} doesn't match source {source}!".format(
|
|
1961
|
+
name=obj.name, task_id=obj.task_id, source_env_prefix=source_env_prefix, repo=repo, source=source
|
|
1962
|
+
)
|
|
1959
1963
|
)
|
|
1960
|
-
|
|
1964
|
+
else:
|
|
1965
|
+
comm_repo = get_repo(obj.task, source_env_prefix="COMM", repo_type="head")
|
|
1966
|
+
match_comm_repo = comm_repo and verify_repo_matches_url(comm_repo, source)
|
|
1967
|
+
|
|
1968
|
+
if not match_repo and not match_comm_repo:
|
|
1969
|
+
raise CoTError(
|
|
1970
|
+
"{name} {task_id}: {source_env_prefix} {repo} or {comm_repo} doesn't match source {source}!".format(
|
|
1971
|
+
name=obj.name, task_id=obj.task_id, source_env_prefix=source_env_prefix, repo=repo, comm_repo=comm_repo, source=source
|
|
1972
|
+
)
|
|
1973
|
+
)
|
|
1974
|
+
|
|
1961
1975
|
log.info("{} {}: found {}".format(obj.name, obj.task_id, source))
|
|
1962
1976
|
return source
|
|
1963
1977
|
|
|
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
|
|
File without changes
|
{scriptworker-63.3.0 → scriptworker-63.4.1}/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
|
|
File without changes
|
|
File without changes
|