Преглед изворни кода

Call issues api and only fallback to search if request is more complicated

Paul Daniel Faria пре 4 година
родитељ
комит
dd547fc86f
3 измењених фајлова са 141 додато и 87 уклоњено
  1. 93 24
      src/github.rs
  2. 31 31
      src/prioritization/config.rs
  3. 17 32
      src/prioritization/mod.rs

+ 93 - 24
src/github.rs

@@ -26,11 +26,7 @@ impl GithubClient {
 
         let mut resp = self.client.execute(req.try_clone().unwrap()).await?;
         if let Some(sleep) = Self::needs_retry(&resp).await {
-            eprintln!(
-                "Need to retry request {}. Sleeping for {}",
-                req_dbg,
-                sleep.as_secs()
-            );
+            drop(resp);
             resp = self.retry(req, sleep, MAX_ATTEMPTS).await?;
         }
 
@@ -99,9 +95,7 @@ impl GithubClient {
         }
 
         async move {
-            eprintln!("Sleeping for {}", sleep.as_secs());
             tokio::time::delay_for(sleep).await;
-            eprintln!("Done sleeping");
 
             // check rate limit
             let rate_resp = self
@@ -114,17 +108,24 @@ impl GithubClient {
                         .unwrap(),
                 )
                 .await?;
-            let search_rate_limit = rate_resp
-                .json::<RateLimitResponse>()
-                .await?
-                .resources
-                .search;
-            eprintln!("search rate limit info: {:?}", search_rate_limit);
+            let rate_limit_response = rate_resp.json::<RateLimitResponse>().await?;
+
+            // Check url for search path because github has different rate limits for the search api
+            let rate_limit = if req
+                .url()
+                .path_segments()
+                .map(|mut segments| matches!(segments.next(), Some("search")))
+                .unwrap_or(false)
+            {
+                rate_limit_response.resources.search
+            } else {
+                rate_limit_response.resources.core
+            };
 
             // If we still don't have any more remaining attempts, try sleeping for the remaining
             // period of time
-            if search_rate_limit.remaining == 0 {
-                let sleep = Self::calc_sleep(search_rate_limit.reset);
+            if rate_limit.remaining == 0 {
+                let sleep = Self::calc_sleep(rate_limit.reset);
                 if sleep > 0 {
                     tokio::time::delay_for(Duration::from_secs(sleep)).await;
                 }
@@ -637,17 +638,85 @@ impl Repository {
     pub async fn get_issues(
         &self,
         client: &GithubClient,
-        filters: &Vec<&str>,
-    ) -> anyhow::Result<IssueSearchResult> {
-        let filters = filters.join("+");
-
-        let url = format!("{}/search/issues?q={}", self.base_url(), filters);
+        filters: &Vec<(&str, &str)>,
+        include_labels: &Vec<&str>,
+        exclude_labels: &Vec<&str>,
+    ) -> anyhow::Result<Vec<Issue>> {
+        let use_issues = exclude_labels.is_empty() || filters.iter().any(|&(key, _)| key == "no");
+        // negating filters can only be handled by the search api
+        let url = if use_issues {
+            self.build_issues_url(filters, include_labels)
+        } else {
+            self.build_search_issues_url(filters, include_labels, exclude_labels)
+        };
 
         let result = client.get(&url);
-        client
-            .json(result)
-            .await
-            .with_context(|| format!("failed to list issues from {}", url))
+        if use_issues {
+            client
+                .json(result)
+                .await
+                .with_context(|| format!("failed to list issues from {}", url))
+        } else {
+            let result = client
+                .json::<IssueSearchResult>(result)
+                .await
+                .with_context(|| format!("failed to list issues from {}", url))?;
+            Ok(result.items)
+        }
+    }
+
+    fn build_issues_url(&self, filters: &Vec<(&str, &str)>, include_labels: &Vec<&str>) -> String {
+        let filters = filters
+            .iter()
+            .map(|(key, val)| format!("{}={}", key, val))
+            .chain(std::iter::once(format!(
+                "labels={}",
+                include_labels.join(",")
+            )))
+            // if no state is defined, assume `state=all` so we don't fall back to the
+            // `state=open` default of github.
+            .chain(
+                if filters.iter().any(|&(key, _)| key == "state") {
+                    None
+                } else {
+                    Some("state=all".to_owned())
+                }
+                .into_iter(),
+            )
+            .chain(std::iter::once("filter=all".to_owned()))
+            .collect::<Vec<_>>()
+            .join("&");
+        format!(
+            "{}/repos/{}/issues?{}",
+            self.base_url(),
+            self.full_name,
+            filters
+        )
+    }
+
+    fn build_search_issues_url(
+        &self,
+        filters: &Vec<(&str, &str)>,
+        include_labels: &Vec<&str>,
+        exclude_labels: &Vec<&str>,
+    ) -> String {
+        let filters = filters
+            .iter()
+            .map(|(key, val)| format!("{}:{}", key, val))
+            .chain(
+                include_labels
+                    .iter()
+                    .map(|label| format!("label:{}", label)),
+            )
+            .chain(
+                exclude_labels
+                    .iter()
+                    .map(|label| format!("-label:{}", label)),
+            )
+            .chain(std::iter::once(format!("repo:{}", self.full_name)))
+            .collect::<Vec<_>>()
+            .join("+");
+        format!("{}/search/issues?q={}", self.base_url(), filters)
     }
 }
 

+ 31 - 31
src/prioritization/config.rs

@@ -19,7 +19,7 @@ pub fn unpri_i_prioritize<'a>() -> Step<'a> {
     queries.push(NamedQuery {
         name: "unpri_i_prioritize.all",
         query: Query {
-            filters: vec!["state:open"],
+            filters: vec![("state", "open")],
             include_labels: vec!["I-prioritize"],
             exclude_labels: vec!["P-critical", "P-high", "P-medium", "P-low"],
         },
@@ -28,7 +28,7 @@ pub fn unpri_i_prioritize<'a>() -> Step<'a> {
     queries.push(NamedQuery {
         name: "unpri_i_prioritize.t_compiler",
         query: Query {
-            filters: vec!["state:open"],
+            filters: vec![("state", "open")],
             include_labels: vec!["I-prioritize", "T-compiler"],
             exclude_labels: vec!["P-critical", "P-high", "P-medium", "P-low"],
         },
@@ -37,7 +37,7 @@ pub fn unpri_i_prioritize<'a>() -> Step<'a> {
     queries.push(NamedQuery {
         name: "unpri_i_prioritize.libs_impl",
         query: Query {
-            filters: vec!["state:open"],
+            filters: vec![("state", "open")],
             include_labels: vec!["I-prioritize", "libs-impl"],
             exclude_labels: vec!["P-critical", "P-high", "P-medium", "P-low"],
         },
@@ -60,7 +60,7 @@ pub fn regressions<'a>() -> Step<'a> {
     queries.push(NamedQuery {
         name: "regressions.stable_to_beta",
         query: Query {
-            filters: vec!["state:open"],
+            filters: vec![("state", "open")],
             include_labels: vec!["regression-from-stable-to-beta"],
             exclude_labels: vec![
                 "P-critical",
@@ -76,7 +76,7 @@ pub fn regressions<'a>() -> Step<'a> {
     queries.push(NamedQuery {
         name: "regressions.stable_to_nightly",
         query: Query {
-            filters: vec!["state:open"],
+            filters: vec![("state", "open")],
             include_labels: vec!["regression-from-stable-to-nightly"],
             exclude_labels: vec![
                 "P-critical",
@@ -92,7 +92,7 @@ pub fn regressions<'a>() -> Step<'a> {
     queries.push(NamedQuery {
         name: "regressions.stable_to_stable",
         query: Query {
-            filters: vec!["state:open", "per_page=100"],
+            filters: vec![("state", "open"), ("per_page","100")],
             include_labels: vec!["regression-from-stable-to-stable"],
             exclude_labels: vec![
                 "P-critical",
@@ -138,7 +138,7 @@ pub fn nominations<'a>() -> Step<'a> {
     queries.push(NamedQuery {
         name: "nominations.i_nominated",
         query: Query {
-            filters: vec!["state:open"],
+            filters: vec![("state", "open")],
             include_labels: vec!["I-nominated"],
             exclude_labels: vec![],
         },
@@ -147,7 +147,7 @@ pub fn nominations<'a>() -> Step<'a> {
     queries.push(NamedQuery {
         name: "nominations.i_nominated_t_compiler",
         query: Query {
-            filters: vec!["state:open"],
+            filters: vec![("state", "open")],
             include_labels: vec!["I-nominated", "T-compiler"],
             exclude_labels: vec![],
         },
@@ -156,7 +156,7 @@ pub fn nominations<'a>() -> Step<'a> {
     queries.push(NamedQuery {
         name: "nominations.i_nominated_libs_impl",
         query: Query {
-            filters: vec!["state:open"],
+            filters: vec![("state", "open")],
             include_labels: vec!["I-nominated", "libs-impl"],
             exclude_labels: vec![],
         },
@@ -177,7 +177,7 @@ pub fn prs_waiting_on_team<'a>() -> Step<'a> {
     queries.push(NamedQuery {
         name: "prs_waiting_on_team.all",
         query: Query {
-            filters: vec!["state:open"],
+            filters: vec![("state", "open")],
             include_labels: vec!["S-waiting-on-team"],
             exclude_labels: vec![],
         },
@@ -186,7 +186,7 @@ pub fn prs_waiting_on_team<'a>() -> Step<'a> {
     queries.push(NamedQuery {
         name: "prs_waiting_on_team.t_compiler",
         query: Query {
-            filters: vec!["state:open"],
+            filters: vec![("state", "open")],
             include_labels: vec!["S-waiting-on-team", "T-compiler"],
             exclude_labels: vec![],
         },
@@ -195,7 +195,7 @@ pub fn prs_waiting_on_team<'a>() -> Step<'a> {
     queries.push(NamedQuery {
         name: "prs_waiting_on_team.libs_impl",
         query: Query {
-            filters: vec!["state:open"],
+            filters: vec![("state", "open")],
             include_labels: vec!["S-waiting-on-team", "libs-impl"],
             exclude_labels: vec![],
         },
@@ -217,7 +217,7 @@ pub fn agenda<'a>() -> Step<'a> {
     queries.push(NamedQuery {
         name: "mcp.seconded",
         query: Query {
-            filters: vec!["state:open"],
+            filters: vec![("state", "open")],
             include_labels: vec!["major-change", "final-comment-period"],
             exclude_labels: vec![],
         },
@@ -226,7 +226,7 @@ pub fn agenda<'a>() -> Step<'a> {
     queries.push(NamedQuery {
         name: "mcp.new_not_seconded",
         query: Query {
-            filters: vec!["state:open"],
+            filters: vec![("state", "open")],
             include_labels: vec!["major-change", "to-announce"],
             exclude_labels: vec!["final-comment-period"],
         },
@@ -235,7 +235,7 @@ pub fn agenda<'a>() -> Step<'a> {
     queries.push(NamedQuery {
         name: "mcp.old_not_seconded",
         query: Query {
-            filters: vec!["state:open"],
+            filters: vec![("state", "open")],
             include_labels: vec!["major-change"],
             exclude_labels: vec!["to-announce", "final-comment-period"],
         },
@@ -305,7 +305,7 @@ pub fn agenda<'a>() -> Step<'a> {
     queries.push(NamedQuery {
         name: "prs_waiting_on_team.t_compiler",
         query: Query {
-            filters: vec!["state:open"],
+            filters: vec![("state", "open")],
             include_labels: vec!["S-waiting-on-team", "T-compiler"],
             exclude_labels: vec![],
         },
@@ -314,7 +314,7 @@ pub fn agenda<'a>() -> Step<'a> {
     queries.push(NamedQuery {
         name: "prs_waiting_on_team.libs_impl",
         query: Query {
-            filters: vec!["state:open"],
+            filters: vec![("state", "open")],
             include_labels: vec!["S-waiting-on-team", "libs-impl"],
             exclude_labels: vec![],
         },
@@ -323,7 +323,7 @@ pub fn agenda<'a>() -> Step<'a> {
     queries.push(NamedQuery {
         name: "issues_of_note.p_critical",
         query: Query {
-            filters: vec!["state:open"],
+            filters: vec![("state", "open")],
             include_labels: vec!["T-compiler", "P-critical"],
             exclude_labels: vec![],
         },
@@ -332,7 +332,7 @@ pub fn agenda<'a>() -> Step<'a> {
     queries.push(NamedQuery {
         name: "issues_of_note.unassigned_p_critical",
         query: Query {
-            filters: vec!["state:open", "no=assignee"],
+            filters: vec![("state", "open"), ("no", "assignee")],
             include_labels: vec!["T-compiler", "P-critical"],
             exclude_labels: vec![],
         },
@@ -341,7 +341,7 @@ pub fn agenda<'a>() -> Step<'a> {
     queries.push(NamedQuery {
         name: "issues_of_note.p_high",
         query: Query {
-            filters: vec!["state:open"],
+            filters: vec![("state", "open")],
             include_labels: vec!["T-compiler", "P-high"],
             exclude_labels: vec![],
         },
@@ -350,7 +350,7 @@ pub fn agenda<'a>() -> Step<'a> {
     queries.push(NamedQuery {
         name: "issues_of_note.unassigned_p_high",
         query: Query {
-            filters: vec!["state:open", "no=assignee"],
+            filters: vec![("state", "open"), ("no", "assignee")],
             include_labels: vec!["T-compiler", "P-high"],
             exclude_labels: vec![],
         },
@@ -363,7 +363,7 @@ pub fn agenda<'a>() -> Step<'a> {
     queries.push(NamedQuery {
         name: "issues_of_note.regression_from_stable_to_beta",
         query: Query {
-            filters: vec!["state:open"],
+            filters: vec![("state", "open")],
             include_labels: vec!["regression-from-stable-to-beta"],
             exclude_labels: vec![],
         },
@@ -372,7 +372,7 @@ pub fn agenda<'a>() -> Step<'a> {
     queries.push(NamedQuery {
         name: "issues_of_note.regression_from_stable_to_nightly",
         query: Query {
-            filters: vec!["state:open"],
+            filters: vec![("state", "open")],
             include_labels: vec!["regression-from-stable-to-nightly"],
             exclude_labels: vec![],
         },
@@ -381,7 +381,7 @@ pub fn agenda<'a>() -> Step<'a> {
     queries.push(NamedQuery {
         name: "issues_of_note.regression_from_stable_to_stable",
         query: Query {
-            filters: vec!["state:open"],
+            filters: vec![("state", "open")],
             include_labels: vec!["regression-from-stable-to-stable"],
             exclude_labels: vec![],
         },
@@ -390,7 +390,7 @@ pub fn agenda<'a>() -> Step<'a> {
     queries.push(NamedQuery {
         name: "p_critical.t_compiler",
         query: Query {
-            filters: vec!["state:open"],
+            filters: vec![("state", "open")],
             include_labels: vec!["T-compiler", "P-critical"],
             exclude_labels: vec![],
         },
@@ -399,7 +399,7 @@ pub fn agenda<'a>() -> Step<'a> {
     queries.push(NamedQuery {
         name: "p_critical.libs_impl",
         query: Query {
-            filters: vec!["state:open"],
+            filters: vec![("state", "open")],
             include_labels: vec!["libs-impl", "P-critical"],
             exclude_labels: vec![],
         },
@@ -408,7 +408,7 @@ pub fn agenda<'a>() -> Step<'a> {
     queries.push(NamedQuery {
         name: "p_critical.t_rustdoc",
         query: Query {
-            filters: vec!["state:open"],
+            filters: vec![("state", "open")],
             include_labels: vec!["T-rustdoc", "P-critical"],
             exclude_labels: vec![],
         },
@@ -417,7 +417,7 @@ pub fn agenda<'a>() -> Step<'a> {
     queries.push(NamedQuery {
         name: "beta_regressions.unassigned_p_high",
         query: Query {
-            filters: vec!["state:open", "no=assignee"],
+            filters: vec![("state", "open"), ("no", "assignee")],
             include_labels: vec!["regression-from-stable-to-beta", "P-high"],
             exclude_labels: vec!["T-infra", "T-release"],
         },
@@ -426,7 +426,7 @@ pub fn agenda<'a>() -> Step<'a> {
     queries.push(NamedQuery {
         name: "nightly_regressions.unassigned_p_high",
         query: Query {
-            filters: vec!["state:open", "no=assignee"],
+            filters: vec![("state", "open"), ("no", "assignee")],
             include_labels: vec!["regression-from-stable-to-nightly", "P-high"],
             exclude_labels: vec!["T-infra", "T-release"],
         },
@@ -435,7 +435,7 @@ pub fn agenda<'a>() -> Step<'a> {
     queries.push(NamedQuery {
         name: "i_nominated.t_compiler",
         query: Query {
-            filters: vec!["state:open"],
+            filters: vec![("state", "open")],
             include_labels: vec!["I-nominated", "T-compiler"],
             exclude_labels: vec![],
         },
@@ -444,7 +444,7 @@ pub fn agenda<'a>() -> Step<'a> {
     queries.push(NamedQuery {
         name: "i_nominated.libs_impl",
         query: Query {
-            filters: vec!["state:open"],
+            filters: vec![("state", "open")],
             include_labels: vec!["I-nominated", "libs-impl"],
             exclude_labels: vec![],
         },

+ 17 - 32
src/prioritization/mod.rs

@@ -32,7 +32,7 @@ pub struct NamedQuery<'a> {
 }
 
 pub struct Query<'a> {
-    pub filters: Vec<&'a str>,
+    pub filters: Vec<(&'a str, &'a str)>,
     pub include_labels: Vec<&'a str>,
     pub exclude_labels: Vec<&'a str>,
 }
@@ -64,38 +64,23 @@ impl<'a> Action for Step<'a> {
                 queries
                     .iter()
                     .map(|NamedQuery { name, query }| {
-                        let filters = query
-                            .filters
-                            .iter()
-                            .map(|s| s.to_string())
-                            .chain(
-                                query
-                                    .include_labels
-                                    .iter()
-                                    .map(|label| format!("label:{}", label)),
-                            )
-                            .chain(
-                                query
-                                    .exclude_labels
-                                    .iter()
-                                    .map(|label| format!("-label:{}", label)),
-                            )
-                            .chain(std::iter::once(format!("repo:{}", repository.full_name)))
-                            .collect::<Vec<_>>();
-                        let issues_search_result = block_on(
-                            repository
-                                .get_issues(&gh, &filters.iter().map(|s| s.as_ref()).collect()),
-                        );
-
-                        if let Err(err) = issues_search_result {
-                            eprintln!("ERROR: {}", err);
-                            err.chain()
-                                .skip(1)
-                                .for_each(|cause| eprintln!("because: {}", cause));
-                            std::process::exit(1);
+                        let issues_search_result = block_on(repository.get_issues(
+                            &gh,
+                            &query.filters,
+                            &query.include_labels,
+                            &query.exclude_labels,
+                        ));
+
+                        match issues_search_result {
+                            Ok(issues) => (*name, issues),
+                            Err(err) => {
+                                eprintln!("ERROR: {}", err);
+                                err.chain()
+                                    .skip(1)
+                                    .for_each(|cause| eprintln!("because: {}", cause));
+                                std::process::exit(1);
+                            }
                         }
-
-                        (*name, issues_search_result.unwrap().items)
                     })
                     .collect::<Vec<_>>()
             })