From c62e9fdcd4602092b5e3b699b2fe00926110465a Mon Sep 17 00:00:00 2001 From: Sharang Parnerkar Date: Wed, 18 Mar 2026 23:41:03 +0100 Subject: [PATCH] fix: check Gitea API response status and fallback for PR reviews MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The Gitea tracker was silently swallowing HTTP error responses — only network failures were caught, so 4xx/5xx from Gitea went unnoticed and the agent logged success even when reviews weren't posted. - Add response status checking to create_pr_review, update_issue_status, and add_comment - Add fallback in create_pr_review: if inline comments fail, retry as a plain PR comment so the summary still gets posted Co-Authored-By: Claude Opus 4.6 (1M context) --- compliance-agent/src/trackers/gitea.rs | 66 ++++++++++++++++++++++++-- 1 file changed, 63 insertions(+), 3 deletions(-) diff --git a/compliance-agent/src/trackers/gitea.rs b/compliance-agent/src/trackers/gitea.rs index 8d3debb..2bdabe7 100644 --- a/compliance-agent/src/trackers/gitea.rs +++ b/compliance-agent/src/trackers/gitea.rs @@ -98,7 +98,8 @@ impl IssueTracker for GiteaTracker { _ => "open", }; - self.http + let resp = self + .http .patch(&url) .header( "Authorization", @@ -109,6 +110,14 @@ impl IssueTracker for GiteaTracker { .await .map_err(|e| CoreError::IssueTracker(format!("Gitea update issue failed: {e}")))?; + if !resp.status().is_success() { + let status = resp.status(); + let text = resp.text().await.unwrap_or_default(); + return Err(CoreError::IssueTracker(format!( + "Gitea update issue returned {status}: {text}" + ))); + } + Ok(()) } @@ -123,7 +132,8 @@ impl IssueTracker for GiteaTracker { "/repos/{owner}/{repo}/issues/{external_id}/comments" )); - self.http + let resp = self + .http .post(&url) .header( "Authorization", @@ -134,6 +144,14 @@ impl IssueTracker for GiteaTracker { .await .map_err(|e| CoreError::IssueTracker(format!("Gitea add comment failed: {e}")))?; + if !resp.status().is_success() { + let status = resp.status(); + let text = resp.text().await.unwrap_or_default(); + return Err(CoreError::IssueTracker(format!( + "Gitea add comment returned {status}: {text}" + ))); + } + Ok(()) } @@ -158,7 +176,8 @@ impl IssueTracker for GiteaTracker { }) .collect(); - self.http + let resp = self + .http .post(&url) .header( "Authorization", @@ -173,6 +192,47 @@ impl IssueTracker for GiteaTracker { .await .map_err(|e| CoreError::IssueTracker(format!("Gitea PR review failed: {e}")))?; + if !resp.status().is_success() { + let status = resp.status(); + let text = resp.text().await.unwrap_or_default(); + + // If inline comments caused the failure, retry with just the summary body + if !comments.is_empty() { + tracing::warn!( + "Gitea PR review with inline comments failed ({status}): {text}, retrying as plain comment" + ); + let fallback_url = + self.api_url(&format!("/repos/{owner}/{repo}/issues/{pr_number}/comments")); + let fallback_resp = self + .http + .post(&fallback_url) + .header( + "Authorization", + format!("token {}", self.token.expose_secret()), + ) + .json(&serde_json::json!({ "body": body })) + .send() + .await + .map_err(|e| { + CoreError::IssueTracker(format!("Gitea PR comment fallback failed: {e}")) + })?; + + if !fallback_resp.status().is_success() { + let fb_status = fallback_resp.status(); + let fb_text = fallback_resp.text().await.unwrap_or_default(); + return Err(CoreError::IssueTracker(format!( + "Gitea PR comment fallback returned {fb_status}: {fb_text}" + ))); + } + + return Ok(()); + } + + return Err(CoreError::IssueTracker(format!( + "Gitea PR review returned {status}: {text}" + ))); + } + Ok(()) }