This is an automated email from the ASF dual-hosted git repository.
sbp pushed a commit to branch sbp
in repository https://gitbox.apache.org/repos/asf/tooling-trusted-releases.git
The following commit(s) were added to refs/heads/sbp by this push:
new 427a333e Propagate archive extraction error messages
427a333e is described below
commit 427a333e8cfd927aee4a1236e2a3feef33c2fb97
Author: Sean B. Palmer <[email protected]>
AuthorDate: Sun Mar 8 14:50:44 2026 +0000
Propagate archive extraction error messages
---
atr/tasks/quarantine.py | 15 +++++++---
tests/unit/test_quarantine_task.py | 60 ++++++++++++++++++++++++++++++++------
2 files changed, 62 insertions(+), 13 deletions(-)
diff --git a/atr/tasks/quarantine.py b/atr/tasks/quarantine.py
index b0d16f23..8cb699ea 100644
--- a/atr/tasks/quarantine.py
+++ b/atr/tasks/quarantine.py
@@ -84,9 +84,11 @@ async def validate(args: QuarantineValidate) ->
results.Results | None:
return None
try:
- await _extract_archives_to_cache(args.archives, quarantine_dir,
str(project_name), str(version_name))
- except Exception:
- await _mark_failed(quarantined, file_entries, "Archive extraction to
cache failed")
+ await _extract_archives_to_cache(
+ args.archives, quarantine_dir, str(project_name),
str(version_name), file_entries
+ )
+ except Exception as exc:
+ await _mark_failed(quarantined, file_entries, f"Archive extraction to
cache failed: {exc}")
await aioshutil.rmtree(quarantine_dir)
return None
@@ -99,6 +101,7 @@ async def _extract_archives_to_cache(
quarantine_dir: pathlib.Path,
project_name: str,
version_name: str,
+ file_entries: list[sql.QuarantineFileEntryV1],
) -> None:
conf = config.get()
cache_base = paths.get_cache_archives_dir() / project_name / version_name
@@ -143,9 +146,13 @@ async def _extract_archives_to_cache(
await aioshutil.rmtree(staging_dir, ignore_errors=True)
else:
raise
- except Exception:
+ except Exception as exc:
log.exception(f"Failed to extract archive {archive.rel_path} to
cache")
await aioshutil.rmtree(staging_dir, ignore_errors=True)
+ for entry in file_entries:
+ if entry.rel_path == archive.rel_path:
+ entry.errors.append(f"Extraction failed: {exc}")
+ break
raise
diff --git a/tests/unit/test_quarantine_task.py
b/tests/unit/test_quarantine_task.py
index 08ad6326..6be95e3d 100644
--- a/tests/unit/test_quarantine_task.py
+++ b/tests/unit/test_quarantine_task.py
@@ -31,7 +31,7 @@ import atr.tasks.quarantine as quarantine
@pytest.mark.asyncio
-async def
test_extract_archives_to_cache_discards_staging_dir_when_other_worker_wins(
+async def
test_extract_archives_to_cache_discards_staging_dir_on_enotempty_collision(
monkeypatch: pytest.MonkeyPatch, tmp_path: pathlib.Path
) -> None:
quarantine_dir = tmp_path / "quarantine"
@@ -52,21 +52,24 @@ async def
test_extract_archives_to_cache_discards_staging_dir_when_other_worker_
await aiofiles.os.makedirs(dst_path, exist_ok=True)
async with aiofiles.open(dst_path / "winner.txt", "w") as f:
await f.write("winner")
- raise FileExistsError(dst)
+ raise OSError(errno.ENOTEMPTY, "Directory not empty", str(dst_path))
monkeypatch.setattr(quarantine.paths, "get_cache_archives_dir", lambda:
cache_root)
monkeypatch.setattr(quarantine.paths, "get_tmp_dir", lambda: tmp_root)
monkeypatch.setattr(quarantine.exarch, "extract_archive", extract_archive)
monkeypatch.setattr(quarantine.aiofiles.os, "rename", rename)
+ entries = [sql.QuarantineFileEntryV1(rel_path=archive_rel_path,
size_bytes=7, content_hash="blake3:ghi", errors=[])]
+
await quarantine._extract_archives_to_cache(
- [quarantine.QuarantineArchiveEntry(rel_path=archive_rel_path,
content_hash="blake3:def")],
+ [quarantine.QuarantineArchiveEntry(rel_path=archive_rel_path,
content_hash="blake3:ghi")],
quarantine_dir,
"proj",
"1.0",
+ entries,
)
- cache_dir = cache_root / "proj" / "1.0" /
quarantine.hashes.filesystem_cache_archives_key("blake3:def")
+ cache_dir = cache_root / "proj" / "1.0" /
quarantine.hashes.filesystem_cache_archives_key("blake3:ghi")
assert cache_dir.is_dir()
assert (cache_dir / "winner.txt").read_text() == "winner"
@@ -74,7 +77,7 @@ async def
test_extract_archives_to_cache_discards_staging_dir_when_other_worker_
@pytest.mark.asyncio
-async def
test_extract_archives_to_cache_discards_staging_dir_on_enotempty_collision(
+async def
test_extract_archives_to_cache_discards_staging_dir_when_other_worker_wins(
monkeypatch: pytest.MonkeyPatch, tmp_path: pathlib.Path
) -> None:
quarantine_dir = tmp_path / "quarantine"
@@ -95,27 +98,63 @@ async def
test_extract_archives_to_cache_discards_staging_dir_on_enotempty_colli
await aiofiles.os.makedirs(dst_path, exist_ok=True)
async with aiofiles.open(dst_path / "winner.txt", "w") as f:
await f.write("winner")
- raise OSError(errno.ENOTEMPTY, "Directory not empty", str(dst_path))
+ raise FileExistsError(dst)
monkeypatch.setattr(quarantine.paths, "get_cache_archives_dir", lambda:
cache_root)
monkeypatch.setattr(quarantine.paths, "get_tmp_dir", lambda: tmp_root)
monkeypatch.setattr(quarantine.exarch, "extract_archive", extract_archive)
monkeypatch.setattr(quarantine.aiofiles.os, "rename", rename)
+ entries = [sql.QuarantineFileEntryV1(rel_path=archive_rel_path,
size_bytes=7, content_hash="blake3:def", errors=[])]
+
await quarantine._extract_archives_to_cache(
- [quarantine.QuarantineArchiveEntry(rel_path=archive_rel_path,
content_hash="blake3:ghi")],
+ [quarantine.QuarantineArchiveEntry(rel_path=archive_rel_path,
content_hash="blake3:def")],
quarantine_dir,
"proj",
"1.0",
+ entries,
)
- cache_dir = cache_root / "proj" / "1.0" /
quarantine.hashes.filesystem_cache_archives_key("blake3:ghi")
+ cache_dir = cache_root / "proj" / "1.0" /
quarantine.hashes.filesystem_cache_archives_key("blake3:def")
assert cache_dir.is_dir()
assert (cache_dir / "winner.txt").read_text() == "winner"
assert not recorded["staging_dir"].exists()
[email protected]
+async def test_extract_archives_to_cache_propagates_exarch_error_to_file_entry(
+ monkeypatch: pytest.MonkeyPatch, tmp_path: pathlib.Path
+) -> None:
+ quarantine_dir = tmp_path / "quarantine"
+ quarantine_dir.mkdir()
+ archive_rel_path = "artifact.tar.gz"
+ (quarantine_dir / archive_rel_path).write_bytes(b"archive")
+ cache_root = tmp_path / "cache"
+ tmp_root = tmp_path / "temporary"
+
+ def extract_archive(_archive_path: str, _extract_dir: str, _config:
object) -> None:
+ raise RuntimeError("unsafe zip detected")
+
+ monkeypatch.setattr(quarantine.paths, "get_cache_archives_dir", lambda:
cache_root)
+ monkeypatch.setattr(quarantine.paths, "get_tmp_dir", lambda: tmp_root)
+ monkeypatch.setattr(quarantine.exarch, "extract_archive", extract_archive)
+
+ entries = [sql.QuarantineFileEntryV1(rel_path=archive_rel_path,
size_bytes=7, content_hash="blake3:bad", errors=[])]
+
+ with pytest.raises(RuntimeError, match="unsafe zip detected"):
+ await quarantine._extract_archives_to_cache(
+ [quarantine.QuarantineArchiveEntry(rel_path=archive_rel_path,
content_hash="blake3:bad")],
+ quarantine_dir,
+ "proj",
+ "1.0",
+ entries,
+ )
+
+ assert len(entries[0].errors) == 1
+ assert "unsafe zip detected" in entries[0].errors[0]
+
+
@pytest.mark.asyncio
async def test_extract_archives_to_cache_stages_in_temporary_then_promotes(
monkeypatch: pytest.MonkeyPatch, tmp_path: pathlib.Path
@@ -138,11 +177,14 @@ async def
test_extract_archives_to_cache_stages_in_temporary_then_promotes(
monkeypatch.setattr(quarantine.paths, "get_tmp_dir", lambda: tmp_root)
monkeypatch.setattr(quarantine.exarch, "extract_archive", extract_archive)
+ entries = [sql.QuarantineFileEntryV1(rel_path=archive_rel_path,
size_bytes=7, content_hash="blake3:abc", errors=[])]
+
await quarantine._extract_archives_to_cache(
[quarantine.QuarantineArchiveEntry(rel_path=archive_rel_path,
content_hash="blake3:abc")],
quarantine_dir,
"proj",
"1.0",
+ entries,
)
cache_dir = cache_root / "proj" / "1.0" /
quarantine.hashes.filesystem_cache_archives_key("blake3:abc")
@@ -312,7 +354,7 @@ async def
test_validate_extraction_failure_marks_failed_and_deletes_dir(tmp_path
)
assert result is None
- mock_mark.assert_awaited_once_with(row, ok_entries, "Archive extraction to
cache failed")
+ mock_mark.assert_awaited_once_with(row, ok_entries, "Archive extraction to
cache failed: Extraction failure")
mock_rmtree.assert_awaited_once_with(quarantine_dir)
---------------------------------------------------------------------
To unsubscribe, e-mail: [email protected]
For additional commands, e-mail: [email protected]