build graph
This commit is contained in:
parent
4345e3573f
commit
cccab03886
90
Cargo.lock
generated
90
Cargo.lock
generated
@ -116,6 +116,21 @@ version = "0.3.3"
|
|||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "3dcaa9ae7725d12cdb85b3ad99a434db70b468c09ded17e012d86b5c1010f7a7"
|
checksum = "3dcaa9ae7725d12cdb85b3ad99a434db70b468c09ded17e012d86b5c1010f7a7"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "futures"
|
||||||
|
version = "0.3.5"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "1e05b85ec287aac0dc34db7d4a569323df697f9c55b99b15d6b4ef8cde49f613"
|
||||||
|
dependencies = [
|
||||||
|
"futures-channel",
|
||||||
|
"futures-core",
|
||||||
|
"futures-executor",
|
||||||
|
"futures-io",
|
||||||
|
"futures-sink",
|
||||||
|
"futures-task",
|
||||||
|
"futures-util",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "futures-channel"
|
name = "futures-channel"
|
||||||
version = "0.3.5"
|
version = "0.3.5"
|
||||||
@ -123,6 +138,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
|
|||||||
checksum = "f366ad74c28cca6ba456d95e6422883cfb4b252a83bed929c83abfdbbf2967d5"
|
checksum = "f366ad74c28cca6ba456d95e6422883cfb4b252a83bed929c83abfdbbf2967d5"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"futures-core",
|
"futures-core",
|
||||||
|
"futures-sink",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
@ -131,6 +147,35 @@ version = "0.3.5"
|
|||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "59f5fff90fd5d971f936ad674802482ba441b6f09ba5e15fd8b39145582ca399"
|
checksum = "59f5fff90fd5d971f936ad674802482ba441b6f09ba5e15fd8b39145582ca399"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "futures-executor"
|
||||||
|
version = "0.3.5"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "10d6bb888be1153d3abeb9006b11b02cf5e9b209fda28693c31ae1e4e012e314"
|
||||||
|
dependencies = [
|
||||||
|
"futures-core",
|
||||||
|
"futures-task",
|
||||||
|
"futures-util",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "futures-io"
|
||||||
|
version = "0.3.5"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "de27142b013a8e869c14957e6d2edeef89e97c289e69d042ee3a49acd8b51789"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "futures-macro"
|
||||||
|
version = "0.3.5"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "d0b5a30a4328ab5473878237c447333c093297bded83a4983d10f4deea240d39"
|
||||||
|
dependencies = [
|
||||||
|
"proc-macro-hack",
|
||||||
|
"proc-macro2",
|
||||||
|
"quote",
|
||||||
|
"syn",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "futures-sink"
|
name = "futures-sink"
|
||||||
version = "0.3.5"
|
version = "0.3.5"
|
||||||
@ -142,6 +187,9 @@ name = "futures-task"
|
|||||||
version = "0.3.5"
|
version = "0.3.5"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "bdb66b5f09e22019b1ab0830f7785bcea8e7a42148683f99214f73f8ec21a626"
|
checksum = "bdb66b5f09e22019b1ab0830f7785bcea8e7a42148683f99214f73f8ec21a626"
|
||||||
|
dependencies = [
|
||||||
|
"once_cell",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "futures-util"
|
name = "futures-util"
|
||||||
@ -149,10 +197,18 @@ version = "0.3.5"
|
|||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "8764574ff08b701a084482c3c7031349104b07ac897393010494beaa18ce32c6"
|
checksum = "8764574ff08b701a084482c3c7031349104b07ac897393010494beaa18ce32c6"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
|
"futures-channel",
|
||||||
"futures-core",
|
"futures-core",
|
||||||
|
"futures-io",
|
||||||
|
"futures-macro",
|
||||||
|
"futures-sink",
|
||||||
"futures-task",
|
"futures-task",
|
||||||
|
"memchr",
|
||||||
"pin-project",
|
"pin-project",
|
||||||
"pin-utils",
|
"pin-utils",
|
||||||
|
"proc-macro-hack",
|
||||||
|
"proc-macro-nested",
|
||||||
|
"slab",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
@ -170,7 +226,9 @@ dependencies = [
|
|||||||
name = "gh-stack"
|
name = "gh-stack"
|
||||||
version = "0.1.0"
|
version = "0.1.0"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
|
"futures",
|
||||||
"reqwest",
|
"reqwest",
|
||||||
|
"serde",
|
||||||
"tokio",
|
"tokio",
|
||||||
]
|
]
|
||||||
|
|
||||||
@ -472,6 +530,12 @@ dependencies = [
|
|||||||
"libc",
|
"libc",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "once_cell"
|
||||||
|
version = "1.4.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "0b631f7e854af39a1739f401cf34a8a013dfe09eac4fa4dba91e9768bd28168d"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "openssl"
|
name = "openssl"
|
||||||
version = "0.10.29"
|
version = "0.10.29"
|
||||||
@ -555,6 +619,18 @@ version = "0.2.8"
|
|||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "237a5ed80e274dbc66f86bd59c1e25edc039660be53194b5fe0a482e0f2612ea"
|
checksum = "237a5ed80e274dbc66f86bd59c1e25edc039660be53194b5fe0a482e0f2612ea"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "proc-macro-hack"
|
||||||
|
version = "0.5.16"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "7e0456befd48169b9f13ef0f0ad46d492cf9d2dbb918bcf38e01eed4ce3ec5e4"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "proc-macro-nested"
|
||||||
|
version = "0.1.4"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "8e946095f9d3ed29ec38de908c22f95d9ac008e424c7bcae54c75a79c527c694"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "proc-macro2"
|
name = "proc-macro2"
|
||||||
version = "1.0.18"
|
version = "1.0.18"
|
||||||
@ -708,6 +784,20 @@ name = "serde"
|
|||||||
version = "1.0.111"
|
version = "1.0.111"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "c9124df5b40cbd380080b2cc6ab894c040a3070d995f5c9dc77e18c34a8ae37d"
|
checksum = "c9124df5b40cbd380080b2cc6ab894c040a3070d995f5c9dc77e18c34a8ae37d"
|
||||||
|
dependencies = [
|
||||||
|
"serde_derive",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "serde_derive"
|
||||||
|
version = "1.0.111"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "3f2c3ac8e6ca1e9c80b8be1023940162bf81ae3cffbb1809474152f2ce1eb250"
|
||||||
|
dependencies = [
|
||||||
|
"proc-macro2",
|
||||||
|
"quote",
|
||||||
|
"syn",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "serde_json"
|
name = "serde_json"
|
||||||
|
@ -9,3 +9,5 @@ edition = "2018"
|
|||||||
[dependencies]
|
[dependencies]
|
||||||
reqwest = { version = "0.10.6", features = ["json"] }
|
reqwest = { version = "0.10.6", features = ["json"] }
|
||||||
tokio = { version = "0.2", features = ["full"] }
|
tokio = { version = "0.2", features = ["full"] }
|
||||||
|
serde = { version = "1.0", features = ["derive"] }
|
||||||
|
futures = "0.3.5"
|
||||||
|
@ -1 +1,13 @@
|
|||||||
|
use crate::Credentials;
|
||||||
|
use reqwest::{Client, RequestBuilder};
|
||||||
|
use std::time::Duration;
|
||||||
|
|
||||||
pub mod search;
|
pub mod search;
|
||||||
|
|
||||||
|
fn base_request(client: &Client, credentials: &Credentials, url: &str) -> RequestBuilder {
|
||||||
|
client
|
||||||
|
.get(url)
|
||||||
|
.timeout(Duration::from_secs(5))
|
||||||
|
.header("Authorization", format!("token {}", credentials.token))
|
||||||
|
.header("User-Agent", "timothyandrew/gh-stack")
|
||||||
|
}
|
||||||
|
@ -1,18 +1,75 @@
|
|||||||
|
use futures::future::join_all;
|
||||||
|
use serde::Deserialize;
|
||||||
use std::error::Error;
|
use std::error::Error;
|
||||||
|
|
||||||
use crate::Credentials;
|
use crate::{api, Credentials};
|
||||||
|
|
||||||
|
#[derive(Deserialize, Debug)]
|
||||||
|
pub struct SearchItem {
|
||||||
|
url: String,
|
||||||
|
title: String,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Deserialize, Debug)]
|
||||||
|
pub struct PullRequestRef {
|
||||||
|
label: String,
|
||||||
|
r#ref: String,
|
||||||
|
sha: String,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Deserialize, Debug)]
|
||||||
|
pub struct PullRequest {
|
||||||
|
id: usize,
|
||||||
|
head: PullRequestRef,
|
||||||
|
base: PullRequestRef,
|
||||||
|
title: String,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl PullRequest {
|
||||||
|
pub fn head(&self) -> &str {
|
||||||
|
&self.head.label
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn base(&self) -> &str {
|
||||||
|
&self.base.label
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Deserialize, Debug)]
|
||||||
|
struct SearchResponse {
|
||||||
|
items: Vec<SearchItem>,
|
||||||
|
}
|
||||||
|
|
||||||
pub async fn fetch_pull_requests_matching(
|
pub async fn fetch_pull_requests_matching(
|
||||||
pattern: &str,
|
pattern: &str,
|
||||||
credentials: &Credentials,
|
credentials: &Credentials,
|
||||||
) -> Result<(), Box<dyn Error>> {
|
) -> Result<Vec<PullRequest>, Box<dyn Error>> {
|
||||||
let client = reqwest::Client::new();
|
let client = reqwest::Client::new();
|
||||||
let request = client
|
|
||||||
.get("https://api.github.com/search/issues")
|
let request = api::base_request(
|
||||||
.query(&[("q", format!("{} in:title", pattern))])
|
&client,
|
||||||
.header("Authorization", format!("token {}", credentials.token))
|
&credentials,
|
||||||
.header("User-Agent", "timothyandrew/gh-stack");
|
"https://api.github.com/search/issues",
|
||||||
let response = request.send().await?.text().await?;
|
)
|
||||||
println!("{}", response);
|
.query(&[("q", format!("{} in:title", pattern))]);
|
||||||
Ok(())
|
|
||||||
|
let items = request.send().await?.json::<SearchResponse>().await?.items;
|
||||||
|
|
||||||
|
let item_futures = items.into_iter().map(|item| {
|
||||||
|
api::base_request(&client, &credentials, &item.url.replace("issues", "pulls")).send()
|
||||||
|
});
|
||||||
|
|
||||||
|
// The `unwrap`s are required here because both `reqwest::send` and `reqwest::json` return a `Result` which has
|
||||||
|
// to be unwrapped after the future has been `await`ed on.
|
||||||
|
let items = join_all(item_futures)
|
||||||
|
.await
|
||||||
|
.into_iter()
|
||||||
|
.map(|item| item.unwrap());
|
||||||
|
let responses: Vec<_> = join_all(items.map(|item| item.json::<PullRequest>()))
|
||||||
|
.await
|
||||||
|
.into_iter()
|
||||||
|
.map(|item| item.unwrap())
|
||||||
|
.collect();
|
||||||
|
|
||||||
|
Ok(responses)
|
||||||
}
|
}
|
34
src/graph.rs
Normal file
34
src/graph.rs
Normal file
@ -0,0 +1,34 @@
|
|||||||
|
use std::collections::{HashMap, HashSet};
|
||||||
|
|
||||||
|
use crate::api::search::PullRequest;
|
||||||
|
|
||||||
|
pub fn build(prs: &[PullRequest]) {
|
||||||
|
let mut heads = HashSet::new();
|
||||||
|
let mut prs_by_base = HashMap::new();
|
||||||
|
|
||||||
|
for pr in prs.iter() {
|
||||||
|
heads.insert(pr.head());
|
||||||
|
let entry = prs_by_base.entry(pr.base()).or_insert(Vec::new());
|
||||||
|
entry.push(pr);
|
||||||
|
}
|
||||||
|
|
||||||
|
let roots: Vec<&PullRequest> = prs.iter().filter(|pr| !heads.contains(pr.base())).collect();
|
||||||
|
let results = resolve(&roots, &prs_by_base);
|
||||||
|
}
|
||||||
|
|
||||||
|
fn resolve<'a>(
|
||||||
|
roots: &Vec<&'a PullRequest>,
|
||||||
|
prs_by_base: &'a HashMap<&str, Vec<&PullRequest>>
|
||||||
|
) -> Vec<&'a PullRequest> {
|
||||||
|
let mut results = Vec::new();
|
||||||
|
|
||||||
|
for &root in roots.iter() {
|
||||||
|
results.push(root);
|
||||||
|
if let Some(children) = prs_by_base.get(root.head()) {
|
||||||
|
let mut children = resolve(children, prs_by_base);
|
||||||
|
results.append(&mut children);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
results
|
||||||
|
}
|
@ -1,5 +1,5 @@
|
|||||||
|
|
||||||
pub mod api;
|
pub mod api;
|
||||||
|
pub mod graph;
|
||||||
|
|
||||||
pub struct Credentials {
|
pub struct Credentials {
|
||||||
// Personal access token
|
// Personal access token
|
||||||
|
13
src/main.rs
13
src/main.rs
@ -1,9 +1,10 @@
|
|||||||
use std::error::Error;
|
|
||||||
use std::env;
|
|
||||||
use std::collections::HashMap;
|
use std::collections::HashMap;
|
||||||
|
use std::env;
|
||||||
|
use std::error::Error;
|
||||||
use std::process;
|
use std::process;
|
||||||
|
|
||||||
use gh_stack::api;
|
use gh_stack::api;
|
||||||
|
use gh_stack::graph;
|
||||||
use gh_stack::Credentials;
|
use gh_stack::Credentials;
|
||||||
|
|
||||||
#[tokio::main]
|
#[tokio::main]
|
||||||
@ -17,10 +18,14 @@ async fn main() -> Result<(), Box<dyn Error>> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
let pattern = args.last().unwrap();
|
let pattern = args.last().unwrap();
|
||||||
let token = env.get("GHSTACK_OAUTH_TOKEN").expect("You didn't pass `GHSTACK_OAUTH_TOKEN`");
|
let token = env
|
||||||
|
.get("GHSTACK_OAUTH_TOKEN")
|
||||||
|
.expect("You didn't pass `GHSTACK_OAUTH_TOKEN`");
|
||||||
|
|
||||||
let credentials = Credentials::new(token);
|
let credentials = Credentials::new(token);
|
||||||
api::search::fetch_pull_requests_matching(&pattern, &credentials).await?;
|
|
||||||
|
let prs = api::search::fetch_pull_requests_matching(&pattern, &credentials).await?;
|
||||||
|
graph::build(&prs);
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
/*
|
/*
|
||||||
|
Loading…
Reference in New Issue
Block a user