ci(core): parallelize UI artifacts' generation
What changed, and why it matters
This commit is a harmless internal CI tooling change. It speeds up a test-artifact packaging script by using multiple parallel workers and removes duplicate ZIP files that could overwrite each other. There is no user-facing or security-relevant change to the Trezor firmware itself.
No security action needed; this is a routine CI optimization.
Security signals we found
No strong security signals were identified.
Evidence from the diff
The change refactors ci/prepare_ui_artifacts.py to parallelize two steps (hashing test results and creating ZIP archives) using multiprocessing.Pool. It also deduplicates archives by actual_hash so multiple tests with the same hash no longer overwrite the same ZIP. The script still validates that each passed test’s actual hash matches the expected hash before archiving. No cryptographic, firmware, or device code is modified.
Changed components
ci/prepare_ui_artifacts.pyInspect captured patch +29 / −5
diff --git a/ci/prepare_ui_artifacts.py b/ci/prepare_ui_artifacts.py
index c31d6889..74d0a580 100644
--- a/ci/prepare_ui_artifacts.py
+++ b/ci/prepare_ui_artifacts.py
@@ -1,5 +1,6 @@
import shutil
import sys
+from multiprocessing import Pool
from pathlib import Path
ROOT = Path(__file__).resolve().parent.parent
@@ -10,10 +11,11 @@ from tests.ui_tests.common import get_current_fixtures # isort:skip
FIXTURES = get_current_fixtures()
-for result in TestResult.recent_results():
+
+def compute_hash(result: TestResult) -> TestResult | None:
if not result.passed or result.expected_hash != result.actual_hash:
print("WARNING: skipping failed test", result.test.id)
- continue
+ return None
actual_hash = _hash_files(result.test.actual_dir)
expected_hash = (
@@ -23,6 +25,28 @@ for result in TestResult.recent_results():
)
assert result.expected_hash == actual_hash
assert expected_hash == actual_hash
- shutil.make_archive(
- str(ROOT / "ci/ui_test_records" / actual_hash), "zip", result.test.actual_dir
- )
+ return result
+
+
+def create_zip(item: tuple[str, TestResult]) -> None:
+ archive_path, result = item
+ shutil.make_archive(archive_path, "zip", result.test.actual_dir)
+
+
+def main():
+ with Pool() as pool:
+ all_results = list(TestResult.recent_results())
+ print(f"Hashing {len(all_results)} results")
+ # deduplicate results by `actual_hash`, and skip failed tests
+ results_map = {
+ str(ROOT / "ci/ui_test_records" / result.actual_hash): result
+ for result in pool.imap_unordered(compute_hash, all_results)
+ if result is not None
+ }
+ print(f"Creating {len(results_map)} ZIP files")
+ for _ in pool.imap_unordered(create_zip, results_map.items()):
+ pass
+
+
+if __name__ == "__main__":
+ main()
Why this scored 15/100
Community notes
Notes can correct, qualify, or add evidence to the AI analysis. Every note shown here has been validated by a human moderator.
The AI analysis stands alone for now. Submit a note if you can add evidence or important context.