From 9f0df716ca156c743737816bb209eb2404f76171 Mon Sep 17 00:00:00 2001 From: tianma-if Date: Fri, 11 Sep 2026 01:28:33 +0800 Subject: [PATCH] fix(ios): retry transient App Store Connect errors in Xcode Cloud monitor --- .github/workflows/ios-xcode-cloud.yml | 47 ++++++++++++++++++--------- 1 file changed, 31 insertions(+), 16 deletions(-) diff --git a/.github/workflows/ios-xcode-cloud.yml b/.github/workflows/ios-xcode-cloud.yml index 7da977f2..7c942ffc 100644 --- a/.github/workflows/ios-xcode-cloud.yml +++ b/.github/workflows/ios-xcode-cloud.yml @@ -96,22 +96,37 @@ jobs: token = encoded.decode() if isinstance(encoded, bytes) else encoded return token - def api(method, path, body=None): - request = urllib.request.Request( - "https://api.appstoreconnect.apple.com" + path, - data=None if body is None else json.dumps(body).encode(), - method=method, - ) - request.add_header("Authorization", f"Bearer {ensure_token()}") - if body is not None: - request.add_header("Content-Type", "application/json") - try: - with urllib.request.urlopen(request) as response: - payload = response.read().decode() - return json.loads(payload) if payload else {} - except urllib.error.HTTPError as error: - details = error.read().decode() - raise SystemExit(f"App Store Connect API {method} {path} failed ({error.code}): {details}") + def invalidate_token(): + global token, token_exp + token = None + token_exp = 0 + + def api(method, path, body=None, attempts=5): + last_error = None + for attempt in range(1, attempts + 1): + request = urllib.request.Request( + "https://api.appstoreconnect.apple.com" + path, + data=None if body is None else json.dumps(body).encode(), + method=method, + ) + request.add_header("Authorization", f"Bearer {ensure_token()}") + if body is not None: + request.add_header("Content-Type", "application/json") + try: + with urllib.request.urlopen(request) as response: + payload = response.read().decode() + return json.loads(payload) if payload else {} + except urllib.error.HTTPError as error: + details = error.read().decode() + last_error = f"App Store Connect API {method} {path} failed ({error.code}): {details}" + if error.code in (401, 429, 500, 502, 503, 504) and attempt < attempts: + if error.code == 401: + invalidate_token() + print(f"{last_error} retrying {attempt}/{attempts - 1}", flush=True) + time.sleep(min(30, 2 ** attempt)) + continue + raise SystemExit(last_error) + raise SystemExit(last_error or f"App Store Connect API {method} {path} failed") if mode == "monitor": if not build_run_id: