diff --git a/site/src/comparison.rs b/site/src/comparison.rs index 47d34dde6..59f188f19 100644 --- a/site/src/comparison.rs +++ b/site/src/comparison.rs @@ -4,6 +4,7 @@ use crate::api; use crate::db::{ArtifactId, Cache, Crate, Profile}; +use crate::github; use crate::load::SiteCtxt; use crate::selector::{self, Tag}; @@ -80,7 +81,7 @@ pub async fn handle_triage( } let end = end.unwrap_or(after); - let report = generate_report(&start, &end, report); + let report = generate_report(&start, &end, report).await; Ok(api::triage::Response(report)) } @@ -191,7 +192,7 @@ impl ComparisonSummary<'_> { use std::fmt::Write; let mut result = if let Some(pr) = comparison.b.pr { - let title = gh_pr_title(pr).await; + let title = github::pr_title(pr).await; format!( "{} [#{}](https://github.com/rust-lang/rust/issues/{})\n", title, pr, pr @@ -437,7 +438,7 @@ impl Comparison { /// Gets the sha of the next commit after `b` pub fn next(&self, master_commits: &[collector::MasterCommit]) -> Option { - next_commit(&self.a.artifact, master_commits).map(|c| c.parent_sha.clone()) + next_commit(&self.b.artifact, master_commits).map(|c| c.sha.clone()) } fn get_benchmarks<'a>(&'a self) -> Vec> { @@ -725,7 +726,7 @@ impl std::fmt::Display for Direction { } } -fn generate_report( +async fn generate_report( start: &Bound, end: &Bound, mut report: HashMap>, @@ -742,6 +743,22 @@ fn generate_report( let regressions = report.remove(&Direction::Regression).unwrap_or_default(); let improvements = report.remove(&Direction::Improvement).unwrap_or_default(); let mixed = report.remove(&Direction::Mixed).unwrap_or_default(); + let untriaged = match github::untriaged_perf_regressions().await { + Ok(u) => u + .iter() + .map(|github::PullRequest { title, number }| { + format!( + "- [#{} {}](https://github.com/rust-lang/rust/pull/{})", + number, title, number + ) + }) + .collect::>() + .join("\n"), + Err(e) => format!( + "An **error** occurred when finding the untriaged PRs: {}", + e + ), + }; format!( r#####"# {date} Triage Log @@ -764,6 +781,10 @@ Revision range: [{first_commit}..{last_commit}](https://perf.rust-lang.org/?star {mixed} +#### Untriaged Pull Requests + +{untriaged} + #### Nags requiring follow up TODO: Nags @@ -778,6 +799,7 @@ TODO: Nags regressions = regressions.join("\n\n"), improvements = improvements.join("\n\n"), mixed = mixed.join("\n\n"), + untriaged = untriaged ) } @@ -795,37 +817,3 @@ fn compare_link(start: &ArtifactId, end: &ArtifactId) -> String { start, end ) } - -async fn gh_pr_title(pr: u32) -> String { - let url = format!("https://api.github.com/repos/rust-lang/rust/pulls/{}", pr); - let client = reqwest::Client::new(); - let mut request = client - .get(&url) - .header("Content-Type", "application/json") - .header("User-Agent", "rustc-perf"); - - if let Some(token) = std::env::var("GITHUB_TOKEN").ok() { - request = request.header("Authorization", format!("token {}", token)); - } - - async fn send(request: reqwest::RequestBuilder) -> Result { - Ok(request - .send() - .await? - .error_for_status()? - .json::() - .await? - .get("title") - .ok_or_else(|| "JSON was malformed".to_owned())? - .as_str() - .ok_or_else(|| "JSON was malformed".to_owned())? - .to_owned()) - } - match send(request).await { - Ok(t) => t, - Err(e) => { - eprintln!("Error fetching url: {}", e); - String::from("") - } - } -} diff --git a/site/src/github.rs b/site/src/github.rs index c54fbe1a9..7fc6a287d 100644 --- a/site/src/github.rs +++ b/site/src/github.rs @@ -1,15 +1,18 @@ use crate::api::{github, ServerResult}; use crate::comparison::{ComparisonSummary, Direction}; use crate::load::{Config, SiteCtxt, TryCommit}; -use anyhow::Context as _; -use hashbrown::HashSet; -use serde::Deserialize; +use anyhow::Context as _; use database::ArtifactId; +use hashbrown::HashSet; use regex::Regex; use reqwest::header::USER_AGENT; +use serde::Deserialize; + use std::{fmt::Write, sync::Arc, time::Duration}; +type BoxedError = Box; + lazy_static::lazy_static! { static ref BODY_TRY_COMMIT: Regex = Regex::new(r#"(?:\W|^)@rust-timer\s+build\s+(\w+)(?:\W|$)(?:include=(\S+))?\s*(?:exclude=(\S+))?\s*(?:runs=(\d+))?"#).unwrap(); @@ -732,3 +735,84 @@ async fn categorize_benchmark( write!(result, "\n{}", DISAGREEMENT).unwrap(); (result, Some(direction)) } + +pub(crate) struct PullRequest { + pub number: u64, + pub title: String, +} + +/// Fetch all merged PRs that are labeled with `perf-regression` and not `perf-regression-triaged` +pub(crate) async fn untriaged_perf_regressions() -> Result, BoxedError> { + let url = "https://api.github.com/search/issues?q=repo:rust-lang/rust+label:perf-regression+-label:perf-regression-triaged+is:merged".to_owned(); + let request = github_request(&url); + let body = send_request(request).await?; + Ok(body + .get("items") + .ok_or_else(malformed_json_error)? + .as_array() + .ok_or_else(malformed_json_error)? + .iter() + .map(|v| { + let title = v + .get("title") + .ok_or_else(malformed_json_error)? + .as_str() + .ok_or_else(malformed_json_error)? + .to_owned(); + let number = v + .get("number") + .ok_or_else(malformed_json_error)? + .as_u64() + .ok_or_else(malformed_json_error)?; + Ok(PullRequest { title, number }) + }) + .collect::>()?) +} + +/// Get the title of a PR with the given number +pub(crate) async fn pr_title(pr: u32) -> String { + let url = format!("https://api.github.com/repos/rust-lang/rust/pulls/{}", pr); + let request = github_request(&url); + + async fn send(request: reqwest::RequestBuilder) -> Result { + let body = send_request(request).await?; + Ok(body + .get("title") + .ok_or_else(malformed_json_error)? + .as_str() + .ok_or_else(malformed_json_error)? + .to_owned()) + } + match send(request).await { + Ok(t) => t, + Err(e) => { + eprintln!("Error fetching url: {}", e); + String::from("") + } + } +} + +fn github_request(url: &str) -> reqwest::RequestBuilder { + let client = reqwest::Client::new(); + let mut request = client + .get(url) + .header("Content-Type", "application/json") + .header("User-Agent", "rustc-perf"); + if let Some(token) = std::env::var("GITHUB_TOKEN").ok() { + request = request.header("Authorization", format!("token {}", token)); + } + request +} + +async fn send_request(request: reqwest::RequestBuilder) -> Result { + Ok(request + .send() + .await? + .error_for_status()? + .json::() + .await?) +} + +fn malformed_json_error() -> String { + "JSON was malformed".to_owned() +}