From 89502c326d0aab93d4e60b7103f5738593d93d6b Mon Sep 17 00:00:00 2001 From: michaelmartinez Date: Mon, 22 Dec 2025 14:23:02 -0800 Subject: [PATCH] update retry logic and logging ### What 1. configureable retry count 2. additional logging ### Why 1. pass retry count as a command line arg; default 5 2. show details when api requests fail ### Testing before merge compiles cleanly ### Validation after merge compile and test ### Issue addressed by this PR https://github.com/stellar/ops/issues/2039 --- github_backup/cli.py | 2 ++ github_backup/github_backup.py | 21 ++++++++++++++++----- github_backup/max_retries.py | 1 + 3 files changed, 19 insertions(+), 5 deletions(-) create mode 100644 github_backup/max_retries.py diff --git a/github_backup/cli.py b/github_backup/cli.py index 54849d4..cdc9c5f 100644 --- a/github_backup/cli.py +++ b/github_backup/cli.py @@ -4,6 +4,7 @@ import logging import os import sys +from github_backup import max_retries from github_backup.github_backup import ( backup_account, @@ -39,6 +40,7 @@ logging.basicConfig(level=logging.INFO, handlers=[stdout_handler, stderr_handler def main(): """Main entry point for github-backup CLI.""" args = parse_args() + max_retries.MAX_RETRIES = args.max_retries if args.private and not get_auth(args): logger.warning( diff --git a/github_backup/github_backup.py b/github_backup/github_backup.py index 1d4e354..13cda22 100644 --- a/github_backup/github_backup.py +++ b/github_backup/github_backup.py @@ -25,6 +25,7 @@ from http.client import IncompleteRead from urllib.error import HTTPError, URLError from urllib.parse import urlencode, urlparse from urllib.request import HTTPRedirectHandler, Request, build_opener, urlopen +from github_backup import max_retries try: from . import __version__ @@ -75,7 +76,7 @@ else: ) # Retry configuration -MAX_RETRIES = 5 +MAX_RETRIES = max_retries.MAX_RETRIES def logging_subprocess( @@ -468,6 +469,13 @@ def parse_args(args=None): parser.add_argument( "--exclude", dest="exclude", help="names of repositories to exclude", nargs="*" ) + parser.add_argument( + "--retries", + dest="max_retries", + type=int, + default=5, + help="maximum number of retries for API calls (default: 5)", + ) return parser.parse_args(args) @@ -737,16 +745,19 @@ def make_request_with_retry(request, auth): except HTTPError as exc: # HTTPError can be used as a response-like object if not is_retryable_status(exc.code, exc.headers): + logger.error(f"API Error: {exc.code} {exc.reason} for {request.full_url}") raise # Non-retryable error if attempt >= MAX_RETRIES - 1: logger.error(f"HTTP {exc.code} failed after {MAX_RETRIES} attempts") + logger.error(f"HTTP {exc.code} failed after {MAX_RETRIES} attempts for {request.full_url}") raise delay = calculate_retry_delay(attempt, exc.headers) logger.warning( - f"HTTP {exc.code}, retrying in {delay:.1f}s " - f"(attempt {attempt + 1}/{MAX_RETRIES})" + f"HTTP {exc.code} ({exc.reason}), retrying in {delay:.1f}s " + f"(attempt {attempt + 1}/{MAX_RETRIES}) for {request.full_url}" + ) if auth is None and exc.code in (403, 429): logger.info("Hint: Authenticate to raise your GitHub rate limit") @@ -754,12 +765,12 @@ def make_request_with_retry(request, auth): except (URLError, socket.error) as e: if attempt >= MAX_RETRIES - 1: - logger.error(f"Connection error failed after {MAX_RETRIES} attempts: {e}") + logger.error(f"Connection error failed after {MAX_RETRIES} attempts: {e} for {request.full_url}") raise delay = calculate_retry_delay(attempt, {}) logger.warning( f"Connection error: {e}, retrying in {delay:.1f}s " - f"(attempt {attempt + 1}/{MAX_RETRIES})" + f"(attempt {attempt + 1}/{MAX_RETRIES}) for {request.full_url}" ) time.sleep(delay) diff --git a/github_backup/max_retries.py b/github_backup/max_retries.py new file mode 100644 index 0000000..3bd0f5d --- /dev/null +++ b/github_backup/max_retries.py @@ -0,0 +1 @@ +MAX_RETRIES=None