feat: publish draft pull requests from mobile (Closes #1344)
CI / lint (pull_request) Successful in 3m22s
CI / build-release (pull_request) Successful in 6s
CI / browser-journey (pull_request) Successful in 5m33s
CI / release-candidate (pull_request) Has been skipped

This commit is contained in:
timmy committed 2026-08-24 10:06:51 +00:00
1 parent 1a2297e943
commit a4ae3e1c7f
6 files changed
+314 -2

No files matched your search

+63
View File
@@ -2028,6 +2028,69 @@ async def request_assigned_pull_review(
}
async def publish_authored_assigned_pull(
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 {}
title = pull.get("title") if isinstance(pull.get("title"), str) else ""
ready_title = re.sub(
r"^(?:WIP\s*:|\[WIP\]|Draft\s*:|\[Draft\])\s*",
"",
title,
count=1,
flags=re.IGNORECASE,
).strip()
if (
pull.get("state") != "open"
or pull.get("merged") is True
or pull.get("draft") is not True
or author.get("login", "").casefold() != login.casefold()
or not _login_in_users(login, pull.get("assignees"))
or head.get("sha") != expected_head_sha
or not ready_title
or ready_title == title
):
raise IssueNotAvailableError("Draft pull request is no longer publishable")
response = await _get_client().patch(
f"/api/v1/repos/{repository}/pulls/{number}",
headers=_auth(),
json={"title": ready_title},
)
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 {}
)
if (
not isinstance(confirmed, dict)
or confirmed.get("number") != number
or confirmed.get("state") != "open"
or confirmed.get("draft") is not False
or confirmed.get("title") != ready_title
or confirmed_head.get("sha") != expected_head_sha
):
raise ValueError("Gitea did not confirm the pull request is ready")
return {
"repository": repository,
"number": number,
"title": ready_title,
"head_sha": expected_head_sha,
"state": "open",
"draft": False,
}
async def cancel_assigned_pull_review(
repository: str, number: int, reviewer: str, expected_head_sha: str
) -> dict:
+35
View File
@@ -1117,6 +1117,12 @@ class PullReviewRequest(BaseModel):
)
class PullReadyRequest(BaseModel):
expected_head_sha: str = Field(
min_length=7, max_length=64, pattern=r"^[A-Fa-f0-9]+$"
)
class PullContentUpdate(BaseModel):
title: str = Field(min_length=1, max_length=255)
body: str = Field(default="", max_length=10_000)
@@ -6609,6 +6615,35 @@ async def update_authored_assigned_pull_content(
return JSONResponse(result)
@app.patch("/api/v1/repos/{owner}/{repo}/pulls/{number}/ready")
async def publish_authored_assigned_pull(
update: PullReadyRequest,
owner: str,
repo: str,
number: int = PathParam(gt=0),
):
repository = f"{owner}/{repo}"
try:
result = await asyncio.wait_for(
gitea_proxy.publish_authored_assigned_pull(
repository, number, update.expected_head_sha
),
timeout=ISSUE_ACTION_TIMEOUT_SECONDS,
)
except gitea_proxy.IssueNotAvailableError:
return JSONResponse(
{"error": "The draft pull request changed. Reload before publishing."},
status_code=409,
)
except Exception:
return JSONResponse(
{"error": "The draft pull request could not be published. 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)