cargo fix + fmt + clippy
This commit is contained in:
parent
251f476577
commit
de80a197ed
|
@ -22,7 +22,7 @@ pub enum PullRequestStatus {
|
||||||
#[serde(rename = "open")]
|
#[serde(rename = "open")]
|
||||||
Open,
|
Open,
|
||||||
#[serde(rename = "closed")]
|
#[serde(rename = "closed")]
|
||||||
Closed
|
Closed,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Deserialize, Debug, Clone)]
|
#[derive(Deserialize, Debug, Clone)]
|
||||||
|
@ -34,7 +34,7 @@ pub struct PullRequest {
|
||||||
title: String,
|
title: String,
|
||||||
url: String,
|
url: String,
|
||||||
body: String,
|
body: String,
|
||||||
state: PullRequestStatus
|
state: PullRequestStatus,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl PullRequest {
|
impl PullRequest {
|
||||||
|
@ -57,7 +57,7 @@ impl PullRequest {
|
||||||
pub fn title(&self) -> String {
|
pub fn title(&self) -> String {
|
||||||
match &self.state {
|
match &self.state {
|
||||||
PullRequestStatus::Open => self.title.to_owned(),
|
PullRequestStatus::Open => self.title.to_owned(),
|
||||||
PullRequestStatus::Closed => format!("~~{}~~", &self.title.trim())
|
PullRequestStatus::Closed => format!("~~{}~~", &self.title.trim()),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
40
src/git.rs
40
src/git.rs
|
@ -1,53 +1,53 @@
|
||||||
use regex::Regex;
|
|
||||||
use crate::graph::FlatDep;
|
|
||||||
use crate::api::search::PullRequestStatus;
|
use crate::api::search::PullRequestStatus;
|
||||||
|
use crate::graph::FlatDep;
|
||||||
|
|
||||||
fn process_ref(git_ref: &str) -> String {
|
fn process_ref(git_ref: &str) -> String {
|
||||||
let re = Regex::new("heap:").unwrap();
|
git_ref.replace("heap:", "")
|
||||||
re.replace_all(git_ref, "").into_owned()
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// For all open pull requests in the graph, generate a series of commands
|
/// For all open pull requests in the graph, generate a series of commands
|
||||||
/// (force-pushes) that will rebase the entire stack. The "PREBASE" variable
|
/// (force-pushes) that will rebase the entire stack. The "PREBASE" variable
|
||||||
/// is a base for the first branch in the stack (essentially a "stop cherry-picking
|
/// is a base for the first branch in the stack (essentially a "stop cherry-picking
|
||||||
/// here" marker), and is required because of our squash-merge workflow.
|
/// here" marker), and is required because of our squash-merge workflow.
|
||||||
/// TODO: Move this directly into Rust.
|
/// TODO: Move this directly into Rust.
|
||||||
pub fn generate_rebase_script(deps: FlatDep) -> String {
|
pub fn generate_rebase_script(deps: FlatDep) -> String {
|
||||||
|
let deps = deps
|
||||||
let deps = deps.iter().filter(|(dep, _)| {
|
.iter()
|
||||||
*dep.state() == PullRequestStatus::Open
|
.filter(|(dep, _)| *dep.state() == PullRequestStatus::Open)
|
||||||
}).collect::<Vec<_>>();
|
.collect::<Vec<_>>();
|
||||||
|
|
||||||
let mut out = String::new();
|
let mut out = String::new();
|
||||||
|
|
||||||
out.push_str("#!/usr/bin/env bash\n\n");
|
out.push_str("#!/usr/bin/env bash\n\n");
|
||||||
out.push_str("set -euo pipefail\n");
|
out.push_str("set -euo pipefail\n");
|
||||||
out.push_str("set -o xtrace\n\n");
|
out.push_str("set -o xtrace\n\n");
|
||||||
|
|
||||||
out.push_str("# ------ THIS SCRIPT ASSUMES YOUR PR STACK IS A SINGLE CHAIN WITHOUT BRANCHING ----- #\n\n");
|
out.push_str("# ------ THIS SCRIPT ASSUMES YOUR PR STACK IS A SINGLE CHAIN WITHOUT BRANCHING ----- #\n\n");
|
||||||
out.push_str("# It starts at the base of the stack, cherry-picking onto the new base and force-pushing as it goes.\n");
|
out.push_str("# It starts at the base of the stack, cherry-picking onto the new base and force-pushing as it goes.\n");
|
||||||
out.push_str("# We can't tell where the initial cherry-pick should stop (mainly because of our squash merge workflow),\n");
|
out.push_str("# We can't tell where the initial cherry-pick should stop (mainly because of our squash merge workflow),\n");
|
||||||
out.push_str("# so that initial stopping point for the first PR needs to be specified manually.\n\n");
|
out.push_str(
|
||||||
|
"# so that initial stopping point for the first PR needs to be specified manually.\n\n",
|
||||||
|
);
|
||||||
|
|
||||||
out.push_str("export PREBASE=\"<enter a marker to stop the initial cherry-pick at>\"\n");
|
out.push_str("export PREBASE=\"<enter a marker to stop the initial cherry-pick at>\"\n");
|
||||||
|
|
||||||
for (from, to) in deps {
|
for (from, to) in deps {
|
||||||
let to = if let Some(pr) = to {
|
let to = if let Some(pr) = to {
|
||||||
pr.head().to_string()
|
pr.head().to_string()
|
||||||
} else {
|
} else {
|
||||||
String::from("<enter a ref to rebase the stack on; usually `develop`>")
|
String::from("<enter a ref to rebase the stack on; usually `develop`>")
|
||||||
};
|
};
|
||||||
|
|
||||||
out.push_str("\n# -------------- #\n\n");
|
out.push_str("\n# -------------- #\n\n");
|
||||||
|
|
||||||
out.push_str(&format!("export TO=\"{}\"\n", process_ref(&to)));
|
out.push_str(&format!("export TO=\"{}\"\n", process_ref(&to)));
|
||||||
out.push_str(&format!("export FROM=\"{}\"\n\n", process_ref(from.head())));
|
out.push_str(&format!("export FROM=\"{}\"\n\n", process_ref(from.head())));
|
||||||
|
|
||||||
out.push_str("git checkout heap/\"$TO\"\n");
|
out.push_str("git checkout heap/\"$TO\"\n");
|
||||||
out.push_str("git cherry-pick \"$PREBASE\"..heap/\"$FROM\"\n");
|
out.push_str("git cherry-pick \"$PREBASE\"..heap/\"$FROM\"\n");
|
||||||
out.push_str("export PREBASE=\"$(git rev-parse --verify heap/$FROM)\"\n");
|
out.push_str("export PREBASE=\"$(git rev-parse --verify heap/$FROM)\"\n");
|
||||||
out.push_str("git push -f heap HEAD:refs/heads/\"$FROM\"\n");
|
out.push_str("git push -f heap HEAD:refs/heads/\"$FROM\"\n");
|
||||||
}
|
}
|
||||||
|
|
||||||
out
|
out
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,14 +1,14 @@
|
||||||
use petgraph::visit::Bfs;
|
use petgraph::visit::Bfs;
|
||||||
use petgraph::visit::EdgeRef;
|
use petgraph::visit::EdgeRef;
|
||||||
use petgraph::{Direction, Graph};
|
use petgraph::{Direction, Graph};
|
||||||
use std::rc::Rc;
|
|
||||||
use std::collections::HashMap;
|
use std::collections::HashMap;
|
||||||
|
use std::rc::Rc;
|
||||||
|
|
||||||
use crate::api::search::PullRequest;
|
use crate::api::search::PullRequest;
|
||||||
|
|
||||||
pub type FlatDep = Vec<(Rc<PullRequest>, Option<Rc<PullRequest>>)>;
|
pub type FlatDep = Vec<(Rc<PullRequest>, Option<Rc<PullRequest>>)>;
|
||||||
|
|
||||||
pub fn build(prs: &Vec<Rc<PullRequest>>) -> Graph<Rc<PullRequest>, usize> {
|
pub fn build(prs: &[Rc<PullRequest>]) -> Graph<Rc<PullRequest>, usize> {
|
||||||
let mut tree = Graph::<Rc<PullRequest>, usize>::new();
|
let mut tree = Graph::<Rc<PullRequest>, usize>::new();
|
||||||
let heads = prs.iter().map(|pr| pr.head());
|
let heads = prs.iter().map(|pr| pr.head());
|
||||||
let handles: Vec<_> = prs.iter().map(|pr| tree.add_node(pr.clone())).collect();
|
let handles: Vec<_> = prs.iter().map(|pr| tree.add_node(pr.clone())).collect();
|
||||||
|
@ -25,7 +25,7 @@ pub fn build(prs: &Vec<Rc<PullRequest>>) -> Graph<Rc<PullRequest>, usize> {
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Return a flattened list of graph nodes as tuples; each tuple is `(node, node's parent [if exists])`.
|
/// Return a flattened list of graph nodes as tuples; each tuple is `(node, node's parent [if exists])`.
|
||||||
pub fn log(graph: &Graph<Rc<PullRequest>, usize>) -> FlatDep {
|
pub fn log(graph: &Graph<Rc<PullRequest>, usize>) -> FlatDep {
|
||||||
let roots: Vec<_> = graph.externals(Direction::Incoming).collect();
|
let roots: Vec<_> = graph.externals(Direction::Incoming).collect();
|
||||||
let mut out = Vec::new();
|
let mut out = Vec::new();
|
||||||
|
|
||||||
|
|
|
@ -1,8 +1,8 @@
|
||||||
pub mod api;
|
pub mod api;
|
||||||
|
pub mod git;
|
||||||
pub mod graph;
|
pub mod graph;
|
||||||
pub mod markdown;
|
pub mod markdown;
|
||||||
pub mod persist;
|
pub mod persist;
|
||||||
pub mod git;
|
|
||||||
|
|
||||||
pub struct Credentials {
|
pub struct Credentials {
|
||||||
// Personal access token
|
// Personal access token
|
||||||
|
|
23
src/main.rs
23
src/main.rs
|
@ -1,13 +1,14 @@
|
||||||
use std::collections::HashMap;
|
use std::collections::HashMap;
|
||||||
use std::env;
|
use std::env;
|
||||||
use std::error::Error;
|
use std::error::Error;
|
||||||
use std::process;
|
|
||||||
use std::io::{self, Write};
|
|
||||||
use std::rc::Rc;
|
|
||||||
use std::fs;
|
use std::fs;
|
||||||
|
use std::io::{self, Write};
|
||||||
|
use std::process;
|
||||||
|
use std::rc::Rc;
|
||||||
|
|
||||||
|
use gh_stack::api::search::PullRequest;
|
||||||
use gh_stack::Credentials;
|
use gh_stack::Credentials;
|
||||||
use gh_stack::{api, graph, markdown, persist, git};
|
use gh_stack::{api, git, graph, markdown, persist};
|
||||||
|
|
||||||
pub fn read_cli_input(message: &str) -> String {
|
pub fn read_cli_input(message: &str) -> String {
|
||||||
print!("{}", message);
|
print!("{}", message);
|
||||||
|
@ -51,7 +52,10 @@ async fn main() -> Result<(), Box<dyn Error>> {
|
||||||
let credentials = Credentials::new(token);
|
let credentials = Credentials::new(token);
|
||||||
|
|
||||||
let prs = api::search::fetch_pull_requests_matching(&pattern, &credentials).await?;
|
let prs = api::search::fetch_pull_requests_matching(&pattern, &credentials).await?;
|
||||||
let prs = prs.into_iter().map(|pr| Rc::new(pr)).collect();
|
let prs = prs
|
||||||
|
.into_iter()
|
||||||
|
.map(Rc::new)
|
||||||
|
.collect::<Vec<Rc<PullRequest>>>();
|
||||||
let tree = graph::build(&prs);
|
let tree = graph::build(&prs);
|
||||||
|
|
||||||
match command {
|
match command {
|
||||||
|
@ -59,15 +63,14 @@ async fn main() -> Result<(), Box<dyn Error>> {
|
||||||
let table = markdown::build_table(tree, pattern);
|
let table = markdown::build_table(tree, pattern);
|
||||||
|
|
||||||
let output = match prelude {
|
let output = match prelude {
|
||||||
Some(prelude) => build_final_output(prelude, &table),
|
Some(prelude) => build_final_output(prelude, &table),
|
||||||
None => table
|
None => table,
|
||||||
};
|
};
|
||||||
|
|
||||||
for pr in prs.iter() {
|
for pr in prs.iter() {
|
||||||
println!("{}: {}", pr.number(), pr.title());
|
println!("{}: {}", pr.number(), pr.title());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
let response = read_cli_input("Going to update these PRs ☝️ (y/n): ");
|
let response = read_cli_input("Going to update these PRs ☝️ (y/n): ");
|
||||||
match &response[..] {
|
match &response[..] {
|
||||||
"y" => persist::persist(&prs, &output, &credentials).await?,
|
"y" => persist::persist(&prs, &output, &credentials).await?,
|
||||||
|
@ -88,12 +91,12 @@ async fn main() -> Result<(), Box<dyn Error>> {
|
||||||
for (pr, maybe_parent) in log {
|
for (pr, maybe_parent) in log {
|
||||||
match maybe_parent {
|
match maybe_parent {
|
||||||
Some(parent) => println!("{} → {}", pr.head(), parent.head()),
|
Some(parent) => println!("{} → {}", pr.head(), parent.head()),
|
||||||
None => println!("{} → N/A", pr.head())
|
None => println!("{} → N/A", pr.head()),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
_ => { panic!("Invalid command!") }
|
_ => panic!("Invalid command!"),
|
||||||
};
|
};
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
|
|
|
@ -32,7 +32,7 @@ pub fn build_table(graph: Graph<Rc<PullRequest>, usize>, title: &str) -> String
|
||||||
"|#{}|{}|#{}|\n",
|
"|#{}|{}|#{}|\n",
|
||||||
node.number(),
|
node.number(),
|
||||||
node.title(),
|
node.title(),
|
||||||
graph[parent.source().clone()].number()
|
graph[parent.source()].number()
|
||||||
),
|
),
|
||||||
None => format!("|#{}|{}|**N/A**|\n", node.number(), node.title()),
|
None => format!("|#{}|{}|**N/A**|\n", node.number(), node.title()),
|
||||||
};
|
};
|
||||||
|
|
|
@ -29,7 +29,7 @@ fn safe_replace(body: &str, table: &str) -> String {
|
||||||
}
|
}
|
||||||
|
|
||||||
pub async fn persist(
|
pub async fn persist(
|
||||||
prs: &Vec<Rc<PullRequest>>,
|
prs: &[Rc<PullRequest>],
|
||||||
table: &str,
|
table: &str,
|
||||||
c: &Credentials,
|
c: &Credentials,
|
||||||
) -> Result<(), Box<dyn Error>> {
|
) -> Result<(), Box<dyn Error>> {
|
||||||
|
|
Loading…
Reference in New Issue