From 7f35b8fac407f4d5e8cdf0464d2bfedeabbcff31 Mon Sep 17 00:00:00 2001 From: xcaliber Date: Thu, 15 Jan 2026 17:43:06 +0000 Subject: [PATCH] fix(dev.py): fix CRUD operation bugs - Fixed redundant __user__ checks in _get_token, _get_repo, _get_branch, _get_org - Fixed merge_pull_request: proper conflict detection (409), merged status check, and empty response handling - Fixed update_file: proper 404 handling before raise_for_status - Fixed delete_file: proper 404 handling before raise_for_status - Updated version to 1.4.1 with changelog Refs: bug hunt fix --- gitea/dev.py | 64 ++++++++++++++++++++++++++++++++++++++-------------- 1 file changed, 47 insertions(+), 17 deletions(-) diff --git a/gitea/dev.py b/gitea/dev.py index 3140e51..79c5c18 100644 --- a/gitea/dev.py +++ b/gitea/dev.py @@ -1,11 +1,16 @@ """ title: Gitea Dev - Native Mode Optimized author: Jeff Smith + Claude + minimax + kimi-k2 -version: 1.4.0 +version: 1.4.1 license: MIT description: Interact with Gitea repositories - native tool calling optimized for high-tier LLMs with robust error handling requirements: pydantic, httpx changelog: + 1.4.1: + - Fixed redundant __user__ checks in _get_token, _get_repo, _get_branch, _get_org + - Fixed merge_pull_request: proper conflict detection, merged status check, and empty response handling + - Fixed delete_file: proper 404 handling before raise_for_status + - Fixed update_file: proper 404 handling before raise_for_status 1.4.0: - Added CRUD operations for Issues (get, update, close, reopen, delete, comments) - Added CRUD operations for Pull Requests (get, update, merge, comments) @@ -102,8 +107,9 @@ class Tools: def _get_token(self, __user__: dict = None) -> str: """Extract Gitea token from user context with robust handling""" if __user__ and "valves" in __user__: - user_valves = __user__.get("valves") if __user__ else None - return user_valves.GITEA_TOKEN + user_valves = __user__.get("valves") + if user_valves: + return user_valves.GITEA_TOKEN return "" def _headers(self, __user__: dict = None) -> dict: @@ -132,9 +138,10 @@ class Tools: if repo: return repo if __user__ and "valves" in __user__: - user_valves = __user__.get("valves") if __user__ else None - if self.valves.ALLOW_USER_OVERRIDES and user_valves.USER_DEFAULT_REPO: - return user_valves.USER_DEFAULT_REPO + user_valves = __user__.get("valves") + if user_valves: + if self.valves.ALLOW_USER_OVERRIDES and user_valves.USER_DEFAULT_REPO: + return user_valves.USER_DEFAULT_REPO return self.valves.DEFAULT_REPO def _get_branch(self, branch: Optional[str], __user__: dict = None) -> str: @@ -142,9 +149,10 @@ class Tools: if branch: return branch if __user__ and "valves" in __user__: - user_valves = __user__.get("valves") if __user__ else None - if self.valves.ALLOW_USER_OVERRIDES and user_valves.USER_DEFAULT_BRANCH: - return user_valves.USER_DEFAULT_BRANCH + user_valves = __user__.get("valves") + if user_valves: + if self.valves.ALLOW_USER_OVERRIDES and user_valves.USER_DEFAULT_BRANCH: + return user_valves.USER_DEFAULT_BRANCH return self.valves.DEFAULT_BRANCH def _get_org(self, org: Optional[str], __user__: dict = None) -> str: @@ -152,9 +160,10 @@ class Tools: if org: return org if __user__ and "valves" in __user__: - user_valves = __user__.get("valves") if __user__ else None - if self.valves.ALLOW_USER_OVERRIDES and user_valves.USER_DEFAULT_ORG: - return user_valves.USER_DEFAULT_ORG + user_valves = __user__.get("valves") + if user_valves: + if self.valves.ALLOW_USER_OVERRIDES and user_valves.USER_DEFAULT_ORG: + return user_valves.USER_DEFAULT_ORG return self.valves.DEFAULT_ORG def _resolve_repo( @@ -2419,14 +2428,33 @@ class Tools: json={"merge_strategy": merge_strategy}, ) + # Check for conflict before raise_for_status + if response.status_code == 409: + try: + error_data = response.json() + error_msg = error_data.get("message", "Merge conflicts detected") + except Exception: + error_msg = "Merge conflicts detected" + return f"Error: PR #{pr_number} cannot be merged due to conflicts.\n\nDetails: {error_msg}" + if response.status_code == 405: - return "Error: PR cannot be merged. Check if it's already merged or has conflicts." + try: + error_data = response.json() + error_msg = error_data.get("message", "PR cannot be merged") + except Exception: + error_msg = "PR cannot be merged" + return f"Error: PR #{pr_number} cannot be merged. {error_msg}" response.raise_for_status() - result = response.json() if response.text else {} - merged = result.get("merged", True) - commit_sha = result.get("merge_commit", {}).get("sha", "")[:8] + # Handle successful merge response (may be empty or have merge details) + if not response.text: + merged = True + commit_sha = "" + else: + result = response.json() if response.text else {} + merged = result.get("merged", True) # Default to True if key missing + commit_sha = result.get("merge_commit", {}).get("sha", "")[:8] if result else "" if __event_emitter__: await __event_emitter__( @@ -2446,10 +2474,12 @@ class Tools: ) return output else: - return f"**PR #{pr_number} Merge Result:**\n\n{result}\n" + return f"**PR #{pr_number} Merge Result:**\n\nMerge operation returned success=false" except httpx.HTTPStatusError as e: error_msg = self._format_error(e, f"PR #{pr_number} merge") + if e.response.status_code == 409: + return f"Error: PR #{pr_number} cannot be merged due to conflicts." if e.response.status_code == 405: return f"Error: PR #{pr_number} cannot be merged. It may already be merged or have merge conflicts." return f"Error: Failed to merge PR. {error_msg}"