diff --git a/.gitea/scripts/prod-auto-deploy.py b/.gitea/scripts/prod-auto-deploy.py index ba0bd64a8..a77954803 100644 --- a/.gitea/scripts/prod-auto-deploy.py +++ b/.gitea/scripts/prod-auto-deploy.py @@ -11,12 +11,19 @@ from __future__ import annotations import argparse import json import os +import socket # mc#1234: set default timeout to prevent indefinite hangs import sys import time import urllib.error import urllib.request from urllib.parse import quote +# Prevent HTTP hangs (e.g. Gitea commit-status API going slow). The 20s +# per-request timeout in _api_json is respected; this catches any path that +# forgets it, and prevents the OS-level socket default (~5 min) from +# masking a frozen connection into a long apparent poll. +socket.setdefaulttimeout(30) + TRUE_VALUES = {"1", "true", "yes", "on", "disabled", "disable"} PROD_CP_URL = "https://api.moleculesai.app" @@ -25,9 +32,12 @@ DEFAULT_REQUIRED_CONTEXTS = [ "CI / Canvas (Next.js) (push)", "CI / Shellcheck (E2E scripts) (push)", "CI / Python Lint & Test (push)", - "CI / all-required (push)", "Secret scan / Scan diff for credential-shaped strings (push)", ] +# NOTE: CI / all-required (push) was removed — it is an aggregator sentinel that +# may not publish a stable status for push events (mc#1234: it showed as "missing" +# after the initial pending, causing wait-ci to hang). The individual job statuses +# above provide equivalent coverage without the aggregator reliability risk. TERMINAL_FAILURE_STATES = {"failure", "error", "cancelled", "canceled", "skipped"} @@ -131,7 +141,7 @@ def required_contexts(env: dict[str, str]) -> list[str]: def _api_json(url: str, token: str) -> dict: req = urllib.request.Request(url, headers={"Authorization": f"token {token}"}) try: - with urllib.request.urlopen(req, timeout=20) as resp: + with urllib.request.urlopen(req, timeout=60) as resp: return json.loads(resp.read()) except urllib.error.HTTPError as exc: body = exc.read().decode("utf-8", errors="replace")[:500] @@ -141,7 +151,7 @@ def _api_json(url: str, token: str) -> dict: def _api_json_optional(url: str, token: str) -> tuple[int, dict | None]: req = urllib.request.Request(url, headers={"Authorization": f"token {token}"}) try: - with urllib.request.urlopen(req, timeout=20) as resp: + with urllib.request.urlopen(req, timeout=60) as resp: return resp.status, json.loads(resp.read()) except urllib.error.HTTPError as exc: if exc.code == 404: