- Fix Python formatting (sync no longer strips blank lines from .py files) - Remove test_community_build.py (tests private sync module) - Fix ruff warnings in test files - Add CODE_OF_CONDUCT.md - Add .github/PULL_REQUEST_TEMPLATE.md Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
227 lines
7.2 KiB
Python
227 lines
7.2 KiB
Python
"""
|
|
Pull Request Pydantic Schemas
|
|
|
|
Validation schemas for Gitea pull request operations.
|
|
"""
|
|
|
|
from datetime import datetime
|
|
|
|
from pydantic import BaseModel, ConfigDict, Field, field_validator
|
|
|
|
from .common import GiteaUser
|
|
from .issue import Label, Milestone
|
|
|
|
|
|
class PRBranchInfo(BaseModel):
|
|
"""Pull request branch information"""
|
|
|
|
model_config = ConfigDict(extra="allow")
|
|
|
|
label: str
|
|
ref: str
|
|
sha: str
|
|
repo_id: int
|
|
repository: dict | None = None
|
|
|
|
|
|
class PullRequest(BaseModel):
|
|
"""Gitea pull request model"""
|
|
|
|
model_config = ConfigDict(extra="allow")
|
|
|
|
id: int
|
|
number: int
|
|
user: GiteaUser
|
|
title: str
|
|
body: str | None = None
|
|
labels: list[Label] = []
|
|
milestone: Milestone | None = None
|
|
assignee: GiteaUser | None = None
|
|
assignees: list[GiteaUser] = []
|
|
requested_reviewers: list[GiteaUser] = []
|
|
state: str # "open" or "closed"
|
|
is_locked: bool = False
|
|
comments: int = 0
|
|
html_url: str
|
|
diff_url: str
|
|
patch_url: str
|
|
mergeable: bool = False
|
|
merged: bool = False
|
|
merged_at: datetime | None = None
|
|
merge_commit_sha: str | None = None
|
|
merged_by: GiteaUser | None = None
|
|
base: PRBranchInfo
|
|
head: PRBranchInfo
|
|
merge_base: str | None = None
|
|
due_date: datetime | None = None
|
|
created_at: datetime
|
|
updated_at: datetime
|
|
closed_at: datetime | None = None
|
|
allow_maintainer_edit: bool = False
|
|
url: str
|
|
|
|
|
|
class PRReview(BaseModel):
|
|
"""Pull request review"""
|
|
|
|
model_config = ConfigDict(extra="allow")
|
|
|
|
id: int
|
|
user: GiteaUser
|
|
body: str | None = None
|
|
commit_id: str
|
|
state: str # "PENDING", "APPROVED", "REQUEST_CHANGES", "COMMENT"
|
|
html_url: str
|
|
pull_request_url: str
|
|
submitted_at: datetime
|
|
|
|
|
|
class PRCommit(BaseModel):
|
|
"""Pull request commit"""
|
|
|
|
model_config = ConfigDict(extra="allow")
|
|
|
|
sha: str
|
|
commit: dict
|
|
url: str
|
|
html_url: str
|
|
comments_url: str
|
|
author: GiteaUser | None = None
|
|
committer: GiteaUser | None = None
|
|
parents: list[dict] = []
|
|
|
|
|
|
class PRFile(BaseModel):
|
|
"""Pull request file change"""
|
|
|
|
model_config = ConfigDict(extra="allow")
|
|
|
|
filename: str
|
|
status: str # "added", "removed", "modified", "renamed"
|
|
additions: int
|
|
deletions: int
|
|
changes: int
|
|
raw_url: str | None = None
|
|
contents_url: str | None = None
|
|
patch: str | None = None
|
|
previous_filename: str | None = None
|
|
|
|
|
|
class CreatePullRequestRequest(BaseModel):
|
|
"""Request to create a pull request"""
|
|
|
|
model_config = ConfigDict(extra="forbid")
|
|
|
|
title: str = Field(..., min_length=1, max_length=255, description="PR title")
|
|
head: str = Field(..., description="Source branch")
|
|
base: str = Field(..., description="Target branch")
|
|
body: str | None = Field(None, description="PR description")
|
|
assignee: str | None = Field(None, description="Username of assignee")
|
|
assignees: list[str] | None = Field(None, description="List of assignee usernames")
|
|
labels: list[int] | None = Field(None, description="List of label IDs")
|
|
milestone: int | None = Field(None, description="Milestone ID")
|
|
due_date: datetime | None = Field(None, description="Due date")
|
|
|
|
|
|
class UpdatePullRequestRequest(BaseModel):
|
|
"""Request to update a pull request"""
|
|
|
|
model_config = ConfigDict(extra="forbid")
|
|
|
|
title: str | None = Field(None, min_length=1, max_length=255, description="PR title")
|
|
body: str | None = Field(None, description="PR description")
|
|
assignee: str | None = Field(None, description="Username of assignee")
|
|
assignees: list[str] | None = Field(None, description="List of assignee usernames")
|
|
labels: list[int] | None = Field(None, description="List of label IDs")
|
|
milestone: int | None = Field(None, description="Milestone ID")
|
|
state: str | None = Field(None, description="State (open/closed)")
|
|
due_date: datetime | None = Field(None, description="Due date")
|
|
unset_due_date: bool | None = Field(None, description="Unset due date")
|
|
allow_maintainer_edit: bool | None = Field(None, description="Allow maintainer edit")
|
|
|
|
@classmethod
|
|
@field_validator("state")
|
|
def validate_state(cls, v):
|
|
if v and v not in ["open", "closed"]:
|
|
raise ValueError("State must be 'open' or 'closed'")
|
|
return v
|
|
|
|
|
|
class MergePullRequestRequest(BaseModel):
|
|
"""Request to merge a pull request"""
|
|
|
|
model_config = ConfigDict(extra="forbid")
|
|
|
|
Do: str = Field(
|
|
..., description="Merge method (merge/rebase/rebase-merge/squash/manually-merged)"
|
|
)
|
|
MergeTitleField: str | None = Field(None, description="Merge commit title")
|
|
MergeMessageField: str | None = Field(None, description="Merge commit message")
|
|
delete_branch_after_merge: bool | None = Field(False, description="Delete branch after merge")
|
|
force_merge: bool | None = Field(False, description="Force merge even if not mergeable")
|
|
head_commit_id: str | None = Field(None, description="Expected HEAD commit SHA")
|
|
merge_when_checks_succeed: bool | None = Field(False, description="Auto-merge when checks pass")
|
|
|
|
@classmethod
|
|
@field_validator("Do")
|
|
def validate_merge_method(cls, v):
|
|
allowed = ["merge", "rebase", "rebase-merge", "squash", "manually-merged"]
|
|
if v not in allowed:
|
|
raise ValueError(f"Merge method must be one of: {', '.join(allowed)}")
|
|
return v
|
|
|
|
|
|
class CreateReviewRequest(BaseModel):
|
|
"""Request to create a review"""
|
|
|
|
model_config = ConfigDict(extra="forbid")
|
|
|
|
body: str | None = Field(None, description="Review comment")
|
|
event: str = Field(..., description="Review event (APPROVED/REQUEST_CHANGES/COMMENT)")
|
|
comments: list[dict] | None = Field(None, description="Inline review comments")
|
|
|
|
@classmethod
|
|
@field_validator("event")
|
|
def validate_event(cls, v):
|
|
allowed = ["APPROVED", "REQUEST_CHANGES", "COMMENT"]
|
|
if v not in allowed:
|
|
raise ValueError(f"Review event must be one of: {', '.join(allowed)}")
|
|
return v
|
|
|
|
|
|
class RequestReviewersRequest(BaseModel):
|
|
"""Request reviewers for a pull request"""
|
|
|
|
model_config = ConfigDict(extra="forbid")
|
|
|
|
reviewers: list[str] = Field(..., min_items=1, description="List of reviewer usernames")
|
|
team_reviewers: list[str] | None = Field(None, description="List of team slugs")
|
|
|
|
|
|
class PRListFilters(BaseModel):
|
|
"""Filters for listing pull requests"""
|
|
|
|
model_config = ConfigDict(extra="forbid")
|
|
|
|
state: str | None = Field("open", description="Filter by state (open/closed/all)")
|
|
sort: str | None = Field(
|
|
"created", description="Sort by (created/updated/comments/recentupdate)"
|
|
)
|
|
labels: str | None = Field(None, description="Comma-separated label IDs")
|
|
milestone: str | None = Field(None, description="Milestone name")
|
|
|
|
@classmethod
|
|
@field_validator("state")
|
|
def validate_state(cls, v):
|
|
if v and v not in ["open", "closed", "all"]:
|
|
raise ValueError("State must be 'open', 'closed', or 'all'")
|
|
return v
|
|
|
|
@classmethod
|
|
@field_validator("sort")
|
|
def validate_sort(cls, v):
|
|
allowed = ["created", "updated", "comments", "recentupdate"]
|
|
if v and v not in allowed:
|
|
raise ValueError(f"Sort must be one of: {', '.join(allowed)}")
|
|
return v
|