@@ -5,6 +5,7 @@ use askama::Template;
55use chrono:: prelude:: * ;
66use chrono:: Duration ;
77
8+ use reqwest:: header:: HeaderMap ;
89use serde_json as json;
910
1011type JsonRefArray < ' a > = Vec < & ' a json:: Value > ;
@@ -54,9 +55,9 @@ fn main() {
5455 while today - end > six_weeks {
5556 end = end + six_weeks;
5657 }
57-
5858 let start = end - six_weeks;
59- let issues = get_issues ( start, end, "rust" ) ;
59+
60+ let issues = get_issues_by_milestone ( & version, "rust" ) ;
6061
6162 // Skips `beta-accepted` as those PRs were backported onto the
6263 // previous stable.
@@ -84,7 +85,7 @@ fn main() {
8485 let ( compat_unsorted, libraries_unsorted, language_unsorted, compiler_unsorted, unsorted) =
8586 partition_prs ( rest) ;
8687
87- let cargo_issues = get_issues ( start, end, "cargo" ) ;
88+ let cargo_issues = get_issues_by_date ( start, end, "cargo" ) ;
8889
8990 let ( cargo_relnotes, cargo_unsorted) = {
9091 let ( relnotes, rest) = partition_by_tag ( cargo_issues. iter ( ) , relnotes_tags) ;
@@ -119,21 +120,99 @@ fn main() {
119120 println ! ( "{}" , relnotes. render( ) . unwrap( ) ) ;
120121}
121122
122- fn get_issues ( start : Date < Utc > , end : Date < Utc > , repo_name : & ' static str ) -> Vec < json:: Value > {
123+ fn get_issues_by_milestone ( version : & str , repo_name : & ' static str ) -> Vec < json:: Value > {
123124 use reqwest:: blocking:: Client ;
124- use reqwest:: header:: * ;
125125
126- let token = env:: var ( "GITHUB_TOKEN" ) . expect ( "Set GITHUB_TOKEN to a valid token" ) ;
127- let mut headers = HeaderMap :: new ( ) ;
128- headers. insert ( CONTENT_TYPE , "application/json" . parse ( ) . unwrap ( ) ) ;
129- headers. insert ( ACCEPT , "application/json" . parse ( ) . unwrap ( ) ) ;
130- headers. insert (
131- AUTHORIZATION ,
132- format ! ( "Bearer {}" , token)
133- . parse ( )
134- . unwrap ( ) ,
135- ) ;
136- headers. insert ( USER_AGENT , "Rust-relnotes/0.1.0" . parse ( ) . unwrap ( ) ) ;
126+ let headers = request_header ( ) ;
127+ let mut args = BTreeMap :: new ( ) ;
128+ args. insert ( "states" , String :: from ( "[MERGED]" ) ) ;
129+ args. insert ( "last" , String :: from ( "100" ) ) ;
130+ let mut issues = Vec :: new ( ) ;
131+
132+ loop {
133+ let query = format ! (
134+ r#"
135+ query {{
136+ repository(owner: "rust-lang", name: "{repo_name}") {{
137+ milestones(query: "{version}", first: 1) {{
138+ totalCount
139+ nodes {{
140+ pullRequests({args}) {{
141+ nodes {{
142+ number
143+ title
144+ url
145+ labels(last: 100) {{
146+ nodes {{
147+ name
148+ }}
149+ }}
150+ }}
151+ pageInfo {{
152+ startCursor
153+ }}
154+ }}
155+ }}
156+ }}
157+ }}
158+ }}"# ,
159+ repo_name = repo_name,
160+ version = version,
161+ args = args
162+ . iter( )
163+ . map( |( k, v) | format!( "{}: {}" , k, v) )
164+ . collect:: <Vec <_>>( )
165+ . join( "," )
166+ )
167+ . replace ( " " , "" )
168+ . replace ( "\n " , " " )
169+ . replace ( '"' , "\\ \" " ) ;
170+
171+ let json_query = format ! ( "{{\" query\" : \" {}\" }}" , query) ;
172+
173+ let client = Client :: new ( ) ;
174+
175+ let json = client
176+ . post ( "https://api.github.com/graphql" )
177+ . headers ( headers. clone ( ) )
178+ . body ( json_query)
179+ . send ( )
180+ . unwrap ( )
181+ . json :: < json:: Value > ( )
182+ . unwrap ( ) ;
183+
184+ let milestones_data = json[ "data" ] [ "repository" ] [ "milestones" ] . clone ( ) ;
185+ assert_eq ! (
186+ milestones_data[ "totalCount" ] . as_u64( ) . unwrap( ) ,
187+ 1 ,
188+ "More than one milestone matched the query \" {version}\" . Please be more specific." ,
189+ version = version
190+ ) ;
191+ let pull_requests_data = milestones_data[ "nodes" ] [ 0 ] [ "pullRequests" ] . clone ( ) ;
192+
193+ let mut pull_requests = pull_requests_data[ "nodes" ] . as_array ( ) . unwrap ( ) . clone ( ) ;
194+ issues. append ( & mut pull_requests) ;
195+
196+ match & pull_requests_data[ "pageInfo" ] [ "startCursor" ] {
197+ json:: Value :: String ( cursor) => {
198+ args. insert ( "before" , format ! ( "\" {}\" " , cursor) ) ;
199+ }
200+ json:: Value :: Null => {
201+ break issues;
202+ }
203+ _ => unreachable ! ( ) ,
204+ }
205+ }
206+ }
207+
208+ fn get_issues_by_date (
209+ start : Date < Utc > ,
210+ end : Date < Utc > ,
211+ repo_name : & ' static str ,
212+ ) -> Vec < json:: Value > {
213+ use reqwest:: blocking:: Client ;
214+
215+ let headers = request_header ( ) ;
137216 let mut args = BTreeMap :: new ( ) ;
138217 args. insert ( "states" , String :: from ( "[MERGED]" ) ) ;
139218 args. insert ( "last" , String :: from ( "100" ) ) ;
@@ -142,9 +221,9 @@ fn get_issues(start: Date<Utc>, end: Date<Utc>, repo_name: &'static str) -> Vec<
142221
143222 loop {
144223 let query = format ! (
145- "
224+ r# "
146225 query {{
147- repository(owner: \ " rust-lang\ " , name: \ " {repo_name}\ " ) {{
226+ repository(owner: "rust-lang", name: "{repo_name}") {{
148227 pullRequests({args}) {{
149228 nodes {{
150229 mergedAt
@@ -162,7 +241,7 @@ fn get_issues(start: Date<Utc>, end: Date<Utc>, repo_name: &'static str) -> Vec<
162241 }}
163242 }}
164243 }}
165- }}" ,
244+ }}"# ,
166245 repo_name = repo_name,
167246 args = args
168247 . iter( )
@@ -229,6 +308,17 @@ fn get_issues(start: Date<Utc>, end: Date<Utc>, repo_name: &'static str) -> Vec<
229308 }
230309}
231310
311+ fn request_header ( ) -> HeaderMap {
312+ use reqwest:: header:: * ;
313+ let token = env:: var ( "GITHUB_TOKEN" ) . expect ( "Set GITHUB_TOKEN to a valid token" ) ;
314+ let mut headers = HeaderMap :: new ( ) ;
315+ headers. insert ( CONTENT_TYPE , "application/json" . parse ( ) . unwrap ( ) ) ;
316+ headers. insert ( ACCEPT , "application/json" . parse ( ) . unwrap ( ) ) ;
317+ headers. insert ( AUTHORIZATION , format ! ( "Bearer {}" , token) . parse ( ) . unwrap ( ) ) ;
318+ headers. insert ( USER_AGENT , "Rust-relnotes/0.1.0" . parse ( ) . unwrap ( ) ) ;
319+ headers
320+ }
321+
232322fn map_to_line_items < ' a > (
233323 prefix : & ' static str ,
234324 iter : impl IntoIterator < Item = & ' a json:: Value > ,
0 commit comments