feat: publish draft pull requests from mobile (Closes #1344)
This commit is contained in:
1 parent
1a2297e943
commit
a4ae3e1c7f
6 files changed
+314
-2
No files matched your search
@@ -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
@@ -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)
|
||||
|
||||
Reference in new issue
Block a user