From 6a0f84ff5bd65944505385343bec8a9aa8f758d1 Mon Sep 17 00:00:00 2001 From: Emma Harper Smith Date: Mon, 19 May 2025 13:58:34 -0400 Subject: [PATCH 1/6] Add retries to generate_sbom.py generate_sbom.py downloads artifacts, which can flake. This commit adds exponential backoff to the download to avoid failures due to network flakes. --- Tools/build/generate_sbom.py | 18 +++++++++++++++++- 1 file changed, 17 insertions(+), 1 deletion(-) diff --git a/Tools/build/generate_sbom.py b/Tools/build/generate_sbom.py index db01426e9722c3..ca0dac06b38a4b 100644 --- a/Tools/build/generate_sbom.py +++ b/Tools/build/generate_sbom.py @@ -9,6 +9,7 @@ import sys import typing import urllib.request +import urllib.error from pathlib import Path, PurePosixPath, PureWindowsPath CPYTHON_ROOT_DIR = Path(__file__).parent.parent.parent @@ -161,6 +162,21 @@ def get_externals() -> list[str]: return externals +def download_with_retries(download_location, retries=5): + """Download a file with exponential backoff retry.""" + attempt = 0 + while attempt < retries: + attempt += 1 + try: + resp = urllib.request.urlopen(download_location) + except urllib.error.URLError as ex: + if attempt == retries: + raise ex + time.sleep(2**attempt) + else: + return resp + + def check_sbom_packages(sbom_data: dict[str, typing.Any]) -> None: """Make a bunch of assertions about the SBOM package data to ensure it's consistent.""" @@ -175,7 +191,7 @@ def check_sbom_packages(sbom_data: dict[str, typing.Any]) -> None: # and that the download URL is valid. if "checksums" not in package or "CI" in os.environ: download_location = package["downloadLocation"] - resp = urllib.request.urlopen(download_location) + resp = download_with_retries(download_location) error_if(resp.status != 200, f"Couldn't access URL: {download_location}'") package["checksums"] = [{ From 255cb80a735ea084ac7df758f0cbce4a62af1951 Mon Sep 17 00:00:00 2001 From: Emma Harper Smith Date: Mon, 19 May 2025 14:12:01 -0400 Subject: [PATCH 2/6] Fix mypy for retry function Ironic --- Tools/build/generate_sbom.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/Tools/build/generate_sbom.py b/Tools/build/generate_sbom.py index ca0dac06b38a4b..80c8707a85e59f 100644 --- a/Tools/build/generate_sbom.py +++ b/Tools/build/generate_sbom.py @@ -7,6 +7,7 @@ import re import subprocess import sys +import time import typing import urllib.request import urllib.error @@ -162,7 +163,7 @@ def get_externals() -> list[str]: return externals -def download_with_retries(download_location, retries=5): +def download_with_retries(download_location: str, retries: int = 5): """Download a file with exponential backoff retry.""" attempt = 0 while attempt < retries: From 95651664814eddf643a5ff96a07566d3e521ca77 Mon Sep 17 00:00:00 2001 From: Emma Harper Smith Date: Mon, 19 May 2025 14:22:42 -0400 Subject: [PATCH 3/6] Add Any return type --- Tools/build/generate_sbom.py | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/Tools/build/generate_sbom.py b/Tools/build/generate_sbom.py index 80c8707a85e59f..5246730d70b9f4 100644 --- a/Tools/build/generate_sbom.py +++ b/Tools/build/generate_sbom.py @@ -163,7 +163,9 @@ def get_externals() -> list[str]: return externals -def download_with_retries(download_location: str, retries: int = 5): +def download_with_retries( + download_location: str, + retries: int = 5) -> typing.Any: """Download a file with exponential backoff retry.""" attempt = 0 while attempt < retries: From 37aadf4b00849f26a37133395a87815bdac9abee Mon Sep 17 00:00:00 2001 From: Emma Harper Smith Date: Mon, 19 May 2025 14:24:32 -0400 Subject: [PATCH 4/6] Sort imports --- Tools/build/generate_sbom.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Tools/build/generate_sbom.py b/Tools/build/generate_sbom.py index 5246730d70b9f4..b3a9d7bf085c11 100644 --- a/Tools/build/generate_sbom.py +++ b/Tools/build/generate_sbom.py @@ -9,8 +9,8 @@ import sys import time import typing -import urllib.request import urllib.error +import urllib.request from pathlib import Path, PurePosixPath, PureWindowsPath CPYTHON_ROOT_DIR = Path(__file__).parent.parent.parent From dc830fe180407ab11edf4fdbdcc07a3aa9c38acb Mon Sep 17 00:00:00 2001 From: Emma Smith Date: Mon, 19 May 2025 12:45:05 -0700 Subject: [PATCH 5/6] Reformat function signature Co-authored-by: Semyon Moroz --- Tools/build/generate_sbom.py | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/Tools/build/generate_sbom.py b/Tools/build/generate_sbom.py index b3a9d7bf085c11..33a3e1770b6a1a 100644 --- a/Tools/build/generate_sbom.py +++ b/Tools/build/generate_sbom.py @@ -163,9 +163,8 @@ def get_externals() -> list[str]: return externals -def download_with_retries( - download_location: str, - retries: int = 5) -> typing.Any: +def download_with_retries(download_location: str, + retries: int = 5) -> typing.Any: """Download a file with exponential backoff retry.""" attempt = 0 while attempt < retries: From 8ade10416a653ff472d51c83387819287139faab Mon Sep 17 00:00:00 2001 From: Emma Harper Smith Date: Mon, 19 May 2025 15:52:15 -0400 Subject: [PATCH 6/6] Apply suggestions from review - Add `base_delay` for retries - use a for loop instead of while loop - rename retries to max_retries --- Tools/build/generate_sbom.py | 11 +++++------ 1 file changed, 5 insertions(+), 6 deletions(-) diff --git a/Tools/build/generate_sbom.py b/Tools/build/generate_sbom.py index 33a3e1770b6a1a..5845f2d85c7fdb 100644 --- a/Tools/build/generate_sbom.py +++ b/Tools/build/generate_sbom.py @@ -164,17 +164,16 @@ def get_externals() -> list[str]: def download_with_retries(download_location: str, - retries: int = 5) -> typing.Any: + max_retries: int = 5, + base_delay: float = 2.0) -> typing.Any: """Download a file with exponential backoff retry.""" - attempt = 0 - while attempt < retries: - attempt += 1 + for attempt in range(max_retries): try: resp = urllib.request.urlopen(download_location) except urllib.error.URLError as ex: - if attempt == retries: + if attempt == max_retries: raise ex - time.sleep(2**attempt) + time.sleep(base_delay**attempt) else: return resp