Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[wip] [fixup]: Start running tests when cherry-picking #20

Open
wants to merge 5 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
57 changes: 10 additions & 47 deletions src/git.rs
Original file line number Diff line number Diff line change
@@ -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;
Expand All @@ -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 {
Expand All @@ -58,43 +44,20 @@ pub struct Branch<T: Into<String>>(pub T);
pub struct Commit<T: Into<String>>(pub T);
pub struct Remote<T: Into<String>>(pub T);

pub struct Output {
pub status: process::ExitStatus,
pub stdout: String,
pub stderr: Vec<u8>,
}

impl TryFrom<process::Output> for Output {
type Error = str::Utf8Error;

fn try_from(out: process::Output) -> Result<Self, Self::Error> {
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<T> shell::Command for T
where
T: GitCmd,
{
fn spawn(self) -> Result<Output, Error> {
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);
}
43 changes: 43 additions & 0 deletions src/github.rs
Original file line number Diff line number Diff line change
@@ -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(
last_commit: String, /* FIXME: Should this be a Commit type? */
commits: Vec<(&str, Option<BuildError>)>,
) -> String {
let tab = String::from("|Commit|Build|Test|\n|---|:-:|:-:|");

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),
_ => (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
)
}
54 changes: 52 additions & 2 deletions src/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -7,8 +7,11 @@ use clap::{Parser, Subcommand};
// FIXME: If not, use nom instead of the hand-written combinator

mod clog;
pub mod git;
mod git;
mod github;
mod make;
mod parser;
mod shell;
mod upstream;

#[derive(Clone, Subcommand)]
Expand All @@ -35,6 +38,40 @@ 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,
},
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,
help = "repository on which to submit the GitHub pull-request"
)]
repo: String,
},
}

Expand All @@ -51,14 +88,27 @@ 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?
}
SubCmd::PullRequest {
branch,
token,
to,
work,
repo,
} => upstream::create_pull_request(token, repo, branch, to, work).await?,
}

Ok(())
Expand Down
62 changes: 62 additions & 0 deletions src/make.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,62 @@
use std::path::PathBuf;
use std::process::{Command, Stdio};

use crate::shell::{self, Error, Output};

#[derive(Default, Debug)]
pub struct Make {
directory: Option<String>,
jobs: Option<usize>,
load: Option<usize>,
recipes: Vec<String>,
}

pub fn new() -> Make {
Make::default()
}

impl Make {
pub fn directory(self, dir: impl Into<PathBuf>) -> 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<String>) -> Make {
let mut recipes = self.recipes;
recipes.push(recipe.into());

Make { recipes, ..self }
}
}

impl shell::Command for Make {
fn spawn(self) -> Result<Output, Error> {
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);
});

Make::spawn_with_output(cmd)
}
}
54 changes: 54 additions & 0 deletions src/shell.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
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),
}

#[derive(Debug)]
pub struct Output {
pub status: process::ExitStatus,
pub stdout: String,
pub stderr: Vec<u8>,
}

impl TryFrom<process::Output> for Output {
type Error = str::Utf8Error;

fn try_from(out: process::Output) -> Result<Self, Self::Error> {
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(<bin>) 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<Output, Error> {
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<Output, Error>;
}
Loading