nisse-review: a new head's review waits for its CI
A review judges the CI result (R1), so a new head's review now waits while its checks are pending, and starts once they finish, passed or failed. It also keeps reviews off a GPU that CI is using: #154's first review ran beside CI's end-to-end job and took about 30 minutes. - Pending: waits up to tuning.review.ci_wait_s (1 h), then reviews anyway. - No checks: waits tuning.review.ci_grace_s (2 min) for them to appear, then reviews. - A PR waiting on CI holds up no other: the oldest settled one goes first. - The head's first sighting is kept on the item (seen_at). Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01J5Utyfzw7gF7UhjRDEtjTk
This commit is contained in:
1 parent
3283f9540d
commit
be5ea5f170
8 files changed
+134
-6
No files matched your search
@@ -342,6 +342,58 @@ def test_a_repository_whose_prs_cannot_be_listed_holds_up_no_other(
|
||||
assert '"intake.repo_failed"' in out.getvalue() and "a/empty" in out.getvalue()
|
||||
|
||||
|
||||
class Clock:
|
||||
def __init__(self) -> None:
|
||||
self.now = 1000.0
|
||||
|
||||
def __call__(self) -> float:
|
||||
return self.now
|
||||
|
||||
|
||||
def test_a_new_head_waits_for_its_ci_to_finish(tmp_path: Path) -> None:
|
||||
# Follow-up to #154: judged with CI's result, and off CI's GPU.
|
||||
clock = Clock()
|
||||
gitea = FakeGitea(repos={REPO: [pr()]})
|
||||
head = pr().head_sha
|
||||
gitea.ci[head] = "pending"
|
||||
w = world(tmp_path, Scripted(*REVIEW), gitea, clock=clock)
|
||||
assert w["scheduler"].round() is False # pending: not yet
|
||||
clock.now += 600
|
||||
assert w["scheduler"].round() is False
|
||||
gitea.ci[head] = "failure" # finished: failing CI is worth a review too
|
||||
assert w["scheduler"].round() is True
|
||||
assert item(w).position == REVIEWED
|
||||
|
||||
|
||||
def test_ci_pending_too_long_is_reviewed_anyway(tmp_path: Path) -> None:
|
||||
clock = Clock()
|
||||
gitea = FakeGitea(repos={REPO: [pr()]})
|
||||
gitea.ci[pr().head_sha] = "pending"
|
||||
w = world(tmp_path, Scripted(*REVIEW), gitea, clock=clock, ci_wait_s=3600)
|
||||
assert w["scheduler"].round() is False
|
||||
clock.now += 3600
|
||||
assert w["scheduler"].round() is True # a stuck runner blocks nothing
|
||||
|
||||
|
||||
def test_no_ci_waits_only_a_grace_for_checks_to_appear(tmp_path: Path) -> None:
|
||||
clock = Clock()
|
||||
gitea = FakeGitea(repos={REPO: [pr()]})
|
||||
gitea.ci[pr().head_sha] = "none"
|
||||
w = world(tmp_path, Scripted(*REVIEW), gitea, clock=clock, ci_grace_s=120)
|
||||
assert w["scheduler"].round() is False
|
||||
clock.now += 120
|
||||
assert w["scheduler"].round() is True
|
||||
|
||||
|
||||
def test_a_pr_waiting_on_ci_holds_up_no_other(tmp_path: Path) -> None:
|
||||
clock = Clock()
|
||||
gitea = FakeGitea(repos={REPO: [pr(7), pr(8, head_sha="c8")]})
|
||||
gitea.ci[pr(7).head_sha] = "pending"
|
||||
w = world(tmp_path, Scripted(*REVIEW), gitea, clock=clock)
|
||||
assert w["scheduler"].round() is True # 8, though 7 is older
|
||||
assert (item(w, 7).position, item(w, 8).position) == (READY, REVIEWED)
|
||||
|
||||
|
||||
def test_a_configured_repository_it_cannot_read_is_left_out() -> None:
|
||||
gitea = FakeGitea(repos={REPO: []})
|
||||
out = io.StringIO()
|
||||
|
||||
Reference in new issue
Block a user