فهرست منبع

Merge pull request #740 from nikomatsakis/add-lang-agenda-structure

Add lang agenda structure
Mark Rousskov 4 سال پیش
والد
کامیت
77ea0f4395
9فایلهای تغییر یافته به همراه181 افزوده شده و 13 حذف شده
  1. 3 0
      README.md
  2. 1 5
      src/actions.rs
  3. 80 2
      src/agenda.rs
  4. 1 1
      src/bin/lang-agenda.rs
  5. 11 0
      src/bin/prioritization-agenda.rs
  6. 60 0
      src/github.rs
  7. 2 5
      src/main.rs
  8. 23 0
      templates/lang_agenda.tt
  9. 0 0
      templates/prioritization_agenda.tt

+ 3 - 0
README.md

@@ -11,6 +11,9 @@ if you find something helpful!
 
 The `GITHUB_WEBHOOK_SECRET` and `GITHUB_API_TOKEN` environment variables need to be set.
 
+If `GITHUB_API_TOKEN` is not set, the token can also be stored in `~/.gitconfig` in the
+`github.oauth-token` setting.
+
 ## License
 
 Triagebot is distributed under the terms of both the MIT license and the

+ 1 - 5
src/actions.rs

@@ -1,7 +1,6 @@
 use async_trait::async_trait;
 
 use reqwest::Client;
-use std::env;
 use tera::{Context, Tera};
 
 use crate::github::{self, GithubClient, Repository};
@@ -51,10 +50,7 @@ lazy_static! {
 #[async_trait]
 impl<'a> Action for Step<'a> {
     async fn call(&self) -> String {
-        let gh = GithubClient::new(
-            Client::new(),
-            env::var("GITHUB_API_TOKEN").expect("Missing GITHUB_API_TOKEN"),
-        );
+        let gh = GithubClient::new_with_default_token(Client::new());
 
         let mut context = Context::new();
 

+ 80 - 2
src/agenda.rs

@@ -1,7 +1,7 @@
 use crate::actions::{Action, Query, QueryMap, Step};
 use crate::github;
 
-pub fn prepare_agenda<'a>() -> Box<dyn Action> {
+pub fn prioritization<'a>() -> Box<dyn Action> {
     let mut actions = Vec::new();
 
     let mut queries = Vec::new();
@@ -445,7 +445,85 @@ pub fn prepare_agenda<'a>() -> Box<dyn Action> {
     });
 
     Box::new(Step {
-        name: "agenda",
+        name: "prioritization_agenda",
+        actions,
+    })
+}
+
+pub fn lang<'a>() -> Box<dyn Action> {
+    let mut actions = Vec::new();
+
+    let mut queries = Vec::new();
+
+    // https://github.com/rust-lang/rfcs/pulls?q=is%3Aopen+is%3Apr+label%3AT-lang
+    queries.push(QueryMap {
+        name: "newly_created_rfcs",
+        query: github::Query {
+            kind: github::QueryKind::List,
+            filters: vec![("state", "open"), ("is", "pr")],
+            include_labels: vec!["T-lang"],
+            exclude_labels: vec![],
+        },
+    });
+
+    //https://github.com/rust-lang/rfcs/pulls?q=is%3Aopen+is%3Apr+label%3AI-nominated+label%3AT-lang
+    queries.push(QueryMap {
+        name: "nominated_rfcs",
+        query: github::Query {
+            kind: github::QueryKind::List,
+            filters: vec![("state", "open"), ("is", "pr")],
+            include_labels: vec!["T-lang", "I-nominated"],
+            exclude_labels: vec![],
+        },
+    });
+
+    actions.push(Query {
+        repo: "rust-lang/rfcs",
+        queries,
+    });
+
+    let mut queries = Vec::new();
+
+    // https://github.com/rust-lang/rust/issues?q=is%3Aissue+is%3Aopen+label%3AP-high+label%3AT-lang
+    queries.push(QueryMap {
+        name: "p_high_issues",
+        query: github::Query {
+            kind: github::QueryKind::List,
+            filters: vec![("state", "open")],
+            include_labels: vec!["T-lang", "P-high"],
+            exclude_labels: vec![],
+        },
+    });
+
+    // https://github.com/rust-lang/rust/pulls?q=is%3Aopen+is%3Apr+label%3AI-nominated+label%3AT-lang
+    queries.push(QueryMap {
+        name: "nominated_prs",
+        query: github::Query {
+            kind: github::QueryKind::List,
+            filters: vec![("state", "open"), ("is", "pr")],
+            include_labels: vec!["T-lang", "I-nominated"],
+            exclude_labels: vec![],
+        },
+    });
+
+    // https://github.com/rust-lang/rust/issues?utf8=%E2%9C%93&q=is%3Aopen+is%3Aissue+label%3AI-nominated+label%3AT-lang+
+    queries.push(QueryMap {
+        name: "nominated_issues",
+        query: github::Query {
+            kind: github::QueryKind::List,
+            filters: vec![("state", "open")],
+            include_labels: vec!["T-lang", "I-nominated"],
+            exclude_labels: vec![],
+        },
+    });
+
+    actions.push(Query {
+        repo: "rust-lang/rust",
+        queries,
+    });
+
+    Box::new(Step {
+        name: "lang_agenda",
         actions,
     })
 }

