ci(auto-merge): stop requiring Greptile and Bugbot on price sync pull requests

Co-Authored-By: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com>
This commit is contained in:
kerry 2026-09-17 21:28:25 +00:00
parent bc9f4fec5b
commit 733a8a482e
2 changed files with 4 additions and 180 deletions

View file

@ -1,9 +1,9 @@
"""Auto-merge the provider-info-sync bot's cost-map pull requests.
Evaluates every gate (author allowlist, cost-map-only diff, required and
non-required checks, Greptile confidence, Bugbot review, human reviews) and
merges with a merge commit when all of them hold. Every hold reason is
logged; the process exits 0 on hold and 1 only on API or programming errors.
non-required checks, human reviews) and merges with a merge commit when
all of them hold. Every hold reason is logged; the process exits 0 on hold
and 1 only on API or programming errors.
``DRY_RUN=1`` prints the verdict without calling the merge endpoint.
"""
@ -11,7 +11,6 @@ from __future__ import annotations
import json
import os
import re
import subprocess
import sys
import time
@ -27,12 +26,6 @@ CLASSIFY_SCRIPT: Final = os.path.join(REPO_ROOT, ".circleci", "scripts", "classi
API_ROOT: Final = "https://api.github.com"
CHANGED_FILE_CEILING: Final = 3000
OK_CHECK_CONCLUSIONS: Final = frozenset({"success", "skipped", "neutral"})
GREPTILE_LOGIN: Final = "greptile-apps[bot]"
BUGBOT_LOGIN: Final = "cursor[bot]"
GREPTILE_SCORE_RE: Final = re.compile(r"Confidence Score:\s*(\d)/5")
BUGBOT_REVIEW_MARKER: Final = "<!-- BUGBOT_REVIEW -->"
BUGBOT_STALE_MARKER: Final = "<!-- BUGBOT_REVIEW_STALE -->"
BUGBOT_CLEAN: Final = "found no new issues"
@dataclass(frozen=True, slots=True)
@ -60,13 +53,6 @@ class CommitStatus:
state: str
@dataclass(frozen=True, slots=True)
class IssueComment:
author_login: str
body: str
updated_at: datetime
@dataclass(frozen=True, slots=True)
class Review:
author_login: str
@ -89,9 +75,7 @@ class EvaluationInputs:
required_contexts: frozenset[str]
check_runs: tuple[CheckRun, ...]
statuses: tuple[CommitStatus, ...]
comments: tuple[IssueComment, ...]
reviews: tuple[Review, ...]
head_commit_date: datetime
self_check_name: str
author_allowlist: frozenset[str]
@ -155,37 +139,6 @@ def evaluate(
if status.state != "success":
reasons.append(f"commit status {status.context!r} is {status.state}")
greptile: Final = tuple(
comment
for comment in inputs.comments
if comment.author_login == GREPTILE_LOGIN and GREPTILE_SCORE_RE.search(comment.body)
)
if not greptile:
reasons.append("greptile score not available")
else:
latest: Final = max(greptile, key=lambda comment: comment.updated_at)
match: Final = GREPTILE_SCORE_RE.search(latest.body)
score: Final = int(match.group(1)) if match else 0
if latest.updated_at < inputs.head_commit_date:
reasons.append("greptile score older than head commit")
elif score != 5:
reasons.append(f"greptile score {score}/5 below 5")
bugbot: Final = tuple(
review
for review in inputs.reviews
if review.author_login == BUGBOT_LOGIN
and BUGBOT_REVIEW_MARKER in review.body
and BUGBOT_STALE_MARKER not in review.body
and review.commit_id == pr.head_sha
)
if not bugbot:
reasons.append("bugbot review not available")
else:
latest_review: Final = max(bugbot, key=lambda review: review.submitted_at)
if BUGBOT_CLEAN not in latest_review.body:
reasons.append("bugbot reported issues")
latest_state_by_reviewer: Final[dict[str, str]] = {}
for review in sorted(inputs.reviews, key=lambda review: review.submitted_at):
if _is_bot_login(review.author_login):
@ -350,19 +303,6 @@ def _statuses(token: str, repo: str, sha: str) -> tuple[CommitStatus, ...]:
)
def _comments(token: str, repo: str, number: int) -> tuple[IssueComment, ...]:
comments: Final = _paginate(token, f"/repos/{repo}/issues/{number}/comments")
return tuple(
IssueComment(
author_login=_text(_nested(item, "user", "login")),
body=_text(item.get("body")),
updated_at=_parse_time(item.get("updated_at")),
)
for item in comments
if isinstance(item, Mapping)
)
def _reviews(token: str, repo: str, number: int) -> tuple[Review, ...]:
reviews: Final = _paginate(token, f"/repos/{repo}/pulls/{number}/reviews")
return tuple(
@ -378,16 +318,6 @@ def _reviews(token: str, repo: str, number: int) -> tuple[Review, ...]:
)
def _head_commit_date(token: str, repo: str, number: int) -> datetime:
commits: Final = _paginate(token, f"/repos/{repo}/pulls/{number}/commits")
if not commits:
return datetime.min.replace(tzinfo=timezone.utc)
last: Final = commits[-1]
if not isinstance(last, Mapping):
return datetime.min.replace(tzinfo=timezone.utc)
return _parse_time(_nested(last, "commit", "committer", "date"))
def _mergeable_or_refetch(token: str, repo: str, pr: PullRequest) -> PullRequest:
if pr.mergeable is not None:
return pr
@ -410,9 +340,7 @@ def _gather_inputs(
required_contexts=_required_contexts(token, repo, base),
check_runs=_check_runs(token, repo, pr.head_sha),
statuses=_statuses(token, repo, pr.head_sha),
comments=_comments(token, repo, number),
reviews=_reviews(token, repo, number),
head_commit_date=_head_commit_date(token, repo, number),
self_check_name=self_check_name,
author_allowlist=allowlist,
)

View file

@ -23,7 +23,6 @@ sys.modules[_spec.name] = merger
_spec.loader.exec_module(merger)
HEAD_SHA: Final = "deadbeef" * 5
HEAD_DATE: Final = datetime(2026, 1, 10, tzinfo=timezone.utc)
ALLOWLIST: Final = frozenset({"berriai-litellm-provider-info-sync[bot]"})
COST_MAP_FILES: Final = ("model_prices_and_context_window.json",)
@ -42,24 +41,6 @@ def _pr(**overrides: object) -> merger.PullRequest:
return merger.PullRequest(**{**base, **overrides})
def _greptile(score: int, updated_at: datetime) -> merger.IssueComment:
return merger.IssueComment(
author_login="greptile-apps[bot]",
body=f"Confidence Score: {score}/5",
updated_at=updated_at,
)
def _bugbot(commit_id: str, body: str, submitted_at: datetime) -> merger.Review:
return merger.Review(
author_login="cursor[bot]",
state="COMMENTED",
body=body,
commit_id=commit_id,
submitted_at=submitted_at,
)
def _inputs(**overrides: object) -> merger.EvaluationInputs:
base: Final = {
"pr": _pr(),
@ -67,15 +48,7 @@ def _inputs(**overrides: object) -> merger.EvaluationInputs:
"required_contexts": frozenset({"build"}),
"check_runs": (merger.CheckRun(name="build", status="completed", conclusion="success"),),
"statuses": (),
"comments": (_greptile(5, datetime(2026, 1, 11, tzinfo=timezone.utc)),),
"reviews": (
_bugbot(
HEAD_SHA,
"<!-- BUGBOT_REVIEW --> cursor bugbot found no new issues",
datetime(2026, 1, 11, tzinfo=timezone.utc),
),
),
"head_commit_date": HEAD_DATE,
"reviews": (),
"self_check_name": "auto-merge-price-sync",
"author_allowlist": ALLOWLIST,
}
@ -182,82 +155,10 @@ def test_pending_commit_status_holds() -> None:
)
def test_greptile_missing_holds() -> None:
_holds(_inputs(comments=()), "greptile score not available")
def test_greptile_four_of_five_holds() -> None:
_holds(
_inputs(comments=(_greptile(4, datetime(2026, 1, 11, tzinfo=timezone.utc)),)),
"greptile score 4/5",
)
def test_greptile_older_than_head_holds() -> None:
_holds(
_inputs(comments=(_greptile(5, datetime(2026, 1, 9, tzinfo=timezone.utc)),)),
"older than head commit",
)
def test_bugbot_missing_holds() -> None:
_holds(_inputs(reviews=()), "bugbot review not available")
def test_bugbot_stale_marker_ignored() -> None:
_holds(
_inputs(
reviews=(
_bugbot(
HEAD_SHA,
"<!-- BUGBOT_REVIEW --><!-- BUGBOT_REVIEW_STALE --> cursor bugbot found no new issues",
datetime(2026, 1, 11, tzinfo=timezone.utc),
),
)
),
"bugbot review not available",
)
def test_bugbot_old_commit_ignored() -> None:
_holds(
_inputs(
reviews=(
_bugbot(
"0" * 40,
"<!-- BUGBOT_REVIEW --> cursor bugbot found no new issues",
datetime(2026, 1, 11, tzinfo=timezone.utc),
),
)
),
"bugbot review not available",
)
def test_bugbot_issues_found_holds() -> None:
_holds(
_inputs(
reviews=(
_bugbot(
HEAD_SHA,
"<!-- BUGBOT_REVIEW --> cursor bugbot found 2 new issues",
datetime(2026, 1, 11, tzinfo=timezone.utc),
),
)
),
"bugbot reported issues",
)
def test_changes_requested_holds() -> None:
_holds(
_inputs(
reviews=(
_bugbot(
HEAD_SHA,
"<!-- BUGBOT_REVIEW --> cursor bugbot found no new issues",
datetime(2026, 1, 11, tzinfo=timezone.utc),
),
merger.Review(
author_login="human-reviewer",
state="CHANGES_REQUESTED",
@ -275,11 +176,6 @@ def test_superseded_changes_requested_merges() -> None:
verdict: Final = _evaluate(
_inputs(
reviews=(
_bugbot(
HEAD_SHA,
"<!-- BUGBOT_REVIEW --> cursor bugbot found no new issues",
datetime(2026, 1, 12, tzinfo=timezone.utc),
),
merger.Review(
author_login="human-reviewer",
state="CHANGES_REQUESTED",