From e77ac94e537d39b07e2b4c201dcbe5194be7d899 Mon Sep 17 00:00:00 2001 From: Tyler Mandry Date: Fri, 14 Jan 2022 10:10:09 -0800 Subject: [PATCH 1/2] Use milestones for rust-lang/rust changes --- src/main.rs | 128 ++++++++++++++++++++++++++++++++++++++++++++-------- 1 file changed, 109 insertions(+), 19 deletions(-) diff --git a/src/main.rs b/src/main.rs index fc00f80..1f1df25 100644 --- a/src/main.rs +++ b/src/main.rs @@ -5,6 +5,7 @@ use askama::Template; use chrono::prelude::*; use chrono::Duration; +use reqwest::header::HeaderMap; use serde_json as json; type JsonRefArray<'a> = Vec<&'a json::Value>; @@ -54,9 +55,9 @@ fn main() { while today - end > six_weeks { end = end + six_weeks; } - let start = end - six_weeks; - let issues = get_issues(start, end, "rust"); + + let issues = get_issues_by_milestone(&version, "rust"); // Skips `beta-accepted` as those PRs were backported onto the // previous stable. @@ -84,7 +85,7 @@ fn main() { let (compat_unsorted, libraries_unsorted, language_unsorted, compiler_unsorted, unsorted) = partition_prs(rest); - let cargo_issues = get_issues(start, end, "cargo"); + let cargo_issues = get_issues_by_date(start, end, "cargo"); let (cargo_relnotes, cargo_unsorted) = { let (relnotes, rest) = partition_by_tag(cargo_issues.iter(), relnotes_tags); @@ -119,21 +120,99 @@ fn main() { println!("{}", relnotes.render().unwrap()); } -fn get_issues(start: Date, end: Date, repo_name: &'static str) -> Vec { +fn get_issues_by_milestone(version: &str, repo_name: &'static str) -> Vec { use reqwest::blocking::Client; - use reqwest::header::*; - let token = env::var("GITHUB_TOKEN").expect("Set GITHUB_TOKEN to a valid token"); - let mut headers = HeaderMap::new(); - headers.insert(CONTENT_TYPE, "application/json".parse().unwrap()); - headers.insert(ACCEPT, "application/json".parse().unwrap()); - headers.insert( - AUTHORIZATION, - format!("Bearer {}", token) - .parse() - .unwrap(), - ); - headers.insert(USER_AGENT, "Rust-relnotes/0.1.0".parse().unwrap()); + let headers = request_header(); + let mut args = BTreeMap::new(); + args.insert("states", String::from("[MERGED]")); + args.insert("last", String::from("100")); + let mut issues = Vec::new(); + + loop { + let query = format!( + r#" + query {{ + repository(owner: "rust-lang", name: "{repo_name}") {{ + milestones(query: "{version}", first: 1) {{ + totalCount + nodes {{ + pullRequests({args}) {{ + nodes {{ + number + title + url + labels(last: 100) {{ + nodes {{ + name + }} + }} + }} + pageInfo {{ + startCursor + }} + }} + }} + }} + }} + }}"#, + repo_name = repo_name, + version = version, + args = args + .iter() + .map(|(k, v)| format!("{}: {}", k, v)) + .collect::>() + .join(",") + ) + .replace(" ", "") + .replace("\n", " ") + .replace('"', "\\\""); + + let json_query = format!("{{\"query\": \"{}\"}}", query); + + let client = Client::new(); + + let json = client + .post("https://api.github.com/graphql") + .headers(headers.clone()) + .body(json_query) + .send() + .unwrap() + .json::() + .unwrap(); + + let milestones_data = json["data"]["repository"]["milestones"].clone(); + assert_eq!( + milestones_data["totalCount"].as_u64().unwrap(), + 1, + "More than one milestone matched the query \"{version}\". Please be more specific.", + version = version + ); + let pull_requests_data = milestones_data["nodes"][0]["pullRequests"].clone(); + + let mut pull_requests = pull_requests_data["nodes"].as_array().unwrap().clone(); + issues.append(&mut pull_requests); + + match &pull_requests_data["pageInfo"]["startCursor"] { + json::Value::String(cursor) => { + args.insert("before", format!("\"{}\"", cursor)); + } + json::Value::Null => { + break issues; + } + _ => unreachable!(), + } + } +} + +fn get_issues_by_date( + start: Date, + end: Date, + repo_name: &'static str, +) -> Vec { + use reqwest::blocking::Client; + + let headers = request_header(); let mut args = BTreeMap::new(); args.insert("states", String::from("[MERGED]")); args.insert("last", String::from("100")); @@ -142,9 +221,9 @@ fn get_issues(start: Date, end: Date, repo_name: &'static str) -> Vec< loop { let query = format!( - " + r#" query {{ - repository(owner: \"rust-lang\", name: \"{repo_name}\") {{ + repository(owner: "rust-lang", name: "{repo_name}") {{ pullRequests({args}) {{ nodes {{ mergedAt @@ -162,7 +241,7 @@ fn get_issues(start: Date, end: Date, repo_name: &'static str) -> Vec< }} }} }} - }}", + }}"#, repo_name = repo_name, args = args .iter() @@ -229,6 +308,17 @@ fn get_issues(start: Date, end: Date, repo_name: &'static str) -> Vec< } } +fn request_header() -> HeaderMap { + use reqwest::header::*; + let token = env::var("GITHUB_TOKEN").expect("Set GITHUB_TOKEN to a valid token"); + let mut headers = HeaderMap::new(); + headers.insert(CONTENT_TYPE, "application/json".parse().unwrap()); + headers.insert(ACCEPT, "application/json".parse().unwrap()); + headers.insert(AUTHORIZATION, format!("Bearer {}", token).parse().unwrap()); + headers.insert(USER_AGENT, "Rust-relnotes/0.1.0".parse().unwrap()); + headers +} + fn map_to_line_items<'a>( prefix: &'static str, iter: impl IntoIterator, From 4318213f47daf15ac5c963b5cf13484936c74300 Mon Sep 17 00:00:00 2001 From: Tyler Mandry Date: Fri, 14 Jan 2022 14:49:10 -0800 Subject: [PATCH 2/2] Sort issues --- src/main.rs | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/src/main.rs b/src/main.rs index 1f1df25..31c244e 100644 --- a/src/main.rs +++ b/src/main.rs @@ -57,7 +57,8 @@ fn main() { } let start = end - six_weeks; - let issues = get_issues_by_milestone(&version, "rust"); + let mut issues = get_issues_by_milestone(&version, "rust"); + issues.sort_by_cached_key(|issue| issue["number"].as_u64().unwrap()); // Skips `beta-accepted` as those PRs were backported onto the // previous stable. @@ -85,7 +86,8 @@ fn main() { let (compat_unsorted, libraries_unsorted, language_unsorted, compiler_unsorted, unsorted) = partition_prs(rest); - let cargo_issues = get_issues_by_date(start, end, "cargo"); + let mut cargo_issues = get_issues_by_date(start, end, "cargo"); + cargo_issues.sort_by_cached_key(|issue| issue["number"].as_u64().unwrap()); let (cargo_relnotes, cargo_unsorted) = { let (relnotes, rest) = partition_by_tag(cargo_issues.iter(), relnotes_tags);