Trust test: wait for Gitea to dismiss a stale approval before merging (#162) #164
@@ -5,6 +5,7 @@ and a commit pushed after the approval needs approving again: at once by the
|
||||
agent's own check, and by Gitea once it has dismissed the approval."""
|
||||
|
||||
import base64
|
||||
import re
|
||||
import time
|
||||
import uuid
|
||||
from pathlib import Path
|
||||
@@ -44,13 +45,29 @@ def wait_for_dismissal(gitea: GiteaClient, repo: str, pr: int) -> None:
|
||||
in the push itself, and its merge check counts what isn't dismissed yet.
|
||||
A merge tried before that job has run can still go through (#162)."""
|
||||
deadline = time.monotonic() + DISMISS_WAIT_S
|
||||
|
|
||||
while gitea.approvals(repo, pr): # dismissed ones aren't listed
|
||||
while True:
|
||||
try:
|
||||
if not gitea.approvals(repo, pr): # dismissed ones aren't listed
|
||||
return
|
||||
except GiteaError as exc:
|
||||
# A busy Gitea may drop a call or answer 5xx; ask again. Anything
|
||||
# else, such as a 4xx, is a real failure.
|
||||
if not transient(exc) or time.monotonic() > deadline:
|
||||
raise
|
||||
assert time.monotonic() < deadline, (
|
||||
f"Gitea didn't dismiss the approval within {DISMISS_WAIT_S}s"
|
||||
)
|
||||
time.sleep(0.5)
|
||||
|
||||
|
||||
def transient(exc: GiteaError) -> bool:
|
||||
"""A dropped connection or a 5xx: worth asking again."""
|
||||
text = str(exc)
|
||||
return text.startswith("gitea request failed") or bool(
|
||||
re.search(r" returned 5\d\d:", text)
|
||||
)
|
||||
|
||||
|
||||
def test_merging_needs_the_operators_approval_of_the_pr_as_it_is(
|
||||
tmp_path: Path, live_config: AgentConfig, gitea: GiteaClient
|
||||
) -> None:
|
||||
|
||||
Reference in new issue
Block a user
This loop makes up to DISMISS_WAIT_S / 0.5 ≈ 60 GET calls, so it has far more exposure to a transient Gitea error (a 5xx, a dropped connection) than any single call elsewhere in the test. A one-off
GiteaErrorinside the window would fail the test even though the fix is working — an ironic new flake source in a PR whose whole purpose is to remove one. The premise is that Gitea can be busy/slow on CI, and a busy Gitea is also the likelier to return a transient error.The sibling live test (
test_end_to_end_loop.py,merge()) already absorbs a known transient Gitea response ("Please try again later") by retrying to a deadline rather than failing. You might do the same here: catchGiteaErrorinside the loop and, while under the deadline, sleep and re-check instead of propagating (ideally only for transient ones, letting a genuine 4xx fail fast). A persistent failure would still hit the deadline and fail, so nothing real is masked — the wait just becomes robust.Agreed, done in
a1eeab1. The loop now asks again after a dropped connection or a 5xx (transient()), up to the same 30 s deadline; any otherGiteaError, such as a 4xx, is raised at once. In practice the loop usually finishes on the first or second call (the job takes about 0.2 s), but a busy Gitea is exactly when it would matter.