Fix gh CLI compatibility for older versions missing baseRefOid field
**Changes:**
1. Added `GhApiPr` and `GhApiRef` structs (lines 31-46) to deserialize the GitHub REST API response
2. Added `get_pr_info_via_api()` function (lines 103-141) as a fallback that uses `gh api repos/{owner}/{repo}/pulls/{number}`
3. Modified `get_pr_info()` (lines 166-170) to detect "unknown json field" errors and fall back to the API method
**How it works:**
- Modern `gh` CLI versions continue to use the faster `gh pr view --json` approach
- When an older `gh` CLI returns "Unknown JSON field: baseRefOid", the code catches this error and falls back to `gh api` which uses the stable REST API
- The REST API fields (`base.sha`, `head.sha`, `head.ref`) have been stable for years and work with all `gh` CLI versions
This commit is contained in:
committed by
GitHub
parent
8fa5b9d098
commit
f30606b48a
@@ -28,6 +28,23 @@ struct GhPrView {
|
|||||||
head_ref_name: String,
|
head_ref_name: String,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Response from `gh api /repos/{owner}/{repo}/pulls/{number}`
|
||||||
|
/// Used as fallback for older gh CLI versions that don't support baseRefOid/headRefOid fields
|
||||||
|
#[derive(Debug, Deserialize)]
|
||||||
|
struct GhApiPr {
|
||||||
|
title: String,
|
||||||
|
body: Option<String>,
|
||||||
|
base: GhApiRef,
|
||||||
|
head: GhApiRef,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Deserialize)]
|
||||||
|
struct GhApiRef {
|
||||||
|
sha: String,
|
||||||
|
#[serde(rename = "ref")]
|
||||||
|
ref_name: String,
|
||||||
|
}
|
||||||
|
|
||||||
/// Parse a GitHub PR URL to extract owner, repo, and PR number
|
/// Parse a GitHub PR URL to extract owner, repo, and PR number
|
||||||
///
|
///
|
||||||
/// Expected format: https://github.com/owner/repo/pull/123
|
/// Expected format: https://github.com/owner/repo/pull/123
|
||||||
@@ -83,6 +100,46 @@ fn ensure_gh_available() -> Result<(), ReviewError> {
|
|||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Get PR information using `gh api` (REST API)
|
||||||
|
/// This is used as a fallback for older gh CLI versions that don't support
|
||||||
|
/// the baseRefOid/headRefOid fields in `gh pr view --json`
|
||||||
|
fn get_pr_info_via_api(owner: &str, repo: &str, pr_number: i64) -> Result<PrInfo, ReviewError> {
|
||||||
|
debug!("Fetching PR info via gh api for {owner}/{repo}#{pr_number}");
|
||||||
|
|
||||||
|
let output = Command::new("gh")
|
||||||
|
.args(["api", &format!("repos/{owner}/{repo}/pulls/{pr_number}")])
|
||||||
|
.output()
|
||||||
|
.map_err(|e| ReviewError::PrInfoFailed(e.to_string()))?;
|
||||||
|
|
||||||
|
if !output.status.success() {
|
||||||
|
let stderr = String::from_utf8_lossy(&output.stderr);
|
||||||
|
let lower = stderr.to_ascii_lowercase();
|
||||||
|
|
||||||
|
if lower.contains("authentication")
|
||||||
|
|| lower.contains("gh auth login")
|
||||||
|
|| lower.contains("unauthorized")
|
||||||
|
{
|
||||||
|
return Err(ReviewError::GhNotAuthenticated);
|
||||||
|
}
|
||||||
|
|
||||||
|
return Err(ReviewError::PrInfoFailed(stderr.to_string()));
|
||||||
|
}
|
||||||
|
|
||||||
|
let stdout = String::from_utf8_lossy(&output.stdout);
|
||||||
|
let api_pr: GhApiPr =
|
||||||
|
serde_json::from_str(&stdout).map_err(|e| ReviewError::PrInfoFailed(e.to_string()))?;
|
||||||
|
|
||||||
|
Ok(PrInfo {
|
||||||
|
owner: owner.to_string(),
|
||||||
|
repo: repo.to_string(),
|
||||||
|
title: api_pr.title,
|
||||||
|
description: api_pr.body.unwrap_or_default(),
|
||||||
|
base_commit: api_pr.base.sha,
|
||||||
|
head_commit: api_pr.head.sha,
|
||||||
|
head_ref_name: api_pr.head.ref_name,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
/// Get PR information using `gh pr view`
|
/// Get PR information using `gh pr view`
|
||||||
pub fn get_pr_info(owner: &str, repo: &str, pr_number: i64) -> Result<PrInfo, ReviewError> {
|
pub fn get_pr_info(owner: &str, repo: &str, pr_number: i64) -> Result<PrInfo, ReviewError> {
|
||||||
ensure_gh_available()?;
|
ensure_gh_available()?;
|
||||||
@@ -106,6 +163,12 @@ pub fn get_pr_info(owner: &str, repo: &str, pr_number: i64) -> Result<PrInfo, Re
|
|||||||
let stderr = String::from_utf8_lossy(&output.stderr);
|
let stderr = String::from_utf8_lossy(&output.stderr);
|
||||||
let lower = stderr.to_ascii_lowercase();
|
let lower = stderr.to_ascii_lowercase();
|
||||||
|
|
||||||
|
// Check for old gh CLI version that doesn't support these JSON fields
|
||||||
|
if lower.contains("unknown json field") {
|
||||||
|
debug!("gh pr view --json failed with unknown field, falling back to gh api");
|
||||||
|
return get_pr_info_via_api(owner, repo, pr_number);
|
||||||
|
}
|
||||||
|
|
||||||
if lower.contains("authentication")
|
if lower.contains("authentication")
|
||||||
|| lower.contains("gh auth login")
|
|| lower.contains("gh auth login")
|
||||||
|| lower.contains("unauthorized")
|
|| lower.contains("unauthorized")
|
||||||
|
|||||||
Reference in New Issue
Block a user