feat: update authored pull branches from mobile (Closes #1360)
CI / lint (pull_request) Successful in 3m28s
CI / build-release (pull_request) Successful in 7s
CI / browser-journey (pull_request) Successful in 5m31s
CI / release-candidate (pull_request) Has been skipped

This commit is contained in:
timmy committed 2026-08-24 18:02:23 +00:00
1 parent a9becd079f
commit b85fe4a0a3
7 files changed
+272

No files matched your search

+56
View File
@@ -173,6 +173,10 @@ class IssueEditConflictError(ValueError):
"""Raised when an issue changed after the editor loaded it."""
class PullUpdateConflictError(ValueError):
"""Raised when Gitea cannot merge a pull request's base into its head."""
class IssueDependencyInvalidError(ValueError):
"""Raised when a requested blocker relationship is not valid."""
@@ -2089,6 +2093,58 @@ async def reopen_authored_pull(
)
async def update_authored_pull_branch(
repository: str, number: int, expected_head_sha: str
) -> dict:
login, pull = await _current_login_and_target(
f"repos/{repository}/pulls/{number}"
)
author = pull.get("user") if isinstance(pull.get("user"), dict) else {}
head = pull.get("head") if isinstance(pull.get("head"), dict) else {}
if (
pull.get("state") != "open"
or pull.get("merged") is True
or pull.get("draft") is True
or author.get("login", "").casefold() != login.casefold()
or head.get("sha") != expected_head_sha
):
raise IssueNotAvailableError("Pull request state changed")
response = await _get_client().post(
f"/api/v1/repos/{repository}/pulls/{number}/update",
headers=_auth(),
params={"style": "merge"},
)
if response.status_code in {409, 422}:
raise PullUpdateConflictError("Pull request has merge conflicts")
response.raise_for_status()
confirmed_response = await _get_client().get(
f"/api/v1/repos/{repository}/pulls/{number}", headers=_auth()
)
confirmed_response.raise_for_status()
confirmed = confirmed_response.json()
confirmed_head = confirmed.get("head") if isinstance(confirmed, dict) and isinstance(confirmed.get("head"), dict) else {}
new_head_sha = confirmed_head.get("sha")
if (
not isinstance(confirmed, dict)
or confirmed.get("number") != number
or confirmed.get("state") != "open"
or confirmed.get("merged") is True
or not isinstance(new_head_sha, str)
or not new_head_sha
or new_head_sha == expected_head_sha
):
raise ValueError("Gitea did not confirm a new pull request head")
return {
"repository": repository,
"number": number,
"title": confirmed.get("title", ""),
"previous_head_sha": expected_head_sha,
"head_sha": new_head_sha,
"state": "open",
"draft": confirmed.get("draft") is True,
}
async def request_assigned_pull_review(
repository: str, number: int, reviewer: str, expected_head_sha: str
) -> dict:
+34
View File
@@ -6721,6 +6721,40 @@ async def reopen_authored_pull(
)
@app.post("/api/v1/repos/{owner}/{repo}/pulls/{number}/update-branch")
async def update_authored_pull_branch(
update: PullReadyRequest,
owner: str,
repo: str,
number: int = PathParam(gt=0),
):
repository = f"{owner}/{repo}"
try:
result = await asyncio.wait_for(
gitea_proxy.update_authored_pull_branch(
repository, number, update.expected_head_sha
),
timeout=ISSUE_ACTION_TIMEOUT_SECONDS,
)
except gitea_proxy.IssueNotAvailableError:
return JSONResponse(
{"error": "The pull request changed. Reload the latest state before updating its branch."},
status_code=409,
)
except gitea_proxy.PullUpdateConflictError:
return JSONResponse(
{"error": "Automatic branch update is unavailable because the branches have conflicts."},
status_code=422,
)
except Exception:
return JSONResponse(
{"error": "The branch could not be updated. Please retry."},
status_code=503,
headers={"Retry-After": "1"},
)
return JSONResponse(result)
@app.get("/api/v1/repos/{owner}/{repo}/pulls/{number}/review-data")
async def assigned_pull_review_data(
owner: str, repo: str, number: int = PathParam(gt=0)