mirror of
https://github.com/fabro-sh/fabro.git
synced 2026-10-06 02:48:25 +00:00
Truncate PR title and body to fit GitHub API limits
Use the first line of the goal as the PR title (not the full goal), truncated to 120 chars. Truncate the PR body to 65,536 chars. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
parent
2025e8303d
commit
d7970fb7ce
1 changed files with 61 additions and 1 deletions
|
|
@ -2,6 +2,30 @@ use tracing::{debug, info};
|
|||
|
||||
use arc_github::{self as github_app, ssh_url_to_https, GitHubAppCredentials};
|
||||
|
||||
/// Derive a PR title from the workflow goal.
|
||||
///
|
||||
/// Uses the first line, truncated to 120 characters for readability.
|
||||
fn pr_title_from_goal(goal: &str) -> String {
|
||||
let first_line = goal.lines().next().unwrap_or(goal);
|
||||
if first_line.chars().count() > 120 {
|
||||
let truncated: String = first_line.chars().take(119).collect();
|
||||
format!("{truncated}…")
|
||||
} else {
|
||||
first_line.to_string()
|
||||
}
|
||||
}
|
||||
|
||||
/// Truncate a PR body to fit GitHub's 65,536 character limit.
|
||||
fn truncate_pr_body(body: &str) -> String {
|
||||
const MAX_BODY: usize = 65_536;
|
||||
const SUFFIX: &str = "\n\n_(truncated)_";
|
||||
if body.len() <= MAX_BODY {
|
||||
return body.to_string();
|
||||
}
|
||||
let cutoff = MAX_BODY - SUFFIX.len();
|
||||
format!("{}{SUFFIX}", &body[..cutoff])
|
||||
}
|
||||
|
||||
/// Generate a PR body from the diff and goal using an LLM.
|
||||
pub async fn generate_pr_body(diff: &str, goal: &str, model: &str) -> Result<String, String> {
|
||||
let system = "Write a concise PR description summarizing the changes.".to_string();
|
||||
|
|
@ -49,6 +73,9 @@ pub async fn maybe_open_pull_request(
|
|||
let (owner, repo) = github_app::parse_github_owner_repo(&https_url)?;
|
||||
|
||||
let body = generate_pr_body(diff, goal, model).await?;
|
||||
let body = truncate_pr_body(&body);
|
||||
|
||||
let title = pr_title_from_goal(goal);
|
||||
|
||||
let (url, pr_number) = github_app::create_pull_request(
|
||||
creds,
|
||||
|
|
@ -56,7 +83,7 @@ pub async fn maybe_open_pull_request(
|
|||
&repo,
|
||||
base_branch,
|
||||
head_branch,
|
||||
goal,
|
||||
&title,
|
||||
&body,
|
||||
)
|
||||
.await?;
|
||||
|
|
@ -70,6 +97,39 @@ pub async fn maybe_open_pull_request(
|
|||
mod tests {
|
||||
use super::*;
|
||||
|
||||
#[test]
|
||||
fn pr_title_uses_first_line() {
|
||||
let goal = "# Add Draft PR Mode\n\nMore details here...";
|
||||
assert_eq!(pr_title_from_goal(goal), "# Add Draft PR Mode");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn pr_title_truncates_long_line() {
|
||||
let long = "x".repeat(300);
|
||||
let title = pr_title_from_goal(&long);
|
||||
assert_eq!(title.chars().count(), 120);
|
||||
assert!(title.ends_with('…'));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn pr_body_truncates_long_body() {
|
||||
let long = "x".repeat(70_000);
|
||||
let body = truncate_pr_body(&long);
|
||||
assert!(body.len() <= 65_536);
|
||||
assert!(body.ends_with("\n\n_(truncated)_"));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn pr_body_short_body_unchanged() {
|
||||
let short = "Some PR description";
|
||||
assert_eq!(truncate_pr_body(short), short);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn pr_title_short_goal_unchanged() {
|
||||
assert_eq!(pr_title_from_goal("Fix bug"), "Fix bug");
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
async fn empty_diff_returns_none() {
|
||||
let creds = GitHubAppCredentials {
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue