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
This commit is contained in:
2026-01-15 17:43:06 +00:00
parent dbfeca8271
commit 7f35b8fac4

View File

@@ -1,11 +1,16 @@
""" """
title: Gitea Dev - Native Mode Optimized title: Gitea Dev - Native Mode Optimized
author: Jeff Smith + Claude + minimax + kimi-k2 author: Jeff Smith + Claude + minimax + kimi-k2
version: 1.4.0 version: 1.4.1
license: MIT license: MIT
description: Interact with Gitea repositories - native tool calling optimized for high-tier LLMs with robust error handling description: Interact with Gitea repositories - native tool calling optimized for high-tier LLMs with robust error handling
requirements: pydantic, httpx requirements: pydantic, httpx
changelog: 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: 1.4.0:
- Added CRUD operations for Issues (get, update, close, reopen, delete, comments) - Added CRUD operations for Issues (get, update, close, reopen, delete, comments)
- Added CRUD operations for Pull Requests (get, update, merge, 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: def _get_token(self, __user__: dict = None) -> str:
"""Extract Gitea token from user context with robust handling""" """Extract Gitea token from user context with robust handling"""
if __user__ and "valves" in __user__: if __user__ and "valves" in __user__:
user_valves = __user__.get("valves") if __user__ else None user_valves = __user__.get("valves")
return user_valves.GITEA_TOKEN if user_valves:
return user_valves.GITEA_TOKEN
return "" return ""
def _headers(self, __user__: dict = None) -> dict: def _headers(self, __user__: dict = None) -> dict:
@@ -132,9 +138,10 @@ class Tools:
if repo: if repo:
return repo return repo
if __user__ and "valves" in __user__: if __user__ and "valves" in __user__:
user_valves = __user__.get("valves") if __user__ else None user_valves = __user__.get("valves")
if self.valves.ALLOW_USER_OVERRIDES and user_valves.USER_DEFAULT_REPO: if user_valves:
return user_valves.USER_DEFAULT_REPO if self.valves.ALLOW_USER_OVERRIDES and user_valves.USER_DEFAULT_REPO:
return user_valves.USER_DEFAULT_REPO
return self.valves.DEFAULT_REPO return self.valves.DEFAULT_REPO
def _get_branch(self, branch: Optional[str], __user__: dict = None) -> str: def _get_branch(self, branch: Optional[str], __user__: dict = None) -> str:
@@ -142,9 +149,10 @@ class Tools:
if branch: if branch:
return branch return branch
if __user__ and "valves" in __user__: if __user__ and "valves" in __user__:
user_valves = __user__.get("valves") if __user__ else None user_valves = __user__.get("valves")
if self.valves.ALLOW_USER_OVERRIDES and user_valves.USER_DEFAULT_BRANCH: if user_valves:
return user_valves.USER_DEFAULT_BRANCH if self.valves.ALLOW_USER_OVERRIDES and user_valves.USER_DEFAULT_BRANCH:
return user_valves.USER_DEFAULT_BRANCH
return self.valves.DEFAULT_BRANCH return self.valves.DEFAULT_BRANCH
def _get_org(self, org: Optional[str], __user__: dict = None) -> str: def _get_org(self, org: Optional[str], __user__: dict = None) -> str:
@@ -152,9 +160,10 @@ class Tools:
if org: if org:
return org return org
if __user__ and "valves" in __user__: if __user__ and "valves" in __user__:
user_valves = __user__.get("valves") if __user__ else None user_valves = __user__.get("valves")
if self.valves.ALLOW_USER_OVERRIDES and user_valves.USER_DEFAULT_ORG: if user_valves:
return user_valves.USER_DEFAULT_ORG if self.valves.ALLOW_USER_OVERRIDES and user_valves.USER_DEFAULT_ORG:
return user_valves.USER_DEFAULT_ORG
return self.valves.DEFAULT_ORG return self.valves.DEFAULT_ORG
def _resolve_repo( def _resolve_repo(
@@ -2419,14 +2428,33 @@ class Tools:
json={"merge_strategy": merge_strategy}, 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: 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() response.raise_for_status()
result = response.json() if response.text else {}
merged = result.get("merged", True) # Handle successful merge response (may be empty or have merge details)
commit_sha = result.get("merge_commit", {}).get("sha", "")[:8] 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__: if __event_emitter__:
await __event_emitter__( await __event_emitter__(
@@ -2446,10 +2474,12 @@ class Tools:
) )
return output return output
else: 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: except httpx.HTTPStatusError as e:
error_msg = self._format_error(e, f"PR #{pr_number} merge") 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: 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: PR #{pr_number} cannot be merged. It may already be merged or have merge conflicts."
return f"Error: Failed to merge PR. {error_msg}" return f"Error: Failed to merge PR. {error_msg}"