From 02bcf2eca5e1d667dd58a22b6ccf7756564d035d Mon Sep 17 00:00:00 2001 From: David Steele Date: Tue, 15 Aug 2023 10:58:10 +0100 Subject: [PATCH 1/4] Add support for capabilities --- Cargo.toml | 2 +- README.md | 2 +- src/config/file_info.rs | 48 +++++++++++++++++++++++------- src/main.rs | 66 +++++++++++++++++++++++++++++++++++++---- 4 files changed, 101 insertions(+), 17 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index 1c28483..2c7a834 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -8,7 +8,7 @@ homepage = "https://github.com/cat-in-136/cargo-generate-rpm" readme = "README.md" keywords = ["rpm", "package", "cargo", "subcommand"] repository = "https://github.com/cat-in-136/cargo-generate-rpm" -version = "0.12.0" +version = "0.12.1" edition = "2018" [dependencies] diff --git a/README.md b/README.md index f0009f2..20a02b1 100644 --- a/README.md +++ b/README.md @@ -53,6 +53,7 @@ from [the `Cargo.toml` file](https://doc.rust-lang.org/cargo/reference/manifest. * doc: set true if it is a document file. * user: the owner of the file. * group: the group owner of the file. + * caps: optional string of capabilities. (e.g. `cap_sys_admin=pe`) * release: optional string of release. * epoch: optional number of epoch. * pre_install_script: optional string of pre_install_script. @@ -198,4 +199,3 @@ The default payload compress type of the generated RPM file is zstd. You can specify the payload compress type with `--payload-compress TYPE`: none, gzip, or zstd. For the legacy system (e.g. centos7), specify legacy compress type explicitly e.g. `--payload-compress none`. - diff --git a/src/config/file_info.rs b/src/config/file_info.rs index a62f4e8..24bcd16 100644 --- a/src/config/file_info.rs +++ b/src/config/file_info.rs @@ -7,7 +7,7 @@ use std::path::{Path, PathBuf}; use toml::Value; #[derive(Debug, Eq, PartialEq, Clone)] -pub struct FileInfo<'a, 'b, 'c, 'd> { +pub struct FileInfo<'a, 'b, 'c, 'd, 'e> { pub source: &'a str, pub dest: &'b str, pub user: Option<&'c str>, @@ -15,9 +15,10 @@ pub struct FileInfo<'a, 'b, 'c, 'd> { pub mode: Option, pub config: bool, pub doc: bool, + pub caps: Option<&'e str>, } -impl FileInfo<'_, '_, '_, '_> { +impl FileInfo<'_, '_, '_, '_, '_> { pub fn new(assets: &[Value]) -> Result, ConfigError> { let mut files = Vec::with_capacity(assets.len()); for (idx, value) in assets.iter().enumerate() { @@ -53,6 +54,14 @@ impl FileInfo<'_, '_, '_, '_> { None }; let mode = Self::get_mode(table, source, idx)?; + let caps = if let Some(caps) = table.get("caps") { + Some( + caps.as_str() + .ok_or(ConfigError::AssetFileWrongType(idx, "caps", "string"))?, + ) + } else { + None + }; let config = if let Some(is_config) = table.get("config") { is_config .as_bool() @@ -76,6 +85,7 @@ impl FileInfo<'_, '_, '_, '_> { mode, config, doc, + caps, }); } Ok(files) @@ -139,7 +149,10 @@ impl FileInfo<'_, '_, '_, '_> { Err(ConfigError::AssetFileNotFound(PathBuf::from(source))) } - fn generate_rpm_file_options(&self, dest: T) -> rpm::FileOptions { + fn generate_rpm_file_options( + &self, + dest: T, + ) -> Result { let mut rpm_file_option = rpm::FileOptions::new(dest.to_string()); if let Some(user) = self.user { rpm_file_option = rpm_file_option.user(user); @@ -150,13 +163,19 @@ impl FileInfo<'_, '_, '_, '_> { if let Some(mode) = self.mode { rpm_file_option = rpm_file_option.mode(mode as i32); } + if let Some(caps) = self.caps { + rpm_file_option = match rpm_file_option.caps(caps) { + Ok(v) => v, + Err(_) => return Err(ConfigError::WrongType("caps".to_string(), "capability")), + }; + } if self.config { rpm_file_option = rpm_file_option.is_config(); } if self.doc { rpm_file_option = rpm_file_option.is_doc(); } - rpm_file_option.into() + Ok(rpm_file_option.into()) } pub(crate) fn generate_rpm_file_entry>( @@ -165,12 +184,14 @@ impl FileInfo<'_, '_, '_, '_> { parent: P, idx: usize, ) -> Result, ConfigError> { - self.generate_expanded_path(build_target, parent, idx) - .map(|p| { - p.iter() - .map(|(src, dst)| (src.clone(), self.generate_rpm_file_options(dst))) - .collect::>() - }) + match self.generate_expanded_path(build_target, parent, idx) { + Ok(v) => v.into_iter().try_fold(Vec::new(), |mut acc, (src, dst)| { + let rpm_file_option = self.generate_rpm_file_options(dst)?; + acc.push((src, rpm_file_option)); + Ok(acc) + }), + Err(e) => Err(e), + } } } @@ -290,6 +311,7 @@ mod test { mode: Some(0o0100755), config: false, doc: false, + caps: None, }, FileInfo { source: "LICENSE", @@ -299,6 +321,7 @@ mod test { mode: Some(0o0100644), config: false, doc: true, + caps: None, }, FileInfo { source: "README.md", @@ -308,6 +331,7 @@ mod test { mode: Some(0o0100644), config: false, doc: true, + caps: None, }, ] ); @@ -326,6 +350,7 @@ mod test { mode: None, config: false, doc: true, + caps: Some("cap_sys_admin=pe"), }; let expanded = file_info .generate_expanded_path(&target, &tempdir, 0) @@ -346,6 +371,7 @@ mod test { mode: None, config: false, doc: true, + caps: None, }; assert!( matches!(file_info.generate_expanded_path(&target, &tempdir, 0), @@ -362,6 +388,7 @@ mod test { mode: None, config: false, doc: false, + caps: None, }; let expanded = file_info .generate_expanded_path(&target, &tempdir, 0) @@ -431,6 +458,7 @@ mod test { mode: None, config: false, doc: false, + caps: None, }; let args = crate::cli::Cli { target_dir: Some( diff --git a/src/main.rs b/src/main.rs index 418223d..c766d19 100644 --- a/src/main.rs +++ b/src/main.rs @@ -41,6 +41,18 @@ fn collect_metadata(args: &Cli) -> Vec { .collect::>() } +fn determine_output_dir( + output: Option<&PathBuf>, + file_name: &str, + build_target: BuildTarget, +) -> PathBuf { + match output.as_ref().map(PathBuf::from) { + Some(path) if path.is_dir() => path.join(file_name), + Some(path) => path, + None => build_target.target_path("generate-rpm").join(file_name), + } +} + fn run() -> Result<(), Error> { let mut args = std::env::args(); let args = if let Some("generate-rpm") = args.nth(1).as_deref() { @@ -76,11 +88,8 @@ fn run() -> Result<(), Error> { .unwrap_or_default(); let file_name = format!("{pkg_name}-{pkg_version}{pkg_release}{pkg_arch}.rpm"); - let target_file_name = match args.target.map(PathBuf::from) { - Some(path) if path.is_dir() => path.join(file_name), - Some(path) => path, - None => build_target.target_path("generate-rpm").join(file_name), - }; + + let target_file_name = determine_output_dir(args.output.as_ref(), &file_name, build_target); if let Some(parent_dir) = target_file_name.parent() { if !parent_dir.exists() { @@ -101,3 +110,50 @@ fn main() { std::process::exit(1); } } + +#[cfg(test)] +mod tests { + use super::*; + // Test the three cases of determining the output file name: + // 1. Output is a directory + // 2. Output is a file + // 3. Output is not specified + #[test] + fn test_output_is_dir() { + let tempdir = tempfile::tempdir().unwrap(); + let pathbufbinding = &tempdir.path().to_path_buf(); + + let output = Some(pathbufbinding); + let file_name = "test.rpm"; + let build_target = BuildTarget::new(&crate::cli::Cli::default()); + + let target_file_name = determine_output_dir(output, &file_name, build_target); + assert_eq!(target_file_name, tempdir.path().join("test.rpm")); + } + #[test] + fn test_output_is_file() { + let tempdir = tempfile::tempdir().unwrap(); + let pathbufbinding = &tempdir.path().to_path_buf(); + let temppath = pathbufbinding.join("foo.rpm"); + + let output = Some(&temppath); + let file_name = "test.rpm"; + let build_target = BuildTarget::new(&crate::cli::Cli::default()); + + let target_file_name = determine_output_dir(output, &file_name, build_target); + assert_eq!(target_file_name, temppath); + } + + #[test] + fn test_no_output_specified() { + let output = None; + let file_name = "test.rpm"; + let build_target = BuildTarget::new(&crate::cli::Cli::default()); + + let target_file_name = determine_output_dir(output, &file_name, build_target); + assert_eq!( + target_file_name, + PathBuf::from("target/generate-rpm/test.rpm") + ); + } +} From 16c4fa6e48aacb492ad994e3cba408da1c1a2868 Mon Sep 17 00:00:00 2001 From: David Steele Date: Fri, 1 Sep 2023 08:43:07 +0100 Subject: [PATCH 2/4] Fixup try_fold clippy lint --- src/config/metadata.rs | 4 ++-- src/main.rs | 1 - 2 files changed, 2 insertions(+), 3 deletions(-) diff --git a/src/config/metadata.rs b/src/config/metadata.rs index 8703f07..8387c8d 100644 --- a/src/config/metadata.rs +++ b/src/config/metadata.rs @@ -145,8 +145,8 @@ impl ExtraMetaData { toml_dotted_bare_key_parser::parse_dotted_bare_keys(branch.as_ref()) .map_err(|e| ConfigError::WrongBranchPathOfToml(branch.clone(), e))? .iter() - .fold(Some(root), |table, key| { - table.and_then(|v| v.get(*key).and_then(|v| v.as_table())) + .try_fold(root, |table, key| { + table.get(*key).and_then(|v| v.as_table()) }) .ok_or(ConfigError::BranchPathNotFoundInToml(branch.to_string())) } else { diff --git a/src/main.rs b/src/main.rs index c766d19..1577ddb 100644 --- a/src/main.rs +++ b/src/main.rs @@ -88,7 +88,6 @@ fn run() -> Result<(), Error> { .unwrap_or_default(); let file_name = format!("{pkg_name}-{pkg_version}{pkg_release}{pkg_arch}.rpm"); - let target_file_name = determine_output_dir(args.output.as_ref(), &file_name, build_target); if let Some(parent_dir) = target_file_name.parent() { From 28a89c5a5ea146db1bcbabfcfc7b28b92f8d808c Mon Sep 17 00:00:00 2001 From: David Steele Date: Mon, 11 Sep 2023 08:04:19 +0100 Subject: [PATCH 3/4] Add AssetFileRpm ConfigError and apply markups --- src/config/file_info.rs | 27 +++++++++++++-------------- src/error.rs | 2 ++ 2 files changed, 15 insertions(+), 14 deletions(-) diff --git a/src/config/file_info.rs b/src/config/file_info.rs index 24bcd16..464ba96 100644 --- a/src/config/file_info.rs +++ b/src/config/file_info.rs @@ -152,6 +152,7 @@ impl FileInfo<'_, '_, '_, '_, '_> { fn generate_rpm_file_options( &self, dest: T, + idx: usize, ) -> Result { let mut rpm_file_option = rpm::FileOptions::new(dest.to_string()); if let Some(user) = self.user { @@ -163,18 +164,17 @@ impl FileInfo<'_, '_, '_, '_, '_> { if let Some(mode) = self.mode { rpm_file_option = rpm_file_option.mode(mode as i32); } - if let Some(caps) = self.caps { - rpm_file_option = match rpm_file_option.caps(caps) { - Ok(v) => v, - Err(_) => return Err(ConfigError::WrongType("caps".to_string(), "capability")), - }; - } if self.config { rpm_file_option = rpm_file_option.is_config(); } if self.doc { rpm_file_option = rpm_file_option.is_doc(); } + if let Some(caps) = self.caps { + rpm_file_option = rpm_file_option + .caps(caps) + .map_err(|err| ConfigError::AssetFileRpm(idx, "caps", err.to_string()))?; + } Ok(rpm_file_option.into()) } @@ -184,14 +184,13 @@ impl FileInfo<'_, '_, '_, '_, '_> { parent: P, idx: usize, ) -> Result, ConfigError> { - match self.generate_expanded_path(build_target, parent, idx) { - Ok(v) => v.into_iter().try_fold(Vec::new(), |mut acc, (src, dst)| { - let rpm_file_option = self.generate_rpm_file_options(dst)?; - acc.push((src, rpm_file_option)); - Ok(acc) - }), - Err(e) => Err(e), - } + self.generate_expanded_path(build_target, parent, idx)? + .iter() + .map(|(src, dst)| { + self.generate_rpm_file_options(dst, idx) + .map(|v| (src.clone(), v)) + }) + .collect::, _>>() } } diff --git a/src/error.rs b/src/error.rs index 28528f9..b145fd6 100644 --- a/src/error.rs +++ b/src/error.rs @@ -40,6 +40,8 @@ pub enum ConfigError { WrongBranchPathOfToml(String, #[source] DottedBareKeyLexError), #[error("Branch `{0}' not found")] BranchPathNotFoundInToml(String), + #[error("Field {1} for file {0} has the following error: {2}")] + AssetFileRpm(usize, &'static str, String), } #[derive(thiserror::Error, Debug)] From 27740ef724d2bdd561afb26ddf1b7b51aff534bc Mon Sep 17 00:00:00 2001 From: David Steele Date: Mon, 18 Sep 2023 08:08:45 +0100 Subject: [PATCH 4/4] Use RC for rpm::Error --- src/config/file_info.rs | 2 +- src/error.rs | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/config/file_info.rs b/src/config/file_info.rs index 464ba96..59c0d77 100644 --- a/src/config/file_info.rs +++ b/src/config/file_info.rs @@ -173,7 +173,7 @@ impl FileInfo<'_, '_, '_, '_, '_> { if let Some(caps) = self.caps { rpm_file_option = rpm_file_option .caps(caps) - .map_err(|err| ConfigError::AssetFileRpm(idx, "caps", err.to_string()))?; + .map_err(|err| ConfigError::AssetFileRpm(idx, "caps", err.into()))?; } Ok(rpm_file_option.into()) } diff --git a/src/error.rs b/src/error.rs index b145fd6..b369fc2 100644 --- a/src/error.rs +++ b/src/error.rs @@ -41,7 +41,7 @@ pub enum ConfigError { #[error("Branch `{0}' not found")] BranchPathNotFoundInToml(String), #[error("Field {1} for file {0} has the following error: {2}")] - AssetFileRpm(usize, &'static str, String), + AssetFileRpm(usize, &'static str, #[source] std::rc::Rc), } #[derive(thiserror::Error, Debug)]