diff options
| author | Benedikt Peetz <benedikt.peetz@b-peetz.de> | 2026-11-04 20:09:47 +0100 |
|---|---|---|
| committer | Benedikt Peetz <benedikt.peetz@b-peetz.de> | 2026-11-05 22:47:43 +0100 |
| commit | a5c07e033b4660e1aabf45ec42eb1e4b4c07db8f (patch) | |
| tree | ce362e2f9d35c40792c2d1bad17a4f50d8b3605d /pkgs/by-name/ro/rollup/src/main.rs | |
| parent | pkgs/rollup: Init (diff) | |
| download | nixos-config-a5c07e033b4660e1aabf45ec42eb1e4b4c07db8f.zip | |
pkgs/rollup: Finish first mvp
Diffstat (limited to 'pkgs/by-name/ro/rollup/src/main.rs')
| -rw-r--r-- | pkgs/by-name/ro/rollup/src/main.rs | 200 |
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(()) |
