This commit is contained in:
Timothy Andrew 2020-06-03 13:04:11 +05:30
parent 8724149a04
commit 260f508dda
No known key found for this signature in database
GPG Key ID: ABD64509E977B249
8 changed files with 114 additions and 10 deletions

View File

@ -3,6 +3,7 @@ use reqwest::{Client, RequestBuilder};
use std::time::Duration;
pub mod search;
pub mod pull_request;
fn base_request(client: &Client, credentials: &Credentials, url: &str) -> RequestBuilder {
client
@ -11,3 +12,11 @@ fn base_request(client: &Client, credentials: &Credentials, url: &str) -> Reques
.header("Authorization", format!("token {}", credentials.token))
.header("User-Agent", "timothyandrew/gh-stack")
}
fn base_patch_request(client: &Client, credentials: &Credentials, url: &str) -> RequestBuilder {
client
.patch(url)
.timeout(Duration::from_secs(5))
.header("Authorization", format!("token {}", credentials.token))
.header("User-Agent", "timothyandrew/gh-stack")
}

20
src/api/pull_request.rs Normal file
View File

@ -0,0 +1,20 @@
use std::error::Error;
use serde::{Serialize};
use std::rc::Rc;
use crate::{Credentials, api};
use crate::api::search::PullRequest;
#[derive(Serialize, Debug)]
struct UpdateDescriptionRequest<'a> {
body: &'a str
}
pub async fn update_description(description: String, pr: Rc<PullRequest>, c: &Credentials) -> Result<(), Box<dyn Error>> {
let client = reqwest::Client::new();
let body = UpdateDescriptionRequest { body: &description };
let request = api::base_patch_request(&client, &c, pr.url()).json(&body);
request.send().await?;
Ok(())
}

View File

@ -24,6 +24,8 @@ pub struct PullRequest {
head: PullRequestRef,
base: PullRequestRef,
title: String,
url: String,
body: String
}
impl PullRequest {
@ -35,12 +37,20 @@ impl PullRequest {
&self.base.label
}
pub fn url(&self) -> &str {
&self.url
}
pub fn number(&self) -> usize {
self.number
}
pub fn title(&self) -> &str {
&self.title
}
pub fn body(&self) -> &str {
&self.body
}
}
#[derive(Deserialize, Debug)]

View File

@ -4,7 +4,7 @@ use std::rc::Rc;
use crate::api::search::PullRequest;
pub fn build(prs: Vec<Rc<PullRequest>>) -> Graph<Rc<PullRequest>, usize> {
pub fn build(prs: &Vec<Rc<PullRequest>>) -> Graph<Rc<PullRequest>, usize> {
let mut tree = Graph::<Rc<PullRequest>, usize>::new();
let heads = prs.iter().map(|pr| pr.head());
let handles: Vec<_> = prs.iter().map(|pr| tree.add_node(pr.clone())).collect();
@ -18,4 +18,4 @@ pub fn build(prs: Vec<Rc<PullRequest>>) -> Graph<Rc<PullRequest>, usize> {
}
tree
}
}

View File

@ -1,6 +1,7 @@
pub mod api;
pub mod graph;
pub mod markdown;
pub mod persist;
pub struct Credentials {
// Personal access token

View File

@ -4,11 +4,21 @@ use std::error::Error;
use std::process;
use std::rc::Rc;
use gh_stack::api;
use gh_stack::graph;
use gh_stack::markdown;
use gh_stack::{api, persist, graph, markdown};
use gh_stack::Credentials;
use std::io::{self, Write};
pub fn read_cli_input(message: &str) -> String {
print!("{}", message);
io::stdout().flush().unwrap();
let mut buf = String::new();
io::stdin().read_line(&mut buf).unwrap();
buf.trim().to_owned()
}
#[tokio::main]
async fn main() -> Result<(), Box<dyn Error>> {
let env: HashMap<String, String> = env::vars().collect();
@ -28,10 +38,23 @@ async fn main() -> Result<(), Box<dyn Error>> {
let prs = api::search::fetch_pull_requests_matching(&pattern, &credentials).await?;
let prs = prs.into_iter().map(|pr| Rc::new(pr)).collect();
let tree = graph::build(prs);
let table = markdown::build_table(tree);
println!("{}", table);
let tree = graph::build(&prs);
let table = markdown::build_table(tree, pattern);
for pr in prs.iter() {
println!("{}: {}", pr.number(), pr.title());
}
let response = read_cli_input("Going to update these PRs ☝️ (y/n): ");
match &response[..] {
"y" => persist::persist(&prs, &table, &credentials).await?,
_ => std::process::exit(1)
}
persist::persist(&prs, &table, &credentials).await?;
println!("Done!");
Ok(())
/*
# TODO

View File

@ -12,9 +12,9 @@ fn process(row: String) -> String {
regex.replace_all(&row, "").into_owned()
}
pub fn build_table(graph: Graph<Rc<PullRequest>, usize>) -> String {
pub fn build_table(graph: Graph<Rc<PullRequest>, usize>, title: &str) -> String {
let mut out = String::new();
out.push_str("## Stacked PR Chain");
out.push_str(&format!("### Stacked PR Chain: {}\n", title));
out.push_str("| PR | Title | Merges Into |\n");
out.push_str("|:--:|:------|:-------------:|\n");

41
src/persist.rs Normal file
View File

@ -0,0 +1,41 @@
use regex::Regex;
use std::error::Error;
use futures::future::join_all;
use std::rc::Rc;
use crate::api::search::PullRequest;
use crate::Credentials;
use crate::api::pull_request;
const SHIELD_OPEN: &str = "<!---GHSTACKOPEN-->";
const SHIELD_CLOSE: &str = "<!---GHSTACKCLOSE-->";
fn safe_replace(body: &str, table: &str) -> String {
let new = format!("\n{}\n{}\n{}\n", SHIELD_OPEN, table, SHIELD_CLOSE);
if body.contains(SHIELD_OPEN) {
let matcher = format!("(?s){}.*{}", regex::escape(SHIELD_OPEN), regex::escape(SHIELD_CLOSE));
let re = Regex::new(&matcher).unwrap();
re.replace_all(body, &new[..]).into_owned()
} else {
let mut body: String = body.to_owned();
body.push_str(&new);
body
}
}
pub async fn persist(prs: &Vec<Rc<PullRequest>>, table: &str, c: &Credentials) -> Result<(), Box<dyn Error>> {
let futures = prs.iter().map(|pr| {
let description = safe_replace(pr.body(), table);
pull_request::update_description(description, pr.clone(), c)
});
let results = join_all(futures.collect::<Vec<_>>()).await;
for result in results {
result.unwrap();
}
Ok(())
}