From 8d3fcb5c16733d5d02bcb486e5513d9d449a9091 Mon Sep 17 00:00:00 2001 From: CohenArthur Date: Fri, 9 Feb 2024 17:10:54 +0100 Subject: [PATCH 1/5] [wip] [fixup]: Start running tests when cherry-picking --- src/main.rs | 16 +++++++- src/make.rs | 104 ++++++++++++++++++++++++++++++++++++++++++++++++ src/upstream.rs | 92 +++++++++++++++++++++++++++++++++++++----- 3 files changed, 200 insertions(+), 12 deletions(-) create mode 100644 src/make.rs diff --git a/src/main.rs b/src/main.rs index f931358..9be54a6 100644 --- a/src/main.rs +++ b/src/main.rs @@ -8,6 +8,7 @@ use clap::{Parser, Subcommand}; mod clog; pub mod git; +mod make; mod parser; mod upstream; @@ -35,6 +36,13 @@ enum SubCmd { help = "work directory which contains a copy of the gccrs respository" )] work: PathBuf, + + #[arg( + short, + long, + help = "repository on which to submit the GitHub pull-request" + )] + repo: String, }, } @@ -51,11 +59,17 @@ async fn main() -> anyhow::Result<()> { match args.cmd { SubCmd::ChangeLogs => clog::check_clog_checker_output()?, - SubCmd::Upstream { token, to, work } => { + SubCmd::Upstream { + token, + to, + work, + repo, + } => { upstream::prepare_commits(upstream::UpstreamOpt { token, branch: to, gccrs: work, + repo, }) .await? } diff --git a/src/make.rs b/src/make.rs new file mode 100644 index 0000000..0afe128 --- /dev/null +++ b/src/make.rs @@ -0,0 +1,104 @@ +use std::fmt; +use std::io; +use std::path::PathBuf; +use std::process::{self, Command, Stdio}; +use std::str; + +use thiserror::Error; + +#[derive(Default, Debug)] +pub struct Make { + directory: Option, + jobs: Option, + load: Option, + recipes: Vec, +} + +// FIXME: Factor these two types in a common type for ::git and this module +#[derive(Debug)] +pub struct Output { + pub status: process::ExitStatus, + pub stdout: String, + pub stderr: Vec, +} +#[derive(Debug, Error)] +pub enum Error { + IO(#[from] io::Error), + Status(process::Output), + Utf8(#[from] str::Utf8Error), +} +// FIXME: +impl fmt::Display for Error { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + write!(f, "{self:#?}") + } +} +// FIXME: +impl TryFrom for Output { + type Error = str::Utf8Error; + + fn try_from(out: process::Output) -> Result { + let stdout = str::from_utf8(out.stdout.as_slice())?; + let stdout = stdout.trim_end().to_string(); + + Ok(Output { + status: out.status, + stderr: out.stderr, + stdout, + }) + } +} + +pub fn new() -> Make { + Make::default() +} + +impl Make { + pub fn directory(self, dir: impl Into) -> Make { + Make { + directory: Some(dir.into().display().to_string()), + ..self + } + } + + pub fn jobs(self, jobs: usize) -> Make { + Make { + jobs: Some(jobs), + ..self + } + } + + pub fn load(self, load: usize) -> Make { + Make { + load: Some(load), + ..self + } + } + + pub fn recipe(self, recipe: impl Into) -> Make { + let mut recipes = self.recipes; + recipes.push(recipe.into()); + + Make { recipes, ..self } + } + + pub fn spawn(self) -> Result { + let mut cmd = Command::new("make"); + cmd.stdout(Stdio::piped()).stderr(Stdio::piped()); + + self.directory.map(|d| cmd.arg("-C").arg(d)); + self.jobs.map(|jobs| cmd.arg(format!("-j{jobs}"))); + self.load.map(|load| cmd.arg(format!("-l{load}"))); + self.recipes.iter().for_each(|recipe| { + cmd.arg(recipe); + }); + + let output = cmd.spawn()?.wait_with_output()?; + + if output.status.success() { + Ok(output.try_into()?) + } else { + Err(Error::Status(output)) + } + } +} diff --git a/src/upstream.rs b/src/upstream.rs index 62f950c..d1c8b8e 100644 --- a/src/upstream.rs +++ b/src/upstream.rs @@ -84,11 +84,13 @@ use octocrab::OctocrabBuilder; use thiserror::Error; use crate::git::{self, GitCmd}; +use crate::make; pub struct UpstreamOpt { pub token: Option, pub branch: String, pub gccrs: PathBuf, + pub repo: String, } #[derive(Debug, Error)] @@ -122,7 +124,7 @@ impl Display for Error { // git push -u origin HEAD // create_pr() -pub fn maybe_prefix_cherry_picked_commit() -> Result<(), Error> { +fn maybe_prefix_cherry_picked_commit() -> Result<(), Error> { let msg = git::log() .amount(1) .format(git::Format::Body) @@ -141,7 +143,41 @@ pub fn maybe_prefix_cherry_picked_commit() -> Result<(), Error> { Ok(()) } -fn prepare_body(last_commit: String, rev_list: String) -> String { +enum BuildError { + Build, + Tests, +} + +fn build_prefixed_commit() -> Result<(), BuildError> { + let build_dir = PathBuf::from("build-gerris"); + + info!("building last applied commit..."); + make::new() + .directory(&build_dir) + .jobs(14) + .load(6) + .spawn() + .map_err(|_| BuildError::Build)?; + + info!("testing last applied commit..."); + let test_output = make::new() + .directory(&build_dir) + .recipe("check-rust") + .jobs(14) + .load(6) + .spawn() + .map_err(|_| BuildError::Tests)? + .stdout; + + if test_output.contains("unexpected") || test_output.contains("unresolved") { + warn!("unexpected test failure in last commit"); + Err(BuildError::Tests) + } else { + Ok(()) + } +} + +fn prepare_body(last_commit: String, result_tab: String) -> String { format!( " This pull-request aims to help upstreaming commits to the GCC repository by formatting them \ @@ -153,7 +189,7 @@ The last commit upstreamed was: The list of commits prepared is as follows: -{rev_list} +{result_tab} 🐙 " @@ -165,10 +201,11 @@ pub async fn prepare_commits( token, branch, gccrs, + repo, }: UpstreamOpt, -) -> Result<(), Error> { +) -> Result<(), Error /* FIXME: Should this return another Error type then */> { // let _ = CdRaii::change_path(gccrs); - std::env::set_current_dir(gccrs)?; + std::env::set_current_dir(&gccrs)?; info!("fetching `upstream`..."); git::fetch().remote("upstream").spawn()?; @@ -224,12 +261,45 @@ pub async fn prepare_commits( info!("created branch `{new_branch}`"); - rev_list.lines().try_for_each(|commit| { + let tab = String::from("|Commit|Build|Test|\n|---|:-:|:-:|"); + + let tab = rev_list.lines().fold(tab, |tab, commit| { info!("cherry-picking {commit}..."); - git::cherry_pick(git::Commit(commit)).spawn()?; - maybe_prefix_cherry_picked_commit() - })?; + // FIXME: Can we unwrap here? + git::cherry_pick(git::Commit(commit)) + .spawn() + .expect("couldn't cherry pick commit"); + + let checks = build_prefixed_commit(); + + enum CheckResult { + BuildFailure, + TestFailure, + AllOk, + } + + let result = match checks { + Ok(_) => CheckResult::AllOk, + Err(BuildError::Build) => CheckResult::BuildFailure, + Err(BuildError::Tests) => CheckResult::TestFailure, + }; + + // FIXME: Can we unwrap here? + maybe_prefix_cherry_picked_commit().expect("couldn't prefix commit"); + + let build_result = match result { + CheckResult::BuildFailure => "❌", + CheckResult::TestFailure | CheckResult::AllOk => "✅", + }; + + let test_result = match result { + CheckResult::AllOk => "✅", + _ => "❌", + }; + + format!("{tab}\n|{commit}|{build_result}|{test_result}|") + }); info!("pushing branch..."); git::push() @@ -247,14 +317,14 @@ pub async fn prepare_commits( .unwrap(); instance - .pulls("rust-gcc", "gccrs") + .pulls(repo, "gccrs") .create( format!("[upstream] [{}] Prepare commits", Local::now().date_naive()), // FIXME: Will branches always be created and pushed from my fork? Add CLI parameter for this maybe? format!("cohenarthur:{new_branch}"), branch, ) - .body(prepare_body(last_upstreamed_commit, rev_list)) + .body(prepare_body(last_upstreamed_commit, tab)) .maintainer_can_modify(true) .send() .await From c863616247a6085d22c5d660046f95cfdde9559a Mon Sep 17 00:00:00 2001 From: CohenArthur Date: Mon, 12 Feb 2024 17:06:33 +0100 Subject: [PATCH 2/5] shell: Add common submodule for git and make --- src/git.rs | 57 +++++++++---------------------------------------- src/main.rs | 3 ++- src/make.rs | 54 ++++++---------------------------------------- src/shell.rs | 53 +++++++++++++++++++++++++++++++++++++++++++++ src/upstream.rs | 5 +++-- 5 files changed, 74 insertions(+), 98 deletions(-) create mode 100644 src/shell.rs diff --git a/src/git.rs b/src/git.rs index 047a2ed..1fb10a3 100644 --- a/src/git.rs +++ b/src/git.rs @@ -1,7 +1,7 @@ -use std::process::{self, Command, Stdio}; -use std::{fmt, io, str}; +use std::process::{Command, Stdio}; +use std::str; -use thiserror::Error; +use crate::shell::{self, Error, Output}; // TODO: Mark all subcommands types as must use mod branch; @@ -22,20 +22,6 @@ pub use push::push; pub use rev_list::rev_list; pub use switch::switch; -#[derive(Debug, Error)] -pub enum Error { - IO(#[from] io::Error), - Status(process::Output), - Utf8(#[from] str::Utf8Error), -} - -// FIXME: -impl fmt::Display for Error { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - write!(f, "{self:#?}") - } -} - // FIXME: Move to `log` module? #[derive(Clone, Copy)] pub enum Format { @@ -58,43 +44,20 @@ pub struct Branch>(pub T); pub struct Commit>(pub T); pub struct Remote>(pub T); -pub struct Output { - pub status: process::ExitStatus, - pub stdout: String, - pub stderr: Vec, -} - -impl TryFrom for Output { - type Error = str::Utf8Error; - - fn try_from(out: process::Output) -> Result { - let stdout = str::from_utf8(out.stdout.as_slice())?; - let stdout = stdout.trim_end().to_string(); - - Ok(Output { - status: out.status, - stderr: out.stderr, - stdout, - }) - } +pub trait GitCmd: Sized { + fn setup(self, cmd: &mut Command); } -pub trait GitCmd: Sized { - // FIXME: Spawn needs to check the exit code and encode that in its return type - non-zero should be Err +impl shell::Command for T +where + T: GitCmd, +{ fn spawn(self) -> Result { let mut cmd = Command::new("git"); cmd.stdout(Stdio::piped()).stderr(Stdio::piped()); self.setup(&mut cmd); - let output = cmd.spawn()?.wait_with_output()?; - - if output.status.success() { - Ok(output.try_into()?) - } else { - Err(Error::Status(output)) - } + T::spawn_with_output(cmd) } - - fn setup(self, cmd: &mut Command); } diff --git a/src/main.rs b/src/main.rs index 9be54a6..de3540c 100644 --- a/src/main.rs +++ b/src/main.rs @@ -7,9 +7,10 @@ use clap::{Parser, Subcommand}; // FIXME: If not, use nom instead of the hand-written combinator mod clog; -pub mod git; +mod git; mod make; mod parser; +mod shell; mod upstream; #[derive(Clone, Subcommand)] diff --git a/src/make.rs b/src/make.rs index 0afe128..5ec7081 100644 --- a/src/make.rs +++ b/src/make.rs @@ -1,10 +1,7 @@ -use std::fmt; -use std::io; use std::path::PathBuf; -use std::process::{self, Command, Stdio}; -use std::str; +use std::process::{Command, Stdio}; -use thiserror::Error; +use crate::shell::{self, Error, Output}; #[derive(Default, Debug)] pub struct Make { @@ -14,41 +11,6 @@ pub struct Make { recipes: Vec, } -// FIXME: Factor these two types in a common type for ::git and this module -#[derive(Debug)] -pub struct Output { - pub status: process::ExitStatus, - pub stdout: String, - pub stderr: Vec, -} -#[derive(Debug, Error)] -pub enum Error { - IO(#[from] io::Error), - Status(process::Output), - Utf8(#[from] str::Utf8Error), -} -// FIXME: -impl fmt::Display for Error { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - write!(f, "{self:#?}") - } -} -// FIXME: -impl TryFrom for Output { - type Error = str::Utf8Error; - - fn try_from(out: process::Output) -> Result { - let stdout = str::from_utf8(out.stdout.as_slice())?; - let stdout = stdout.trim_end().to_string(); - - Ok(Output { - status: out.status, - stderr: out.stderr, - stdout, - }) - } -} - pub fn new() -> Make { Make::default() } @@ -81,8 +43,10 @@ impl Make { Make { recipes, ..self } } +} - pub fn spawn(self) -> Result { +impl shell::Command for Make { + fn spawn(self) -> Result { let mut cmd = Command::new("make"); cmd.stdout(Stdio::piped()).stderr(Stdio::piped()); @@ -93,12 +57,6 @@ impl Make { cmd.arg(recipe); }); - let output = cmd.spawn()?.wait_with_output()?; - - if output.status.success() { - Ok(output.try_into()?) - } else { - Err(Error::Status(output)) - } + Make::spawn_with_output(cmd) } } diff --git a/src/shell.rs b/src/shell.rs new file mode 100644 index 0000000..9302bba --- /dev/null +++ b/src/shell.rs @@ -0,0 +1,53 @@ +use std::io; +use std::process; +use std::str; + +use thiserror::Error; + +#[derive(Debug, Error)] +pub enum Error { + #[error("i/o error: {0}")] + IO(#[from] io::Error), + #[error("non-zero exit code: {} {0:?}", .0.status)] + Status(process::Output), + #[error("invalid UTF8: {0}")] + Utf8(#[from] str::Utf8Error), +} + +pub struct Output { + pub status: process::ExitStatus, + pub stdout: String, + pub stderr: Vec, +} + +impl TryFrom for Output { + type Error = str::Utf8Error; + + fn try_from(out: process::Output) -> Result { + let stdout = str::from_utf8(out.stdout.as_slice())?; + let stdout = stdout.trim_end().to_string(); + + Ok(Output { + status: out.status, + stderr: out.stderr, + stdout, + }) + } +} + +// FIXME: Should we have something like a shell::Command::new() function +// which setups the process::Command and pipes both stderr and stdout? +pub trait Command: Sized { + fn spawn_with_output(mut cmd: process::Command) -> Result { + let output = cmd.spawn()?.wait_with_output()?; + + if output.status.success() { + Ok(output.try_into()?) + } else { + Err(Error::Status(output)) + } + } + + // FIXME: Documentation: Spawn needs to check the exit code and encode that in its return type - non-zero should be Err + fn spawn(self) -> Result; +} diff --git a/src/upstream.rs b/src/upstream.rs index d1c8b8e..1ae6159 100644 --- a/src/upstream.rs +++ b/src/upstream.rs @@ -83,8 +83,9 @@ use log::{error, info, warn}; use octocrab::OctocrabBuilder; use thiserror::Error; -use crate::git::{self, GitCmd}; +use crate::git; use crate::make; +use crate::shell::{self, Command}; pub struct UpstreamOpt { pub token: Option, @@ -97,7 +98,7 @@ pub struct UpstreamOpt { pub enum Error { Io(#[from] io::Error), Utf8(#[from] string::FromUtf8Error), - Git(#[from] git::Error), + Shell(#[from] shell::Error), } impl Display for Error { From 905890e27aac6c39039e30ab41eb975690e59ded Mon Sep 17 00:00:00 2001 From: CohenArthur Date: Mon, 12 Feb 2024 17:52:22 +0100 Subject: [PATCH 3/5] chore: Cleanup creation of PR body --- src/github.rs | 43 ++++++++++++++++++++++++++++ src/main.rs | 1 + src/upstream.rs | 74 +++++++++++-------------------------------------- 3 files changed, 60 insertions(+), 58 deletions(-) create mode 100644 src/github.rs diff --git a/src/github.rs b/src/github.rs new file mode 100644 index 0000000..422f206 --- /dev/null +++ b/src/github.rs @@ -0,0 +1,43 @@ +//! Github specific part of `gerris` - this module concerns the formatting of the PR's body, as well as any operations +//! related to interacting with Github. + +use crate::upstream::BuildError; + +const FAILURE: &str = "❌"; +const SUCCESS: &str = "✅"; + +pub fn prepare_body<'commit>( + last_commit: String, /* FIXME: Should this be a Commit type? */ + commit_status_iter: impl Iterator)>, +) -> String { + let tab = String::from("|Commit|Build|Test|\n|---|:-:|:-:|"); + + let tab = commit_status_iter.fold(tab, |tab, (commit, result)| { + let (build_result, test_result) = match result { + Some(BuildError::Build) => (FAILURE, FAILURE), + Some(BuildError::Tests) => (SUCCESS, FAILURE), + _ => (SUCCESS, SUCCESS), + }; + + format!("{tab}\n|{commit}|{build_result}|{test_result}|") + }); + + // TODO: Put this in a const somewhere. Cleanup that file overall + format!( + " +This pull-request aims to help upstreaming commits to the GCC repository by formatting them \ +and checking that they can be cherry-picked/rebased properly. + +The last commit upstreamed was: + +`{}` + +The list of commits prepared is as follows: + +{} + +🐙 +", + last_commit, tab + ) +} diff --git a/src/main.rs b/src/main.rs index de3540c..485f3f9 100644 --- a/src/main.rs +++ b/src/main.rs @@ -8,6 +8,7 @@ use clap::{Parser, Subcommand}; mod clog; mod git; +mod github; mod make; mod parser; mod shell; diff --git a/src/upstream.rs b/src/upstream.rs index 1ae6159..e7c2dd3 100644 --- a/src/upstream.rs +++ b/src/upstream.rs @@ -73,7 +73,6 @@ // git push -u origin HEAD // create_pr() -use std::fmt::{Display, Formatter, Result as FmtResult}; use std::io; use std::path::PathBuf; use std::string; @@ -84,6 +83,7 @@ use octocrab::OctocrabBuilder; use thiserror::Error; use crate::git; +use crate::github; use crate::make; use crate::shell::{self, Command}; @@ -96,17 +96,14 @@ pub struct UpstreamOpt { #[derive(Debug, Error)] pub enum Error { + #[error("i/o error: {0}")] Io(#[from] io::Error), + #[error("invalid UTF8: {0}")] Utf8(#[from] string::FromUtf8Error), + #[error("shell issue: {0}")] Shell(#[from] shell::Error), } -impl Display for Error { - fn fmt(&self, f: &mut Formatter<'_>) -> FmtResult { - write!(f, "{self:#?}") - } -} - // shell script equivalent: // // git fetch gcc @@ -125,6 +122,7 @@ impl Display for Error { // git push -u origin HEAD // create_pr() +// FIXME: It would be nice if this function had a better API since it mutates "global" state fn maybe_prefix_cherry_picked_commit() -> Result<(), Error> { let msg = git::log() .amount(1) @@ -144,7 +142,7 @@ fn maybe_prefix_cherry_picked_commit() -> Result<(), Error> { Ok(()) } -enum BuildError { +pub enum BuildError { Build, Tests, } @@ -178,25 +176,6 @@ fn build_prefixed_commit() -> Result<(), BuildError> { } } -fn prepare_body(last_commit: String, result_tab: String) -> String { - format!( - " -This pull-request aims to help upstreaming commits to the GCC repository by formatting them \ -and checking that they can be cherry-picked/rebased properly. - -The last commit upstreamed was: - -`{last_commit}` - -The list of commits prepared is as follows: - -{result_tab} - -🐙 - " - ) -} - pub async fn prepare_commits( UpstreamOpt { token, @@ -204,8 +183,7 @@ pub async fn prepare_commits( gccrs, repo, }: UpstreamOpt, -) -> Result<(), Error /* FIXME: Should this return another Error type then */> { - // let _ = CdRaii::change_path(gccrs); +) -> Result<(), Error> { std::env::set_current_dir(&gccrs)?; info!("fetching `upstream`..."); @@ -262,9 +240,7 @@ pub async fn prepare_commits( info!("created branch `{new_branch}`"); - let tab = String::from("|Commit|Build|Test|\n|---|:-:|:-:|"); - - let tab = rev_list.lines().fold(tab, |tab, commit| { + let commit_status_iter = rev_list.lines().map(|commit| { info!("cherry-picking {commit}..."); // FIXME: Can we unwrap here? @@ -272,36 +248,15 @@ pub async fn prepare_commits( .spawn() .expect("couldn't cherry pick commit"); - let checks = build_prefixed_commit(); - - enum CheckResult { - BuildFailure, - TestFailure, - AllOk, - } - - let result = match checks { - Ok(_) => CheckResult::AllOk, - Err(BuildError::Build) => CheckResult::BuildFailure, - Err(BuildError::Tests) => CheckResult::TestFailure, - }; + let result = build_prefixed_commit().err(); // FIXME: Can we unwrap here? maybe_prefix_cherry_picked_commit().expect("couldn't prefix commit"); - let build_result = match result { - CheckResult::BuildFailure => "❌", - CheckResult::TestFailure | CheckResult::AllOk => "✅", - }; - - let test_result = match result { - CheckResult::AllOk => "✅", - _ => "❌", - }; - - format!("{tab}\n|{commit}|{build_result}|{test_result}|") + (commit, result) }); + // FIXME: Factor this in a function in github module info!("pushing branch..."); git::push() .upstream(git::Remote("origin")) @@ -321,11 +276,14 @@ pub async fn prepare_commits( .pulls(repo, "gccrs") .create( format!("[upstream] [{}] Prepare commits", Local::now().date_naive()), - // FIXME: Will branches always be created and pushed from my fork? Add CLI parameter for this maybe? + // FIXME: Will branches always be created and pushed from my fork? Add CLI parameter for this format!("cohenarthur:{new_branch}"), branch, ) - .body(prepare_body(last_upstreamed_commit, tab)) + .body(github::prepare_body( + last_upstreamed_commit, + commit_status_iter, + )) .maintainer_can_modify(true) .send() .await From a8ba5d5a8d59731c22ce510765ddb677a6e74335 Mon Sep 17 00:00:00 2001 From: Arthur Cohen Date: Tue, 20 Feb 2024 11:23:48 +0100 Subject: [PATCH 4/5] [wip] --- src/github.rs | 6 ++--- src/shell.rs | 1 + src/upstream.rs | 58 +++++++++++++++++++++++++++++++++---------------- 3 files changed, 43 insertions(+), 22 deletions(-) diff --git a/src/github.rs b/src/github.rs index 422f206..e4fdc32 100644 --- a/src/github.rs +++ b/src/github.rs @@ -6,13 +6,13 @@ use crate::upstream::BuildError; const FAILURE: &str = "❌"; const SUCCESS: &str = "✅"; -pub fn prepare_body<'commit>( +pub fn prepare_body( last_commit: String, /* FIXME: Should this be a Commit type? */ - commit_status_iter: impl Iterator)>, + commits: Vec<(&str, Option)>, ) -> String { let tab = String::from("|Commit|Build|Test|\n|---|:-:|:-:|"); - let tab = commit_status_iter.fold(tab, |tab, (commit, result)| { + let tab = commits.iter().fold(tab, |tab, (commit, result)| { let (build_result, test_result) = match result { Some(BuildError::Build) => (FAILURE, FAILURE), Some(BuildError::Tests) => (SUCCESS, FAILURE), diff --git a/src/shell.rs b/src/shell.rs index 9302bba..ad5d5e7 100644 --- a/src/shell.rs +++ b/src/shell.rs @@ -14,6 +14,7 @@ pub enum Error { Utf8(#[from] str::Utf8Error), } +#[derive(Debug)] pub struct Output { pub status: process::ExitStatus, pub stdout: String, diff --git a/src/upstream.rs b/src/upstream.rs index e7c2dd3..c3f70bd 100644 --- a/src/upstream.rs +++ b/src/upstream.rs @@ -176,6 +176,24 @@ fn build_prefixed_commit() -> Result<(), BuildError> { } } +fn escape_regex_characters(s: &str) -> String { + let is_regex_character = |c| match c { + // FIXME: Add more regex characters as understood by git grep + '*' | '+' | '?' => true, + _ => false, + }; + + s.chars().into_iter().fold(String::new(), |mut acc, c| { + if is_regex_character(c) { + acc.push('\\'); + } + + acc.push(c); + + acc + }) +} + pub async fn prepare_commits( UpstreamOpt { token, @@ -199,6 +217,7 @@ pub async fn prepare_commits( .format(git::Format::Title) .spawn()? .stdout; + let last_upstreamed_commit = "gccrs: Fix macro parsing for trait items.".to_string(); info!("found last upstreamed commit: {}", last_upstreamed_commit); @@ -206,15 +225,15 @@ pub async fn prepare_commits( .strip_prefix("gccrs: ") .unwrap() .trim_end(); + let last_msg = escape_regex_characters(last_msg); - let last_commit_us = git::log() + let last_commit_us = dbg!(git::log() .amount(1) .grep(last_msg) .branch(git::Branch("upstream/master")) - .grep(last_msg) .format(git::Format::Hash) - .spawn()? - .stdout; + .spawn()?) + .stdout; info!("found equivalent commit: {}", last_commit_us); @@ -229,6 +248,7 @@ pub async fn prepare_commits( .stdout; warn!("found {} commits to upstream", rev_list.lines().count()); + info!("rev-list: {}", rev_list); let now = Local::now(); let new_branch = format!("prepare-{}-{}", now.date_naive(), now.timestamp_micros()); @@ -240,21 +260,24 @@ pub async fn prepare_commits( info!("created branch `{new_branch}`"); - let commit_status_iter = rev_list.lines().map(|commit| { - info!("cherry-picking {commit}..."); + let commits = rev_list + .lines() + .map(|commit| { + info!("cherry-picking {commit}..."); - // FIXME: Can we unwrap here? - git::cherry_pick(git::Commit(commit)) - .spawn() - .expect("couldn't cherry pick commit"); + // FIXME: Can we unwrap here? + git::cherry_pick(git::Commit(commit)) + .spawn() + .expect("couldn't cherry pick commit"); - let result = build_prefixed_commit().err(); + let result = build_prefixed_commit().err(); - // FIXME: Can we unwrap here? - maybe_prefix_cherry_picked_commit().expect("couldn't prefix commit"); + // FIXME: Can we unwrap here? + maybe_prefix_cherry_picked_commit().expect("couldn't prefix commit"); - (commit, result) - }); + (commit, result) + }) + .collect(); // FIXME: Factor this in a function in github module info!("pushing branch..."); @@ -280,10 +303,7 @@ pub async fn prepare_commits( format!("cohenarthur:{new_branch}"), branch, ) - .body(github::prepare_body( - last_upstreamed_commit, - commit_status_iter, - )) + .body(github::prepare_body(last_upstreamed_commit, commits)) .maintainer_can_modify(true) .send() .await From 85306e94cb47ed486031d441a6d42175add4bd86 Mon Sep 17 00:00:00 2001 From: Arthur Cohen Date: Wed, 21 Feb 2024 17:25:00 +0100 Subject: [PATCH 5/5] [wip] --- src/main.rs | 34 ++++++++++++++++++++++++++++++++++ src/upstream.rs | 32 ++++++++++++++++++++++++++++++++ 2 files changed, 66 insertions(+) diff --git a/src/main.rs b/src/main.rs index 485f3f9..38c6324 100644 --- a/src/main.rs +++ b/src/main.rs @@ -39,6 +39,33 @@ enum SubCmd { )] work: PathBuf, + #[arg( + short, + long, + help = "repository on which to submit the GitHub pull-request" + )] + repo: String, + }, + PullRequest { + #[arg(short, long, help = "branch from which to create the pull-request")] + branch: String, + + #[arg(short, long, help = "GitHub token to perform actions as gerris")] + token: String, + + #[arg( + long, + help = "branch on which to base the pull-request gerris will create" + )] + to: String, + + #[arg( + short, + long, + help = "work directory which contains a copy of the gccrs respository and the branch you would like to create a pull-request from (--branch)" + )] + work: PathBuf, + #[arg( short, long, @@ -75,6 +102,13 @@ async fn main() -> anyhow::Result<()> { }) .await? } + SubCmd::PullRequest { + branch, + token, + to, + work, + repo, + } => upstream::create_pull_request(token, repo, branch, to, work).await?, } Ok(()) diff --git a/src/upstream.rs b/src/upstream.rs index c3f70bd..ad602d5 100644 --- a/src/upstream.rs +++ b/src/upstream.rs @@ -314,3 +314,35 @@ pub async fn prepare_commits( Ok(()) } + +pub async fn create_pull_request( + token: String, + repo: String, + new_branch: String, + base: String, + gccrs: PathBuf, +) -> Result<(), Error> { + std::env::set_current_dir(&gccrs)?; + + info!("creating pull-request for `{new_branch}`..."); + + let instance = OctocrabBuilder::new() + .personal_token(token) + .build() + .unwrap(); + + instance + .pulls(repo, "gccrs") + .create( + format!("[upstream] [{}] Prepare commits", Local::now().date_naive()), + // FIXME: Will branches always be created and pushed from my fork? Add CLI parameter for this + format!("cohenarthur:{new_branch}"), + base, + ) + .maintainer_can_modify(true) + .send() + .await + .unwrap(); + + Ok(()) +}