feat: update authored pull branches from mobile (Closes #1360)
This commit is contained in:
1 parent
a9becd079f
commit
b85fe4a0a3
7 files changed
+272
No files matched your search
@@ -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
@@ -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)
|
||||
|
||||
Reference in new issue
Block a user