fix(scripts): forgejo timeout coverage for python oracles and create-readme #42
Loading…
Reference in a new issue
No description provided.
Delete branch "%!s()"
Deleting a branch is permanent. Although the deleted branch may continue to exist for a short time before it actually gets removed, it CANNOT be undone in most cases. Continue?
Контекст
PR#41 (issue #40) пофиксил hang в
_shared.ts:callForgejo(fetch timeout 10с × 3 retry +AbortSignal.any). Но аудит выявил непокрытые call sites к Forgejo API — тот же класс 5-мин hang'а:pipeline-status.py:110—_forgejo_requestвызываетurllib.request.urlopen(req)БЕЗtimeout=. 10 call sites (:128,157,164,194,238,253,281,302,332,350) наследуют безтаймаутный urlopen. Это оракул оркестратора — вызывается перед каждым pipeline-action. CI poll loopwait_timeout=300ограничивает ЦИКЛ, но не отдельныйurlopen— один hung-вызов побеждает бюджет.project-status.py:202—_forgejo_geturlopenбез timeout (branch protection check, 1 call site).spec-status.py:127—_forgejo_geturlopenбез timeout (issue view, 1 call site).create-readme.ts:316,332— прямойfetch()к Forgejo/contents/README.md(GET + PUT), без timeout/signal/retry. Полностью bypass'ит_shared.ts:callForgejo.pipeline-status.ts:12,project-status.ts:20,spec-status.ts:13—spawnSync("python3", [script])без timeout. Обёртки над Python-оракулами.Задача
pipeline-status.py:110_forgejo_request: добавитьurlopen(req, timeout=OPENCODE_FORGEJO_TIMEOUT)(10с дефолт) + retry-цикл (3×, 1с sleep, skip на первой попытке — как_retry_no_checks:589-606). Retry на timeout/5xx, НЕ на 4xx. При исчерпании — return(0, "", "[forgejo] ... failed after N retries").project-status.py:202_forgejo_get: тот же паттерн (urlopen timeout + retry).spec-status.py:127_forgejo_get: тот же паттерн.pipeline-status.ts:12,project-status.ts:20,spec-status.ts:13:spawnSync("python3", ...)добавитьtimeout: 60000(60с — с запасом над Python-внутренними 10с×3 retry = 32с).create-readme.ts:316,332: мигрировать прямойfetch()наcallForgejoиз_shared.ts(уже с timeout+retry+signal). Если миграция невозможна (другая семантика response) — добавитьAbortSignal.timeout(10000)+ retry-цикл локально, в стиле_shared.ts.OPENCODE_FORGEJO_TIMEOUT/RETRY/RETRY_INTERVALчерезos.environ.get(тот же helper-стиль что_env_intвpipeline-status.py:392-400).print(..., file=sys.stderr)с[forgejo]prefix (только retry/error, не успешные вызовы). TS —process.stderr.writeс[forgejo]prefix.tests/test_pipeline_status.py(если есть) на timeout/retry/4xx-no-retry. Дляcreate-readme— Bun test если возможно.Контракты
_forgejo_request(method, path, body)— сигнатура НЕ меняется. Внутренняя реализация добавляет timeout+retry.urlopen(req, timeout=N)— Python 3 stdlib поддерживаетtimeoutпараметр.urllib.error.URLError(timeout/connection) и HTTP 5xx; НЕ на 4xx.create-readme.ts— если мигрирует наcallForgejo, то import из_shared.ts. Если локальный retry — helper_fetchWithRetryвcreate-readme.ts.spawnSynctimeout: 60000мс для Python-оракулов (60с с запасом).Инварианты
wait_timeout=300(5 мин) — НЕ трогаем. Это by design (ожидание CI билда). Меняем только individualurlopentimeout._forgejo_request/_forgejo_getпродолжают работать (сигнатуры не меняются).OPENCODE_FORGEJO_RETRY=0— мгновенный фейл без retry.OPENCODE_FORGEJO_TIMEOUT=0— невалидный, fallback на дефолт 10с (логировать warning в stderr).Граничные случаи
urlopentimeout на медленном CI logs endpoint (/actions/jobs/{id}/logs) — может быть большой response. Timeout 10с может быть tight. Рассмотреть больший timeout для logs (20с?) или оставить 10с + retry.create-readme.tsPUT/contents/README.md— large body (README может быть 50KB+). Timeout 10с может быть tight для upload. Рассмотреть 30с для PUT-операций или оставить 10с + retry.pipeline-status.pyCI poll loop: после фикса каждая итерация опроса ограничена 10с + retry. Если CI полностью завис, poll loop теперь будет делать 3 retry за итерацию → 32с × (300/10) итераций = много. Но это лучше бесконечного hang на одном urlopen.spawnSync("python3", timeout: 60000)— если Python-скрипт превысит 60с (много retry), spawnSync убьёт процесс. TS-тул получит ошибку — нужно читаемое сообщение.Влияние на связанные компоненты
pipeline-statusTS tool →pipeline-status.py— транзитивно получает timeout+retry.project-statusTS tool →project-status.py— то же.spec-statusTS tool →spec-status.py— то же.create-readmetool — отдельный фикс (миграция наcallForgejoили локальный retry).Вне scope
wait_timeout=300— by design, не трогаем.Критерии приемки
pipeline-status.py:110urlopenимеетtimeout=OPENCODE_FORGEJO_TIMEOUT(10с default).pipeline-status.py_forgejo_requestретраит на timeout/5xx, НЕ на 4xx.project-status.py:202urlopenимеет timeout + retry.spec-status.py:127urlopenимеет timeout + retry.pipeline-status.ts:12,project-status.ts:20,spec-status.ts:13—spawnSync timeout: 60000.create-readme.ts:316,332— fetch с timeout (черезcallForgejoили локальныйAbortSignal.timeout).OPENCODE_FORGEJO_TIMEOUT/RETRY/RETRY_INTERVALчитаются в Python черезos.environ.get.[forgejo]prefix (Python:print(file=sys.stderr), TS:process.stderr.write).wait_timeout=300НЕ изменён.