diff --git a/openapi/SPEC_COMMIT b/openapi/SPEC_COMMIT index 2f095ce..7bfc6c3 100644 --- a/openapi/SPEC_COMMIT +++ b/openapi/SPEC_COMMIT @@ -1 +1 @@ -9129f2310df187638cffb41a7e7694f450ce847d +9cb2eaed5ef2fa38a924c4ba72c11d96f0f7cff5 diff --git a/openapi/openapi.yaml b/openapi/openapi.yaml index d7022c4..37b3ceb 100644 --- a/openapi/openapi.yaml +++ b/openapi/openapi.yaml @@ -1215,7 +1215,9 @@ paths: $ref: "#/components/schemas/PullRequestDependabotActionRequest" responses: "204": - description: Comment posted. + description: | + Comment posted. The request is recorded as `botRequest` on the + pull request in the next snapshot. "400": description: | The request body wasn't valid JSON, `action` wasn't "rebase" @@ -1296,7 +1298,9 @@ paths: $ref: "#/components/schemas/PullRequestActionRequest" responses: "204": - description: Label added. + description: | + Label added. The request is recorded as `botRequest` on the pull + request in the next snapshot. "400": description: | The request body wasn't valid JSON, `fullName` wasn't @@ -3120,6 +3124,16 @@ components: description: | The numbers of the open pull requests stacked directly on this one. Always a list, empty when none. + botRequest: + description: | + A Dependabot or Renovate rebase asked for through this app and not + settled yet, or null. The server keeps it, so a reload during the + wait still shows it, and moves it along on every snapshot that + came from a fetch started after the request. Held in memory per + account: a server restart forgets it. + oneOf: + - type: "null" + - $ref: "#/components/schemas/BotRequest" requestedReviewerLogins: type: array items: @@ -3198,6 +3212,41 @@ components: requested and an unknown review state are all false. Always present. + BotRequest: + type: object + required: [bot, action, phase, requestedAt, expiresAt] + properties: + bot: + type: string + enum: [dependabot, renovate] + action: + type: string + enum: [rebase, recreate] + description: | + `recreate` is Dependabot's only. Renovate's rebase label is + reported as `rebase`. + phase: + type: string + enum: [queued, rebasing, expired] + description: | + `queued`: asked, and nothing seen yet. `rebasing`: the bot + pushed (the head changed, or the pull request was behind and + isn't), and CI hasn't restarted yet. `expired`: the bot didn't + act within 5 minutes. An expired request stays until the pull + request is gone, a new request replaces it, or an hour passes. + A request is dropped once CI shows pending after the push, 2 + minutes into `rebasing`, or when the pull request is gone. + requestedAt: + type: string + format: date-time + expiresAt: + type: string + format: date-time + description: | + When the server stops waiting in the current phase: 5 minutes + after the request while `queued`, 2 minutes after the pickup + while `rebasing`. + StackPosition: type: object required: [position, size] diff --git a/src/forge_dashboard/_generated/models/__init__.py b/src/forge_dashboard/_generated/models/__init__.py index 7571872..ef23bba 100644 --- a/src/forge_dashboard/_generated/models/__init__.py +++ b/src/forge_dashboard/_generated/models/__init__.py @@ -13,6 +13,10 @@ from .api_token import APIToken from .api_token_create_request import APITokenCreateRequest from .api_token_create_response import APITokenCreateResponse +from .bot_request import BotRequest +from .bot_request_action import BotRequestAction +from .bot_request_bot import BotRequestBot +from .bot_request_phase import BotRequestPhase from .check import Check from .check_state import CheckState from .ci_status import CIStatus @@ -74,6 +78,10 @@ "APIToken", "APITokenCreateRequest", "APITokenCreateResponse", + "BotRequest", + "BotRequestAction", + "BotRequestBot", + "BotRequestPhase", "Check", "CheckState", "CIStatus", diff --git a/src/forge_dashboard/_generated/models/bot_request.py b/src/forge_dashboard/_generated/models/bot_request.py new file mode 100644 index 0000000..413698b --- /dev/null +++ b/src/forge_dashboard/_generated/models/bot_request.py @@ -0,0 +1,137 @@ +from __future__ import annotations + +from collections.abc import Mapping +from typing import Any, TypeVar, BinaryIO, TextIO, TYPE_CHECKING, Generator + +from attrs import define as _attrs_define +from attrs import field as _attrs_field + +from ..types import UNSET, Unset + +from ..models.bot_request_action import BotRequestAction +from ..models.bot_request_bot import BotRequestBot +from ..models.bot_request_phase import BotRequestPhase +from typing import cast +import datetime + + + + + + +T = TypeVar("T", bound="BotRequest") + + + +@_attrs_define +class BotRequest: + """ + Attributes: + bot (BotRequestBot): + action (BotRequestAction): `recreate` is Dependabot's only. Renovate's rebase label is + reported as `rebase`. + phase (BotRequestPhase): `queued`: asked, and nothing seen yet. `rebasing`: the bot + pushed (the head changed, or the pull request was behind and + isn't), and CI hasn't restarted yet. `expired`: the bot didn't + act within 5 minutes. An expired request stays until the pull + request is gone, a new request replaces it, or an hour passes. + A request is dropped once CI shows pending after the push, 2 + minutes into `rebasing`, or when the pull request is gone. + requested_at (datetime.datetime): + expires_at (datetime.datetime): When the server stops waiting in the current phase: 5 minutes + after the request while `queued`, 2 minutes after the pickup + while `rebasing`. + """ + + bot: BotRequestBot + action: BotRequestAction + phase: BotRequestPhase + requested_at: datetime.datetime + expires_at: datetime.datetime + additional_properties: dict[str, Any] = _attrs_field(init=False, factory=dict) + + + + + + def to_dict(self) -> dict[str, Any]: + bot = self.bot.value + + action = self.action.value + + phase = self.phase.value + + requested_at = self.requested_at.isoformat() + + expires_at = self.expires_at.isoformat() + + + field_dict: dict[str, Any] = {} + field_dict.update(self.additional_properties) + field_dict.update({ + "bot": bot, + "action": action, + "phase": phase, + "requestedAt": requested_at, + "expiresAt": expires_at, + }) + + return field_dict + + + + @classmethod + def from_dict(cls: type[T], src_dict: Mapping[str, Any]) -> T: + d = dict(src_dict) + bot = BotRequestBot(d.pop("bot")) + + + + + action = BotRequestAction(d.pop("action")) + + + + + phase = BotRequestPhase(d.pop("phase")) + + + + + requested_at = datetime.datetime.fromisoformat(d.pop("requestedAt")) + + + + + expires_at = datetime.datetime.fromisoformat(d.pop("expiresAt")) + + + + + bot_request = cls( + bot=bot, + action=action, + phase=phase, + requested_at=requested_at, + expires_at=expires_at, + ) + + + bot_request.additional_properties = d + return bot_request + + @property + def additional_keys(self) -> list[str]: + return list(self.additional_properties.keys()) + + def __getitem__(self, key: str) -> Any: + return self.additional_properties[key] + + def __setitem__(self, key: str, value: Any) -> None: + self.additional_properties[key] = value + + def __delitem__(self, key: str) -> None: + del self.additional_properties[key] + + def __contains__(self, key: str) -> bool: + return key in self.additional_properties diff --git a/src/forge_dashboard/_generated/models/bot_request_action.py b/src/forge_dashboard/_generated/models/bot_request_action.py new file mode 100644 index 0000000..f0507ce --- /dev/null +++ b/src/forge_dashboard/_generated/models/bot_request_action.py @@ -0,0 +1,8 @@ +from enum import StrEnum + +class BotRequestAction(StrEnum): + REBASE = "rebase" + RECREATE = "recreate" + + def __str__(self) -> str: + return str(self.value) diff --git a/src/forge_dashboard/_generated/models/bot_request_bot.py b/src/forge_dashboard/_generated/models/bot_request_bot.py new file mode 100644 index 0000000..e93e879 --- /dev/null +++ b/src/forge_dashboard/_generated/models/bot_request_bot.py @@ -0,0 +1,8 @@ +from enum import StrEnum + +class BotRequestBot(StrEnum): + DEPENDABOT = "dependabot" + RENOVATE = "renovate" + + def __str__(self) -> str: + return str(self.value) diff --git a/src/forge_dashboard/_generated/models/bot_request_phase.py b/src/forge_dashboard/_generated/models/bot_request_phase.py new file mode 100644 index 0000000..bc4e846 --- /dev/null +++ b/src/forge_dashboard/_generated/models/bot_request_phase.py @@ -0,0 +1,9 @@ +from enum import StrEnum + +class BotRequestPhase(StrEnum): + EXPIRED = "expired" + QUEUED = "queued" + REBASING = "rebasing" + + def __str__(self) -> str: + return str(self.value) diff --git a/src/forge_dashboard/_generated/models/pull_request.py b/src/forge_dashboard/_generated/models/pull_request.py index fbc1eeb..c824900 100644 --- a/src/forge_dashboard/_generated/models/pull_request.py +++ b/src/forge_dashboard/_generated/models/pull_request.py @@ -17,6 +17,7 @@ if TYPE_CHECKING: from ..models.allowed_action import AllowedAction + from ..models.bot_request import BotRequest from ..models.label import Label from ..models.review_state import ReviewState from ..models.stack_position import StackPosition @@ -116,6 +117,11 @@ class PullRequest: isn't a draft. Unreviewed with nobody asked, approved, changes requested and an unknown review state are all false. Always present. + bot_request (BotRequest | None | Unset): A Dependabot or Renovate rebase asked for through this app and not + settled yet, or null. The server keeps it, so a reload during the + wait still shows it, and moves it along on every snapshot that + came from a fetch started after the request. Held in memory per + account: a server restart forgets it. auto_merge_enabled (bool | Unset): Whether auto-merge is currently scheduled on this pull request. Omitted when the owning forge has no way to report this at all (Forgejo, today) — never false in that case, since this service @@ -170,6 +176,7 @@ class PullRequest: allowed_actions: list[AllowedAction] ready_to_merge: bool needs_review: bool + bot_request: BotRequest | None | Unset = UNSET auto_merge_enabled: bool | Unset = UNSET auto_merge_allowed: bool | Unset = UNSET review: ReviewState | Unset = UNSET @@ -181,6 +188,7 @@ class PullRequest: def to_dict(self) -> dict[str, Any]: from ..models.allowed_action import AllowedAction # noqa: PLC0415 + from ..models.bot_request import BotRequest # noqa: PLC0415 from ..models.label import Label # noqa: PLC0415 from ..models.review_state import ReviewState # noqa: PLC0415 from ..models.stack_position import StackPosition # noqa: PLC0415 @@ -259,6 +267,14 @@ def to_dict(self) -> dict[str, Any]: needs_review = self.needs_review + bot_request: dict[str, Any] | None | Unset + if isinstance(self.bot_request, Unset): + bot_request = UNSET + elif isinstance(self.bot_request, BotRequest): + bot_request = self.bot_request.to_dict() + else: + bot_request = self.bot_request + auto_merge_enabled = self.auto_merge_enabled auto_merge_allowed = self.auto_merge_allowed @@ -298,6 +314,8 @@ def to_dict(self) -> dict[str, Any]: "readyToMerge": ready_to_merge, "needsReview": needs_review, }) + if bot_request is not UNSET: + field_dict["botRequest"] = bot_request if auto_merge_enabled is not UNSET: field_dict["autoMergeEnabled"] = auto_merge_enabled if auto_merge_allowed is not UNSET: @@ -312,6 +330,7 @@ def to_dict(self) -> dict[str, Any]: @classmethod def from_dict(cls: type[T], src_dict: Mapping[str, Any]) -> T: from ..models.allowed_action import AllowedAction # noqa: PLC0415 + from ..models.bot_request import BotRequest # noqa: PLC0415 from ..models.label import Label # noqa: PLC0415 from ..models.review_state import ReviewState # noqa: PLC0415 from ..models.stack_position import StackPosition # noqa: PLC0415 @@ -434,6 +453,26 @@ def _parse_stacked_on(data: object) -> None | StackRef: needs_review = d.pop("needsReview") + def _parse_bot_request(data: object) -> BotRequest | None | Unset: + if data is None: + return data + if isinstance(data, Unset): + return data + try: + if not isinstance(data, dict): + raise TypeError() + bot_request_type_1 = BotRequest.from_dict(data) + + + + return bot_request_type_1 + except (TypeError, ValueError, AttributeError, KeyError): + pass + return cast(BotRequest | None | Unset, data) + + bot_request = _parse_bot_request(d.pop("botRequest", UNSET)) + + auto_merge_enabled = d.pop("autoMergeEnabled", UNSET) auto_merge_allowed = d.pop("autoMergeAllowed", UNSET) @@ -475,6 +514,7 @@ def _parse_stacked_on(data: object) -> None | StackRef: allowed_actions=allowed_actions, ready_to_merge=ready_to_merge, needs_review=needs_review, + bot_request=bot_request, auto_merge_enabled=auto_merge_enabled, auto_merge_allowed=auto_merge_allowed, review=review,