forge_notify: show assignees and reviewers unconditionally (closes #256)
This commit is contained in:
parent
9b9277db78
commit
845fafdf1b
1 changed files with 56 additions and 25 deletions
|
|
@ -199,10 +199,13 @@ fn review_state_label(state: &str) -> Option<&str> {
|
|||
/// and should be silently discarded (and marked read by the caller).
|
||||
///
|
||||
/// Formats:
|
||||
/// - Comment: `[comment on PR #N repo] title\nurl: ...\n\nauthor: body`
|
||||
/// - Review: `[PR approved #N repo] title\nurl: ...\n\nreviewer: body`
|
||||
/// - Comment: `[comment on PR #N repo] title\nurl: ...\n\nauthor: body\nassignee: user`
|
||||
/// - Review: `[PR approved #N repo] title\nurl: ...\n\nreviewer: body\nassignee: user`
|
||||
/// - New item: `[new issue #N repo] title\nurl: ...\nassignee: user`
|
||||
/// - State: `[PR merged #N repo] title\nurl: ...`
|
||||
/// - State: `[PR merged #N repo] title\nurl: ...\nassignee: user`
|
||||
///
|
||||
/// Assignees (and, for PRs, requested_reviewers) are appended unconditionally
|
||||
/// on all issue/PR notifications (closes #256).
|
||||
///
|
||||
/// Number is extracted from `html_url` last path segment before any `#`.
|
||||
/// Repo slug (`owner/name`) is always included — agents may watch multiple repos.
|
||||
|
|
@ -241,6 +244,49 @@ async fn format_notification(
|
|||
.as_str()
|
||||
.unwrap_or("");
|
||||
|
||||
// Always fetch subject detail for assignee/reviewer metadata (#256).
|
||||
// Keeps agents informed of current ownership without a follow-up fetch.
|
||||
let subject = if !subject_api_url.is_empty() {
|
||||
fetch_json(client, subject_api_url, token).await
|
||||
} else {
|
||||
None
|
||||
};
|
||||
|
||||
let is_pr = matches!(notif_type, "Pull Request" | "Pull");
|
||||
|
||||
// Build assignee + reviewer suffix appended to all notification kinds.
|
||||
let meta_suffix = {
|
||||
let assignees: Vec<&str> = subject
|
||||
.as_ref()
|
||||
.and_then(|s| s["assignees"].as_array())
|
||||
.map(|arr| arr.iter().filter_map(|a| a["login"].as_str()).collect())
|
||||
.unwrap_or_default();
|
||||
let assignee_line = if assignees.is_empty() {
|
||||
"assignee: unassigned".to_owned()
|
||||
} else {
|
||||
format!("assignee: {}", assignees.join(", "))
|
||||
};
|
||||
// For PRs, include requested_reviewers when present.
|
||||
let reviewer_line = if is_pr {
|
||||
let reviewers: Vec<&str> = subject
|
||||
.as_ref()
|
||||
.and_then(|s| s["requested_reviewers"].as_array())
|
||||
.map(|arr| arr.iter().filter_map(|r| r["login"].as_str()).collect())
|
||||
.unwrap_or_default();
|
||||
if reviewers.is_empty() {
|
||||
None
|
||||
} else {
|
||||
Some(format!("reviewer: {}", reviewers.join(", ")))
|
||||
}
|
||||
} else {
|
||||
None
|
||||
};
|
||||
match reviewer_line {
|
||||
Some(r) => format!("\n{assignee_line}\n{r}"),
|
||||
None => format!("\n{assignee_line}"),
|
||||
}
|
||||
};
|
||||
|
||||
// Determine whether this notification was triggered by a comment/review or
|
||||
// by creation/state-change of the subject itself.
|
||||
let has_comment = !comment_api_url.is_empty() && comment_api_url != subject_api_url;
|
||||
|
|
@ -287,14 +333,19 @@ async fn format_notification(
|
|||
} else {
|
||||
out.push_str(&format!("\n\nreviewer: {author}"));
|
||||
}
|
||||
out.push_str(&meta_suffix);
|
||||
Some(out)
|
||||
} else {
|
||||
// Regular comment.
|
||||
let kind = format!("comment on {}{num}{repo}", notif_type_label(notif_type));
|
||||
let mut out = format!("[{kind}] {title}\nurl: {url}\n\n{author}: {}", truncate(body_text, BODY_TRUNCATE));
|
||||
let mut out = format!(
|
||||
"[{kind}] {title}\nurl: {url}\n\n{author}: {}",
|
||||
truncate(body_text, BODY_TRUNCATE)
|
||||
);
|
||||
if out.ends_with('\n') {
|
||||
out.pop();
|
||||
}
|
||||
out.push_str(&meta_suffix);
|
||||
Some(out)
|
||||
}
|
||||
} else {
|
||||
|
|
@ -328,27 +379,7 @@ async fn format_notification(
|
|||
};
|
||||
|
||||
let mut out = format!("[{kind}] {title}\nurl: {html_url}");
|
||||
|
||||
// For new (open) items: fetch assignee(s). Skip body — agents can
|
||||
// run `hive-forge view <n>` for full content. One extra API call
|
||||
// only on creation events, not state changes.
|
||||
if is_new {
|
||||
let subject = fetch_json(client, subject_api_url, token).await;
|
||||
let assignees: Vec<&str> = subject
|
||||
.as_ref()
|
||||
.and_then(|s| s["assignees"].as_array())
|
||||
.map(|arr| {
|
||||
arr.iter()
|
||||
.filter_map(|a| a["login"].as_str())
|
||||
.collect()
|
||||
})
|
||||
.unwrap_or_default();
|
||||
if assignees.is_empty() {
|
||||
out.push_str("\nassignee: unassigned");
|
||||
} else {
|
||||
out.push_str(&format!("\nassignee: {}", assignees.join(", ")));
|
||||
}
|
||||
}
|
||||
out.push_str(&meta_suffix);
|
||||
Some(out)
|
||||
}
|
||||
}
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue