summaryrefslogtreecommitdiffstats
path: root/pkgs/by-name/ro/rollup/src/main.rs
diff options
context:
space:
mode:
Diffstat (limited to 'pkgs/by-name/ro/rollup/src/main.rs')
-rw-r--r--pkgs/by-name/ro/rollup/src/main.rs200
1 files changed, 120 insertions, 80 deletions
diff --git a/pkgs/by-name/ro/rollup/src/main.rs b/pkgs/by-name/ro/rollup/src/main.rs
index 8285e702..48b395a5 100644
--- a/pkgs/by-name/ro/rollup/src/main.rs
+++ b/pkgs/by-name/ro/rollup/src/main.rs
@@ -1,105 +1,145 @@
-use core::fmt;
-use std::{path::PathBuf, process::Command};
+use std::{
+ io::Write,
+ path::PathBuf,
+ process::{Command, Stdio},
+};
-use anyhow::{Context, Result};
+use anyhow::{Context, Result, bail};
use clap::Parser;
+use crate::parse::Patch;
+
+mod parse;
+
#[derive(Parser)]
struct Cli {
/// The file to pull the patch to rollup from.
- patch_file: PathBuf,
-}
+ patch_files: Vec<PathBuf>,
-struct Diff {
- files: Vec<FileDiff>,
-}
-
-struct FileDiff {
- from: PathBuf,
+ /// Which repository to use as a target for the rollup
+ ///
+ /// Needs to be clean.
+ #[arg(short, long)]
to: PathBuf,
- diff: String,
- index: String,
-}
-impl fmt::Display for FileDiff {
- fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
- writeln!(
- f,
- "diff --git a/{} b/{}",
- self.from.display(),
- self.to.display()
- )?;
- writeln!(f, "--- a/{}", self.from.display())?;
- writeln!(f, "+++ b/{}", self.to.display())?;
- writeln!(f, "index {}", self.index)?;
- writeln!(f, "{}", self.diff)?;
-
- Ok(())
- }
-}
-
-macro_rules! take {
- ($line:expr, $prefix:literal) => {
- $line.strip_prefix($prefix).unwrap().trim()
- };
+ #[arg(short, long)]
+ debug: bool,
}
fn main() -> Result<()> {
let args = Cli::parse();
- let data = Command::new("filterdiff")
- .arg(&args.patch_file)
- .output()
- .context("Failed to run `filterdiff` for patch analysis")?;
+ let mut patches = vec![];
+ for file in &args.patch_files {
+ patches.push(parse::parse_file(file, args.debug)?);
+ }
- let stdout = String::from_utf8(data.stdout).unwrap();
+ eprintln!("Successfully parsed all {} patch files.", patches.len());
- let mut diff = Diff { files: vec![] };
- let mut file_diff_from = None;
- let mut file_diff_to = None;
- let mut file_diff_diff: Option<String> = None;
- let mut file_diff_index: Option<String> = None;
- for (idx, line) in stdout.lines().enumerate() {
- eprintln!("{}", line);
+ {
+ let git_check = Command::new("git")
+ .args([
+ "-C",
+ args.to.display().to_string().as_str(),
+ "status",
+ "--porcelain",
+ ])
+ .output()
+ .context("Failed to run `git status --procelain`")?;
+ if !git_check.stdout.is_empty() {
+ bail!(
+ "`--to` (`{}`) workspace is dirty, refusing to continue.",
+ args.to.display()
+ );
+ }
+ }
- if line.starts_with("---") {
- file_diff_from = Some(PathBuf::from(take!(line, "--- a/")));
- } else if line.starts_with("+++") {
- file_diff_to = Some(PathBuf::from(take!(line, "+++ b/")));
- } else if line.starts_with("index") {
- file_diff_index = Some(take!(line, "index").to_owned());
- } else if line.starts_with("diff --git") {
- if idx != 0 {
- diff.files.push(FileDiff {
- from: file_diff_from.take().unwrap(),
- to: file_diff_to.take().unwrap(),
- diff: file_diff_diff.take().unwrap(),
- index: file_diff_index.take().unwrap(),
- });
- }
- } else {
- match file_diff_diff.as_mut() {
- Some(str) => {
- str.push_str(line);
- str.push('\n');
+ for (idx, patch) in patches.iter().enumerate() {
+ eprintln!(
+ "Applying {}. patch: `{}`...",
+ idx + 1,
+ args.patch_files[idx].display()
+ );
+
+ let mut final_diffs = vec![];
+ for file_diff in &patch.files {
+ match file_diff {
+ parse::FileDiff::Add { path, .. } => {
+ if args.to.join(path).exists() {
+ bail!("A file was added, that already existed. This should be impossible.")
+ }
+ final_diffs.push(file_diff.to_owned());
}
- None => {
- let mut base = line.to_owned();
- base.push('\n');
- file_diff_diff = Some(base);
+ parse::FileDiff::Remove { path, .. } => {
+ if args.to.join(path).exists() {
+ final_diffs.push(file_diff.to_owned());
+ } else {
+ eprintln!(
+ "Dropping remove of `{}`, as the file does not exist in the rollup target.",
+ path.display()
+ );
+ }
+ }
+ parse::FileDiff::Change { from, to, .. } => {
+ if args.to.join(from).exists() {
+ if from != to && args.to.join(to).exists() {
+ bail!(
+ "A rename (`{}` -> `{}`) tried to rename a path to an already existing path. This should be impossible.",
+ from.display(),
+ to.display()
+ )
+ }
+ final_diffs.push(file_diff.to_owned());
+ } else {
+ eprintln!(
+ "Dropping change to `{}`, as the file does not exist in the rollup target.",
+ from.display()
+ );
+ }
}
}
}
- }
- diff.files.push(FileDiff {
- from: file_diff_from.unwrap(),
- to: file_diff_to.unwrap(),
- diff: file_diff_diff.unwrap(),
- index: file_diff_index.unwrap(),
- });
- for file in diff.files {
- eprintln!("{}", file)
+ let filtered_patch = Patch {
+ files: final_diffs,
+ commit_message: patch.commit_message.clone(),
+ };
+
+ let mut apply_child = Command::new("git")
+ .args(["-C", args.to.display().to_string().as_str(), "apply", "-"])
+ .stdin(Stdio::piped())
+ .spawn()?;
+ let mut stdin = apply_child.stdin.take().expect("is piped");
+ write!(stdin, "{}", filtered_patch)?;
+ drop(stdin);
+ let status = apply_child.wait()?;
+ if !status.success() {
+ bail!("git apply failed.")
+ }
+
+ let add = Command::new("git")
+ .args(["-C", args.to.display().to_string().as_str(), "add", "."])
+ .status()?;
+ if !add.success() {
+ bail!("git add ., failed.")
+ }
+
+ let mut commit_child = Command::new("git")
+ .args([
+ "-C",
+ args.to.display().to_string().as_str(),
+ "commit",
+ "--file=-",
+ ])
+ .stdin(Stdio::piped())
+ .spawn()?;
+ let mut stdin = commit_child.stdin.take().expect("is piped");
+ write!(stdin, "{}", filtered_patch.commit_message)?;
+ drop(stdin);
+ let status = apply_child.wait()?;
+ if !status.success() {
+ bail!("git commit failed.")
+ }
}
Ok(())