+ 1 - 1
src/bin/prioritization.rs → src/bin/lang-agenda.rs

@@ -5,7 +5,7 @@ async fn main() {
     dotenv::dotenv().ok();
     logger::init();
 
-    let agenda = agenda::prepare_agenda();
+    let agenda = agenda::lang();
 
     print!("{}", agenda.call().await);
 }

+ 11 - 0
src/bin/prioritization-agenda.rs

@@ -0,0 +1,11 @@
+use triagebot::{agenda, logger};
+
+#[tokio::main]
+async fn main() {
+    dotenv::dotenv().ok();
+    logger::init();
+
+    let agenda = agenda::prioritization();
+
+    print!("{}", agenda.call().await);
+}

+ 60 - 0
src/github.rs

@@ -669,9 +669,14 @@ impl Repository {
         } = query;
 
         let use_issues = exclude_labels.is_empty() && filters.iter().all(|&(key, _)| key != "no");
+        let is_pr = filters
+            .iter()
+            .any(|&(key, value)| key == "is" && value == "pr");
         // negating filters can only be handled by the search api
         let url = if use_issues {
             self.build_issues_url(filters, include_labels)
+        } else if is_pr {
+            self.build_pulls_url(filters, include_labels)
         } else {
             self.build_search_issues_url(filters, include_labels, exclude_labels)
         };
@@ -721,6 +726,28 @@ impl Repository {
         )
     }
 
+    fn build_pulls_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(",")
+            )))
+            .chain(std::iter::once("filter=all".to_owned()))
+            .chain(std::iter::once(format!("sort=created")))
+            .chain(std::iter::once(format!("direction=asc")))
+            .chain(std::iter::once(format!("per_page=100")))
+            .collect::<Vec<_>>()
+            .join("&");
+        format!(
+            "{}/repos/{}/pulls?{}",
+            Repository::GITHUB_API_URL,
+            self.full_name,
+            filters
+        )
+    }
+
     fn build_search_issues_url(
         &self,
         filters: &Vec<(&str, &str)>,
@@ -836,6 +863,35 @@ impl RequestSend for RequestBuilder {
     }
 }
 
+/// Finds the token in the user's environment, panicking if no suitable token
+/// can be found.
+pub fn default_token_from_env() -> String {
+    match std::env::var("GITHUB_API_TOKEN") {
+        Ok(v) => return v,
+        Err(_) => (),
+    }
+
+    match get_token_from_git_config() {
+        Ok(v) => return v,
+        Err(_) => (),
+    }
+
+    panic!("could not find token in GITHUB_API_TOKEN or .gitconfig/github.oath-token")
+}
+
+fn get_token_from_git_config() -> anyhow::Result<String> {
+    let output = std::process::Command::new("git")
+        .arg("config")
+        .arg("--get")
+        .arg("github.oauth-token")
+        .output()?;
+    if !output.status.success() {
+        anyhow::bail!("error received executing `git`: {:?}", output.status);
+    }
+    let git_token = String::from_utf8(output.stdout)?.trim().to_string();
+    Ok(git_token)
+}
+
 #[derive(Clone)]
 pub struct GithubClient {
     token: String,
@@ -847,6 +903,10 @@ impl GithubClient {
         GithubClient { client, token }
     }
 
+    pub fn new_with_default_token(client: Client) -> Self {
+        Self::new(client, default_token_from_env())
+    }
+
     pub fn raw(&self) -> &Client {
         &self.client
     }

+ 2 - 5
src/main.rs

@@ -179,12 +179,9 @@ async fn run_server(addr: SocketAddr) -> anyhow::Result<()> {
         .context("database migrations")?;
 
     let client = Client::new();
-    let gh = github::GithubClient::new(
-        client.clone(),
-        env::var("GITHUB_API_TOKEN").expect("Missing GITHUB_API_TOKEN"),
-    );
+    let gh = github::GithubClient::new_with_default_token(client.clone());
     let oc = octocrab::OctocrabBuilder::new()
-        .personal_token(env::var("GITHUB_API_TOKEN").expect("Missing GITHUB_API_TOKEN"))
+        .personal_token(github::default_token_from_env())
         .build()
         .expect("Failed to build octograb.");
     let ctx = Arc::new(Context {

+ 23 - 0
templates/lang_agenda.tt

@@ -0,0 +1,23 @@
+{% import "_issues.tt" as issues %}
+
+# T-lang meeting agenda
+
+## Newly created RFCs
+
+{{-issues::render(issues=newly_created_rfcs, indent="  ", empty="No new RFCs this time.")}}
+
+## Nominated RFCs
+
+{{-issues::render(issues=nominated_rfcs, indent="  ", empty="No nominated RFCs this time.")}}
+
+## P-high issues
+
+{{-issues::render(issues=p_high_issues, indent="  ", empty="No P-high issues this time.")}}
+
+## Nominated PRs
+
+{{-issues::render(issues=nominated_prs, indent="  ", empty="No nominated PRs this time.")}}
+
+## Nominated issues
+
+{{-issues::render(issues=nominated_issues, indent="  ", empty="No nominated issues this time.")}}

+ 0 - 0
templates/agenda.tt → templates/prioritization_agenda.tt