浏览代码

Merge pull request #1596 from chazkiker2/feature/proposed-fcp-details

[feature] Lang Agenda — Proposed FCPs: include concerns and FCP notes
Mark Rousskov 3 年之前
父节点
当前提交
0fc41f5ec5
共有 8 个文件被更改,包括 174 次插入16 次删除
  1. 13 3
      src/actions.rs
  2. 59 11
      src/github.rs
  3. 2 0
      src/github/graphql.rs
  4. 0 1
      src/interactions.rs
  5. 1 0
      src/lib.rs
  6. 81 0
      src/rfcbot.rs
  7. 16 0
      templates/_issues_fcps.tt
  8. 2 1
      templates/lang_agenda.tt

+ 13 - 3
src/actions.rs

@@ -2,8 +2,8 @@ use chrono::{DateTime, Utc};
 use std::collections::HashMap;
 
 use async_trait::async_trait;
-
 use reqwest::Client;
+use serde::{Deserialize, Serialize};
 use tera::{Context, Tera};
 
 use crate::github::{self, GithubClient, Repository};
@@ -35,7 +35,7 @@ pub struct QueryMap<'a> {
     pub query: Box<dyn github::IssuesQuery + Send + Sync>,
 }
 
-#[derive(serde::Serialize)]
+#[derive(Debug, serde::Serialize)]
 pub struct IssueDecorator {
     pub number: u64,
     pub title: String,
@@ -44,6 +44,16 @@ pub struct IssueDecorator {
     pub labels: String,
     pub assignees: String,
     pub updated_at: String,
+
+    pub fcp_details: Option<FCPDetails>,
+}
+
+#[derive(Serialize, Deserialize, Debug)]
+pub struct FCPDetails {
+    pub bot_tracking_comment_html_url: String,
+    pub bot_tracking_comment_content: String,
+    pub initiating_comment_html_url: String,
+    pub initiating_comment_content: String,
 }
 
 lazy_static! {
@@ -83,7 +93,7 @@ impl<'a> Action for Step<'a> {
                 };
 
                 for QueryMap { name, kind, query } in queries {
-                    let issues = query.query(&repository, &gh).await;
+                    let issues = query.query(&repository, name == &"proposed_fcp", &gh).await;
 
                     match issues {
                         Ok(issues_decorator) => match kind {

+ 59 - 11
src/github.rs

@@ -1,6 +1,4 @@
 use anyhow::Context;
-use tracing as log;
-
 use async_trait::async_trait;
 use chrono::{DateTime, FixedOffset, Utc};
 use futures::{future::BoxFuture, FutureExt};
@@ -8,10 +6,12 @@ use hyper::header::HeaderValue;
 use once_cell::sync::OnceCell;
 use reqwest::header::{AUTHORIZATION, USER_AGENT};
 use reqwest::{Client, Request, RequestBuilder, Response, StatusCode};
+use std::convert::TryInto;
 use std::{
     fmt,
     time::{Duration, SystemTime},
 };
+use tracing as log;
 
 pub mod graphql;
 
@@ -838,8 +838,6 @@ pub fn files_changed(diff: &str) -> Vec<&str> {
     files
 }
 
-impl IssuesEvent {}
-
 #[derive(Debug, serde::Deserialize)]
 pub struct IssueSearchResult {
     pub total_count: usize,
@@ -929,7 +927,7 @@ impl Repository {
             } else {
                 self.build_issues_url(&filters, include_labels, ordering)
             };
-    
+
             let result = client.get(&url);
             if use_search_api {
                 let result = client
@@ -939,7 +937,7 @@ impl Repository {
                 issues.extend(result.items);
                 if issues.len() < result.total_count {
                     ordering.page += 1;
-                    continue
+                    continue;
                 }
             } else {
                 // FIXME: paginate with non-search
@@ -1046,11 +1044,20 @@ pub struct Query<'a> {
     pub exclude_labels: Vec<&'a str>,
 }
 
+fn quote_reply(markdown: &str) -> String {
+    if markdown.is_empty() {
+        String::from("*No content*")
+    } else {
+        format!("\n\t> {}", markdown.replace("\n", "\n\t> "))
+    }
+}
+
 #[async_trait]
 impl<'q> IssuesQuery for Query<'q> {
     async fn query<'a>(
         &'a self,
         repo: &'a Repository,
+        include_fcp_details: bool,
         client: &'a GithubClient,
     ) -> anyhow::Result<Vec<crate::actions::IssueDecorator>> {
         let issues = repo
@@ -1058,9 +1065,48 @@ impl<'q> IssuesQuery for Query<'q> {
             .await
             .with_context(|| "Unable to get issues.")?;
 
-        let issues_decorator: Vec<_> = issues
-            .iter()
-            .map(|issue| crate::actions::IssueDecorator {
+        let fcp_map = crate::rfcbot::get_all_fcps().await?;
+
+        let mut issues_decorator = Vec::new();
+        for issue in issues {
+            let fcp_details = if include_fcp_details {
+                let repository_name = if let Some(repo) = issue.repository.get() {
+                    repo.repository.clone()
+                } else {
+                    let re = regex::Regex::new("https://github.com/rust-lang/|/").unwrap();
+                    let split = re.split(&issue.html_url).collect::<Vec<&str>>();
+                    split[1].to_string()
+                };
+                let key = format!(
+                    "rust-lang/{}:{}:{}",
+                    repository_name, issue.number, issue.title,
+                );
+
+                if let Some(fcp) = fcp_map.get(&key) {
+                    let bot_tracking_comment_html_url = format!(
+                        "{}#issuecomment-{}",
+                        issue.html_url, fcp.fcp.fk_bot_tracking_comment
+                    );
+                    let bot_tracking_comment_content = quote_reply(&fcp.status_comment.body);
+
+                    let fk_initiating_comment = fcp.fcp.fk_initiating_comment;
+                    let init_comment = issue
+                        .get_comment(&client, fk_initiating_comment.try_into()?)
+                        .await?;
+
+                    Some(crate::actions::FCPDetails {
+                        bot_tracking_comment_html_url,
+                        bot_tracking_comment_content,
+                        initiating_comment_html_url: init_comment.html_url.clone(),
+                        initiating_comment_content: quote_reply(&init_comment.body),
+                    })
+                } else {
+                    None
+                }
+            } else {
+                None
+            };
+            issues_decorator.push(crate::actions::IssueDecorator {
                 title: issue.title.clone(),
                 number: issue.number,
                 html_url: issue.html_url.clone(),
@@ -1078,8 +1124,9 @@ impl<'q> IssuesQuery for Query<'q> {
                     .collect::<Vec<_>>()
                     .join(", "),
                 updated_at: crate::actions::to_human(issue.updated_at),
-            })
-            .collect();
+                fcp_details,
+            });
+        }
 
         Ok(issues_decorator)
     }
@@ -1374,6 +1421,7 @@ pub trait IssuesQuery {
     async fn query<'a>(
         &'a self,
         repo: &'a Repository,
+        include_fcp_details: bool,
         client: &'a GithubClient,
     ) -> anyhow::Result<Vec<crate::actions::IssueDecorator>>;
 }

+ 2 - 0
src/github/graphql.rs

@@ -164,6 +164,7 @@ impl super::IssuesQuery for LeastRecentlyReviewedPullRequests {
     async fn query<'a>(
         &'a self,
         repo: &'a super::Repository,
+        _include_fcp_details: bool,
         client: &'a super::GithubClient,
     ) -> anyhow::Result<Vec<crate::actions::IssueDecorator>> {
         use cynic::QueryBuilder;
@@ -317,6 +318,7 @@ impl super::IssuesQuery for LeastRecentlyReviewedPullRequests {
                         labels,
                         assignees,
                         updated_at,
+                        fcp_details: None,
                     }
                 },
             )

+ 0 - 1
src/interactions.rs

@@ -1,6 +1,5 @@
 use crate::github::{GithubClient, Issue};
 use std::fmt::Write;
-use tracing as log;
 
 pub struct ErrorComment<'a> {
     issue: &'a Issue,

+ 1 - 0
src/lib.rs

@@ -19,6 +19,7 @@ pub mod handlers;
 pub mod interactions;
 pub mod notification_listing;
 pub mod payload;
+pub mod rfcbot;
 pub mod team;
 mod team_data;
 pub mod triage;

+ 81 - 0
src/rfcbot.rs

@@ -0,0 +1,81 @@
+use reqwest::Url;
+use serde::{Deserialize, Serialize};
+use std::collections::HashMap;
+
+#[derive(Serialize, Deserialize, Debug, Clone)]
+pub struct FCP {
+    pub id: u32,
+    pub fk_issue: u32,
+    pub fk_initiator: u32,
+    pub fk_initiating_comment: u32,
+    pub disposition: Option<String>,
+    pub fk_bot_tracking_comment: u32,
+    pub fcp_start: Option<String>,
+    pub fcp_closed: bool,
+}
+#[derive(Serialize, Deserialize, Debug, Clone)]
+pub struct Reviewer {
+    pub id: u32,
+    pub login: String,
+}
+#[derive(Serialize, Deserialize, Debug, Clone)]
+pub struct Review {
+    pub reviewer: Reviewer,
+    pub approved: bool,
+}
+#[derive(Serialize, Deserialize, Debug, Clone)]
+pub struct FCPIssue {
+    pub id: u32,
+    pub number: u32,
+    pub fk_milestone: Option<String>,
+    pub fk_user: u32,
+    pub fk_assignee: Option<u32>,
+    pub open: bool,
+    pub is_pull_request: bool,
+    pub title: String,
+    pub body: String,
+    pub locked: bool,
+    pub closed_at: Option<String>,
+    pub created_at: Option<String>,
+    pub updated_at: Option<String>,
+    pub labels: Vec<String>,
+    pub repository: String,
+}
+
+#[derive(Serialize, Deserialize, Debug, Clone)]
+pub struct StatusComment {
+    pub id: u64,
+    pub fk_issue: u32,
+    pub fk_user: u32,
+    pub body: String,
+    pub created_at: String,
+    pub updated_at: Option<String>,
+    pub repository: String,
+}
+
+#[derive(Serialize, Deserialize, Debug, Clone)]
+pub struct FullFCP {
+    pub fcp: FCP,
+    pub reviews: Vec<Review>,
+    pub issue: FCPIssue,
+    pub status_comment: StatusComment,
+}
+
+pub async fn get_all_fcps() -> anyhow::Result<HashMap<String, FullFCP>> {
+    let url = Url::parse(&"https://rfcbot.rs/api/all")?;
+    let res = reqwest::get(url).await?.json::<Vec<FullFCP>>().await?;
+    let mut map: HashMap<String, FullFCP> = HashMap::new();
+    for full_fcp in res.into_iter() {
+        map.insert(
+            format!(
+                "{}:{}:{}",
+                full_fcp.issue.repository.clone(),
+                full_fcp.issue.number.clone(),
+                full_fcp.issue.title.clone(),
+            ),
+            full_fcp,
+        );
+    }
+
+    Ok(map)
+}

+ 16 - 0
templates/_issues_fcps.tt

@@ -0,0 +1,16 @@
+{% import "_issue.tt" as issue %}
+
+{% macro render(issues, heading="###", empty="No issues this time.") %}
+{%- for issue in issues %}
+{{heading}} "{{issue.title}}" {{issue.repo_name}}#{{issue.number}}
+
+- **Link:** {{issue.html_url}}
+- [**Tracking Comment**]({{issue.fcp_details.bot_tracking_comment_html_url}}): {{issue.fcp_details.bot_tracking_comment_content}}
+- [**Initiating Comment**]({{issue.fcp_details.initiating_comment_html_url}}): {{issue.fcp_details.initiating_comment_content}}
+
+{%else%}
+
+None.
+
+{%endfor%}
+{% endmacro %}

+ 2 - 1
templates/lang_agenda.tt

@@ -1,4 +1,5 @@
 {% import "_issues_heading.tt" as issues_heading %}
+{% import "_issues_fcps.tt" as issues_fcps %}
 {% import "_issues.tt" as issues %}
 ---
 title: Triage meeting DATE
@@ -47,7 +48,7 @@ tags: triage-meeting
 
 **Check your boxes!**
 
-{{-issues_heading::render(issues=proposed_fcp)}}
+{{-issues_fcps::render(issues=proposed_fcp)}}
 
 ## Active FCPs