feat(#2327): surface forgejo stale/dismissed review flags in pr-status/reviews/merge
This commit is contained in:
parent
078ea74ba9
commit
cd092a8ae4
4 changed files with 77 additions and 26 deletions
|
|
@ -121,18 +121,30 @@ pub(crate) fn pct_encode(s: &str) -> String {
|
|||
out
|
||||
}
|
||||
|
||||
/// Latest non-comment review verdict per reviewer on a PR, as
|
||||
/// `(login, state)`. Reviews come oldest-first, so a later verdict from
|
||||
/// the same user supersedes an earlier one; `COMMENT` / `PENDING`
|
||||
/// reviews carry no verdict and are skipped. Shared by `pr-status` (for
|
||||
/// its health view + readiness verdict) and `pr-merge` (for its
|
||||
/// pre-merge changes-requested gate) so the verdict semantics stay in
|
||||
/// one place.
|
||||
pub(crate) fn latest_reviews(
|
||||
client: &Client,
|
||||
repo: &str,
|
||||
pr: u64,
|
||||
) -> Result<Vec<(String, String)>> {
|
||||
/// One reviewer's latest verdict on a PR, plus forgejo's `stale` bit.
|
||||
///
|
||||
/// `stale` is set by forgejo when the PR head commit changed after this
|
||||
/// review was submitted — branch protection then wants a fresh review, so
|
||||
/// a `stale` APPROVED no longer satisfies the merge gate even though its
|
||||
/// `state` string still reads `APPROVED`. Surfacing it stops the CLI from
|
||||
/// reporting a stale-but-technically-approved review as still-good.
|
||||
pub(crate) struct ReviewInfo {
|
||||
pub login: String,
|
||||
pub state: String,
|
||||
pub stale: bool,
|
||||
}
|
||||
|
||||
/// Latest non-comment review per reviewer on a PR. Reviews come
|
||||
/// oldest-first, so a later verdict from the same user supersedes an
|
||||
/// earlier one; `COMMENT` / `PENDING` reviews carry no verdict and are
|
||||
/// skipped. Shared by `pr-status` (health view + readiness verdict) and
|
||||
/// `pr-merge` (pre-merge changes-requested gate) so the verdict semantics
|
||||
/// stay in one place.
|
||||
///
|
||||
/// # Errors
|
||||
///
|
||||
/// Propagates the forge API errors from listing the PR's reviews.
|
||||
pub(crate) fn latest_reviews(client: &Client, repo: &str, pr: u64) -> Result<Vec<ReviewInfo>> {
|
||||
let (owner, name) = crate::client::split_repo(repo)?;
|
||||
let pr = index(pr)?;
|
||||
// Paginate (50/page, 10-page runaway cap — same ceiling the raw
|
||||
|
|
@ -151,7 +163,7 @@ pub(crate) fn latest_reviews(
|
|||
break;
|
||||
}
|
||||
}
|
||||
let mut latest: Vec<(String, String)> = Vec::new();
|
||||
let mut latest: Vec<ReviewInfo> = Vec::new();
|
||||
for r in &reviews {
|
||||
let Some(login) = r.user.as_ref().and_then(|u| u.login.as_deref()) else {
|
||||
continue;
|
||||
|
|
@ -160,10 +172,16 @@ pub(crate) fn latest_reviews(
|
|||
if st == "COMMENT" || st == "PENDING" || st.is_empty() {
|
||||
continue;
|
||||
}
|
||||
if let Some(slot) = latest.iter_mut().find(|(l, _)| l == login) {
|
||||
st.clone_into(&mut slot.1);
|
||||
let stale = r.stale.unwrap_or(false);
|
||||
if let Some(slot) = latest.iter_mut().find(|info| info.login == login) {
|
||||
st.clone_into(&mut slot.state);
|
||||
slot.stale = stale;
|
||||
} else {
|
||||
latest.push((login.to_owned(), st.to_owned()));
|
||||
latest.push(ReviewInfo {
|
||||
login: login.to_owned(),
|
||||
state: st.to_owned(),
|
||||
stale,
|
||||
});
|
||||
}
|
||||
}
|
||||
Ok(latest)
|
||||
|
|
|
|||
Loading…
Reference in a new issue