From d0cb94acc081a16e980b37e55ef5b6b10ab30c3a Mon Sep 17 00:00:00 2001 From: apiraino Date: Thu, 11 Jan 2024 12:48:29 +0100 Subject: [PATCH] Add MCP unresolved concerns to T-compiler meeting agenda --- src/actions.rs | 1 + src/github.rs | 86 +++++++++++++++++++++++------- templates/_issue.tt | 5 +- templates/prioritization_agenda.tt | 2 +- 4 files changed, 73 insertions(+), 21 deletions(-) diff --git a/src/actions.rs b/src/actions.rs index 4c4b35c9..73557078 100644 --- a/src/actions.rs +++ b/src/actions.rs @@ -65,6 +65,7 @@ pub struct FCPDetails { #[derive(Serialize, Deserialize, Debug)] pub struct MCPDetails { pub zulip_link: String, + pub concerns: Option>, } lazy_static! { diff --git a/src/github.rs b/src/github.rs index 96b29f9e..b987599b 100644 --- a/src/github.rs +++ b/src/github.rs @@ -5,6 +5,7 @@ use chrono::{DateTime, FixedOffset, Utc}; use futures::{future::BoxFuture, FutureExt}; use hyper::header::HeaderValue; use once_cell::sync::OnceCell; +use regex::Regex; use reqwest::header::{AUTHORIZATION, USER_AGENT}; use reqwest::{Client, Request, RequestBuilder, Response, StatusCode}; use std::collections::{HashMap, HashSet}; @@ -509,19 +510,6 @@ impl Issue { Ok(comment) } - // returns an array of one element - pub async fn get_first_comment(&self, client: &GithubClient) -> anyhow::Result> { - let comment_url = format!( - "{}/issues/{}/comments?page=1&per_page=1", - self.repository().url(client), - self.number, - ); - Ok(client - .json::>(client.get(&comment_url)) - .await?) - } - - // returns an array of one element pub async fn get_first100_comments( &self, client: &GithubClient, @@ -1763,12 +1751,22 @@ impl<'q> IssuesQuery for Query<'q> { }; let mcp_details = if include_mcp_details { - let first_comment = issue.get_first_comment(&client).await?; - let split = re_zulip_link - .split(&first_comment[0].body) - .collect::>(); - let zulip_link = split.last().unwrap_or(&"#").to_string(); - Some(crate::actions::MCPDetails { zulip_link }) + let first100_comments = issue.get_first100_comments(&client).await?; + let (zulip_link, concerns) = if !first100_comments.is_empty() { + let split = re_zulip_link + .split(&first100_comments[0].body) + .collect::>(); + let zulip_link = split.last().unwrap_or(&"#").to_string(); + let concerns = find_open_concerns(first100_comments); + (zulip_link, concerns) + } else { + ("".to_string(), None) + }; + + Some(crate::actions::MCPDetails { + zulip_link, + concerns, + }) } else { None }; @@ -1800,6 +1798,56 @@ impl<'q> IssuesQuery for Query<'q> { } } +/// Return open concerns filed in an issue under MCP/RFC process +/// Concerns are marked by `@rfcbot concern` and `@rfcbot resolve` +fn find_open_concerns(comments: Vec) -> Option> { + let re_concern_raise = + Regex::new(r"@rfcbot concern (?P.*)").expect("Invalid regexp"); + let re_concern_solve = + Regex::new(r"@rfcbot resolve (?P.*)").expect("Invalid regexp"); + let mut raised: HashMap = HashMap::new(); + let mut solved: HashMap = HashMap::new(); + + for comment in comments { + // Parse the comment and look for text markers to raise or resolve concerns + let comment_lines = comment.body.lines(); + for line in comment_lines { + let r: Vec<&str> = re_concern_raise + .captures_iter(line) + .map(|caps| caps.name("concern_title").map(|f| f.as_str()).unwrap_or("")) + .collect(); + let s: Vec<&str> = re_concern_solve + .captures_iter(line) + .map(|caps| caps.name("concern_title").map(|f| f.as_str()).unwrap_or("")) + .collect(); + + // pick the first match only + if !r.is_empty() { + let x = r[0].replace("@rfcbot concern", ""); + raised.insert(x.trim().to_string(), comment.html_url.to_string()); + } + if !s.is_empty() { + let x = s[0].replace("@rfcbot resolve", ""); + solved.insert(x.trim().to_string(), comment.html_url.to_string()); + } + } + } + + // remove solved concerns and return the rest + let unresolved_concerns = raised + .iter() + .filter_map(|(&ref title, &ref comment_url)| { + if !solved.contains_key(title) { + Some((title.to_string(), comment_url.to_string())) + } else { + None + } + }) + .collect(); + + Some(unresolved_concerns) +} + #[derive(Debug, serde::Deserialize)] #[serde(rename_all = "snake_case")] pub enum CreateKind { diff --git a/templates/_issue.tt b/templates/_issue.tt index 60011ef2..159af4bc 100644 --- a/templates/_issue.tt +++ b/templates/_issue.tt @@ -1 +1,4 @@ -{% macro render(issue, with_age="") %}"{{issue.title}}" [{{issue.repo_name}}#{{issue.number}}]({{issue.html_url}}){% if issue.mcp_details.zulip_link %} ([Zulip]({{issue.mcp_details.zulip_link}})){% endif %}{% if with_age %} (last review activity: {{issue.updated_at_hts}}){% endif %}{% endmacro %} +{% macro render(issue, with_age="") %}"{{issue.title}}" [{{issue.repo_name}}#{{issue.number}}]({{issue.html_url}}){% if issue.mcp_details.zulip_link %} ([Zulip]({{issue.mcp_details.zulip_link}})){% endif %}{% if with_age %} (last review activity: {{issue.updated_at_hts}}){%- endif -%} +{% if issue.mcp_details.concerns %}{%- for concern in issue.mcp_details.concerns %} + - concern: [{{- concern.0 -}}]({{- concern.1 -}}) +{%- endfor -%}{% endif -%}{% endmacro %} diff --git a/templates/prioritization_agenda.tt b/templates/prioritization_agenda.tt index a92ee980..7fbabf81 100644 --- a/templates/prioritization_agenda.tt +++ b/templates/prioritization_agenda.tt @@ -48,7 +48,7 @@ note_id: xxx ## Backport nominations -[T-compiler stable](https://github.com/rust-lang/rust/issues?q=is%3Aall+label%3Abeta-nominated+-label%3Abeta-accepted+label%3AT-compiler) / [T-compiler beta](https://github.com/rust-lang/rust/issues?q=is%3Aall+label%3Astable-nominated+-label%3Astable-accepted+label%3AT-compiler) +[T-compiler beta](https://github.com/rust-lang/rust/issues?q=is%3Aall+label%3Abeta-nominated+-label%3Abeta-accepted+label%3AT-compiler) / [T-compiler stable](https://github.com/rust-lang/rust/issues?q=is%3Aall+label%3Astable-nominated+-label%3Astable-accepted+label%3AT-compiler) {{-issues::render(issues=beta_nominated_t_compiler, branch=":beta:", empty="No beta nominations for `T-compiler` this time.")}}