Skip to content

Commit

Permalink
Remove RPM prefix from type names
Browse files Browse the repository at this point in the history
  • Loading branch information
newpavlov committed Jun 23, 2023
1 parent 9498e02 commit 765809c
Show file tree
Hide file tree
Showing 16 changed files with 278 additions and 272 deletions.
11 changes: 8 additions & 3 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -8,12 +8,17 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0

## Breaking Change

- The `RPMBuilder::build_time` method is removed. Package build time is now included by default and can be clamped using the `RPMBuilder::source_date` method.
- Removed `RPM` prefix from type names, e.g. `RPMPackage` is renamed to `Package`.
- `RPMBuilder` is renamed to `PackageBuilder`.
- The `PackageBuilder::build_time` method is removed. Package build time is now
included by default and can be clamped using the `PackageBuilder::source_date` method.

## Added

- `RPMBuilder::source_date` method for clamping modification time of files, build time of the package, and signature time. This functionality is required for reproducible generation of packages.
- `RPMPackage::sign_with_timestamp` method.
- `PackageBuilder::source_date` method for clamping modification time of files,
build time of the package, and signature time. This functionality is required for
reproducible generation of packages.
- `Package::sign_with_timestamp` method.
- Package build time is now included by default.

### Fixed
Expand Down
10 changes: 5 additions & 5 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -34,21 +34,21 @@ use rpm::signature::pgp::{Signer, Verifier};
let raw_secret_key = std::fs::read("./test_assets/secret_key.asc")?;
// It's recommended to use timestamp of last commit in your VCS
let source_date = 1_600_000_000;
let pkg = rpm::RPMBuilder::new("test", "1.0.0", "MIT", "x86_64", "some awesome package")
let pkg = rpm::PackageBuilder::new("test", "1.0.0", "MIT", "x86_64", "some awesome package")
.compression(rpm::CompressionType::Gzip)
.with_file(
"./test_assets/awesome.toml",
rpm::RPMFileOptions::new("/etc/awesome/config.toml").is_config(),
rpm::FileOptions::new("/etc/awesome/config.toml").is_config(),
)?
// file mode is inherited from source file
.with_file(
"./test_assets/awesome.py",
rpm::RPMFileOptions::new("/usr/bin/awesome"),
rpm::FileOptions::new("/usr/bin/awesome"),
)?
.with_file(
"./test_assets/awesome.toml",
// you can set a custom mode and custom user too
rpm::RPMFileOptions::new("/etc/awesome/second.toml")
rpm::FileOptions::new("/etc/awesome/second.toml")
.mode(rpm::FileMode::regular(0o644))
.user("hugo"),
)?
Expand Down Expand Up @@ -79,7 +79,7 @@ pkg.write_file("./awesome.rpm")?;

// reading
let raw_pub_key = std::fs::read("/path/to/gpg.key.pub")?;
let pkg = rpm::RPMPackage::open("test_assets/389-ds-base-devel-1.3.8.4-15.el7.x86_64.rpm")?;
let pkg = rpm::Package::open("test_assets/389-ds-base-devel-1.3.8.4-15.el7.x86_64.rpm")?;

let name = pkg.metadata.get_name()?;
let version = pkg.metadata.get_version()?;
Expand Down
12 changes: 6 additions & 6 deletions src/errors.rs
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ use crate::{DigestAlgorithm, TimestampError};

#[derive(Error, Debug)]
#[non_exhaustive]
pub enum RPMError {
pub enum Error {
#[error(transparent)]
Io(#[from] io::Error),

Expand Down Expand Up @@ -87,19 +87,19 @@ pub enum RPMError {
TimestampConv(TimestampError),
}

impl From<nom::Err<(&[u8], nom::error::ErrorKind)>> for RPMError {
impl From<nom::Err<(&[u8], nom::error::ErrorKind)>> for Error {
fn from(error: nom::Err<(&[u8], nom::error::ErrorKind)>) -> Self {
match error {
nom::Err::Error((_, kind)) | nom::Err::Failure((_, kind)) => {
RPMError::Nom(kind.description().to_string())
Error::Nom(kind.description().to_string())
}
nom::Err::Incomplete(_) => RPMError::Nom("unhandled incomplete".to_string()),
nom::Err::Incomplete(_) => Error::Nom("unhandled incomplete".to_string()),
}
}
}

impl From<TimestampError> for RPMError {
impl From<TimestampError> for Error {
fn from(error: TimestampError) -> Self {
RPMError::TimestampConv(error)
Error::TimestampConv(error)
}
}
10 changes: 5 additions & 5 deletions src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -22,21 +22,21 @@
//! let raw_secret_key = std::fs::read("./test_assets/secret_key.asc")?;
//! // It's recommended to use timestamp of last commit in your VCS
//! let source_date = 1_600_000_000;
//! let pkg = rpm::RPMBuilder::new("test", "1.0.0", "MIT", "x86_64", "some awesome package")
//! let pkg = rpm::PackageBuilder::new("test", "1.0.0", "MIT", "x86_64", "some awesome package")
//! .compression(rpm::CompressionType::Gzip)
//! .with_file(
//! "./test_assets/awesome.toml",
//! rpm::RPMFileOptions::new("/etc/awesome/config.toml").is_config(),
//! rpm::FileOptions::new("/etc/awesome/config.toml").is_config(),
//! )?
//! // file mode is inherited from source file
//! .with_file(
//! "./test_assets/awesome.py",
//! rpm::RPMFileOptions::new("/usr/bin/awesome"),
//! rpm::FileOptions::new("/usr/bin/awesome"),
//! )?
//! .with_file(
//! "./test_assets/awesome.toml",
//! // you can set a custom mode and custom user too
//! rpm::RPMFileOptions::new("/etc/awesome/second.toml")
//! rpm::FileOptions::new("/etc/awesome/second.toml")
//! .mode(rpm::FileMode::regular(0o644))
//! .user("hugo"),
//! )?
Expand Down Expand Up @@ -67,7 +67,7 @@
//!
//! // reading
//! let raw_pub_key = std::fs::read("test_assets/public_key.asc")?;
//! let pkg = rpm::RPMPackage::open("/tmp/awesome.rpm")?;
//! let pkg = rpm::Package::open("/tmp/awesome.rpm")?;
//! // verifying
//! pkg.verify_signature(Verifier::load_from_asc_bytes(&raw_pub_key)?)?;
//! # }
Expand Down
76 changes: 37 additions & 39 deletions src/rpm/builder.rs
Original file line number Diff line number Diff line change
Expand Up @@ -22,23 +22,23 @@ use crate::{constants::*, Timestamp};
#[cfg(feature = "signature-meta")]
use crate::signature;

use crate::RPMPackage;
use crate::RPMPackageMetadata;
use crate::Package;
use crate::PackageMetadata;
use crate::{CompressionType, CompressionWithLevel, Digests};

#[cfg(unix)]
fn file_mode(file: &fs::File) -> Result<u32, RPMError> {
fn file_mode(file: &fs::File) -> Result<u32, Error> {
Ok(file.metadata()?.permissions().mode())
}

#[cfg(windows)]
fn file_mode(_file: &fs::File) -> Result<u32, RPMError> {
fn file_mode(_file: &fs::File) -> Result<u32, Error> {
Ok(0)
}

/// Create an RPM file by specifying metadata and files using the builder pattern.
#[derive(Default)]
pub struct RPMBuilder {
pub struct PackageBuilder {
name: String,
epoch: u32,
version: String,
Expand All @@ -52,7 +52,7 @@ pub struct RPMBuilder {
// File entries need to be sorted. The entries need to be in the same order as they come
// in the cpio payload. Otherwise rpm will not be able to resolve those paths.
// key is the directory, values are complete paths
files: BTreeMap<String, RPMFileEntry>,
files: BTreeMap<String, PackageFileEntry>,
directories: BTreeSet<String>,
requires: Vec<Dependency>,
obsoletes: Vec<Dependency>,
Expand Down Expand Up @@ -84,7 +84,7 @@ pub struct RPMBuilder {
build_host: Option<String>,
}

impl RPMBuilder {
impl PackageBuilder {
/// Create a new package, providing the required metadata.
///
/// Additional metadata is added using the builder pattern. However `name`, `version`, `license`,
Expand All @@ -99,12 +99,12 @@ impl RPMBuilder {
///
/// ```
/// # fn foo() -> Result<(), Box<dyn std::error::Error>> {
/// let pkg = rpm::RPMBuilder::new("foo", "1.0.0", "Apache-2.0", "x86_64", "some baz package").build();
/// let pkg = rpm::PackageBuilder::new("foo", "1.0.0", "Apache-2.0", "x86_64", "some baz package").build();
/// # Ok(())
/// # }
/// ```
pub fn new(name: &str, version: &str, license: &str, arch: &str, desc: &str) -> Self {
RPMBuilder {
Self {
name: name.to_string(),
epoch: 0,
version: version.to_string(),
Expand Down Expand Up @@ -141,7 +141,7 @@ impl RPMBuilder {
///
/// ```
/// # fn foo() -> Result<(), Box<dyn std::error::Error>> {
/// let pkg = rpm::RPMBuilder::new("foo", "1.0.0", "MPL-2.0", "x86_64", "some bar package")
/// let pkg = rpm::PackageBuilder::new("foo", "1.0.0", "MPL-2.0", "x86_64", "some bar package")
/// .build_host(gethostname::gethostname().to_str().ok_or("Funny hostname")?)
/// .build()?;
/// # Ok(())
Expand All @@ -162,7 +162,7 @@ impl RPMBuilder {
/// // It's recommended to use timestamp of last commit in your VCS
/// let source_date = 1_600_000_000;
/// // Do not forget
/// let pkg = rpm::RPMBuilder::new("foo", "1.0.0", "MPL-2.0", "x86_64", "some bar package")
/// let pkg = rpm::PackageBuilder::new("foo", "1.0.0", "MPL-2.0", "x86_64", "some bar package")
/// .source_date(source_date)
/// .build()?;
/// # Ok(())
Expand All @@ -189,7 +189,7 @@ impl RPMBuilder {
/// ```
/// # fn foo() -> Result<(), Box<dyn std::error::Error>> {
///
/// let pkg = rpm::RPMBuilder::new("foo", "1.0.0", "MIT", "x86_64", "some baz package")
/// let pkg = rpm::PackageBuilder::new("foo", "1.0.0", "MIT", "x86_64", "some baz package")
/// .compression(rpm::CompressionType::Gzip)
/// .build()?;
/// # Ok(())
Expand All @@ -201,7 +201,7 @@ impl RPMBuilder {
/// ```
/// # fn foo() -> Result<(), Box<dyn std::error::Error>> {
///
/// let pkg = rpm::RPMBuilder::new("foo", "1.0.0", "MIT", "x86_64", "some baz package")
/// let pkg = rpm::PackageBuilder::new("foo", "1.0.0", "MIT", "x86_64", "some baz package")
/// .compression(rpm::CompressionWithLevel::Zstd(3))
/// .build()?;
/// # Ok(())
Expand All @@ -228,7 +228,7 @@ impl RPMBuilder {
/// # #[cfg(feature = "chrono")]
/// # || -> Result<(), Box<dyn std::error::Error>> {
///
/// let pkg = rpm::RPMBuilder::new("foo", "1.0.0", "Apache-2.0", "x86_64", "some baz package")
/// let pkg = rpm::PackageBuilder::new("foo", "1.0.0", "Apache-2.0", "x86_64", "some baz package")
/// .add_changelog_entry(
/// "Alfred J. Quack <[email protected]> - 0.1-27",
/// r#" - Obsolete `fn foo`, in favor of `fn bar`.
Expand Down Expand Up @@ -261,20 +261,20 @@ impl RPMBuilder {
/// ```
/// # fn foo() -> Result<(), Box<dyn std::error::Error>> {
///
/// let pkg = rpm::RPMBuilder::new("foo", "1.0.0", "Apache-2.0", "x86_64", "some baz package")
/// let pkg = rpm::PackageBuilder::new("foo", "1.0.0", "Apache-2.0", "x86_64", "some baz package")
/// .with_file(
/// "./awesome-config.toml",
/// rpm::RPMFileOptions::new("/etc/awesome/config.toml").is_config(),
/// rpm::FileOptions::new("/etc/awesome/config.toml").is_config(),
/// )?
/// // file mode is inherited from source file
/// .with_file(
/// "./awesome-bin",
/// rpm::RPMFileOptions::new("/usr/bin/awesome"),
/// rpm::FileOptions::new("/usr/bin/awesome"),
/// )?
/// .with_file(
/// "./awesome-config.toml",
/// // you can set a custom mode and custom user too
/// rpm::RPMFileOptions::new("/etc/awesome/second.toml").mode(0o100744).user("hugo"),
/// rpm::FileOptions::new("/etc/awesome/second.toml").mode(0o100744).user("hugo"),
/// )?
/// .build()?;
/// # Ok(())
Expand All @@ -283,8 +283,8 @@ impl RPMBuilder {
pub fn with_file(
mut self,
source: impl AsRef<Path>,
options: impl Into<RPMFileOptions>,
) -> Result<Self, RPMError> {
options: impl Into<FileOptions>,
) -> Result<Self, Error> {
let mut input = fs::File::open(source)?;
let mut content = Vec::new();
input.read_to_end(&mut content)?;
Expand All @@ -303,24 +303,22 @@ impl RPMBuilder {
&mut self,
content: Vec<u8>,
modified_at: Timestamp,
options: RPMFileOptions,
) -> Result<(), RPMError> {
options: FileOptions,
) -> Result<(), Error> {
let dest = options.destination;
if !dest.starts_with("./") && !dest.starts_with('/') {
return Err(RPMError::InvalidDestinationPath {
return Err(Error::InvalidDestinationPath {
path: dest,
desc: "invalid start, expected / or ./",
});
}

let pb = PathBuf::from(dest.clone());

let parent = pb
.parent()
.ok_or_else(|| RPMError::InvalidDestinationPath {
path: dest.clone(),
desc: "no parent directory found",
})?;
let parent = pb.parent().ok_or_else(|| Error::InvalidDestinationPath {
path: dest.clone(),
desc: "no parent directory found",
})?;

let (cpio_path, dir) = if dest.starts_with('.') {
(
Expand All @@ -339,7 +337,7 @@ impl RPMBuilder {
hasher.update(&content);
let hash_result = hasher.finalize();
let sha_checksum = hex::encode(hash_result); // encode as string
let entry = RPMFileEntry {
let entry = PackageFileEntry {
// file_name() should never fail because we've checked the special cases already
base_name: pb.file_name().unwrap().to_string_lossy().to_string(),
size: content.len() as u64,
Expand Down Expand Up @@ -461,7 +459,7 @@ impl RPMBuilder {
/// build without a signature
///
/// ignores a present key, if any
pub fn build(self) -> Result<RPMPackage, RPMError> {
pub fn build(self) -> Result<Package, Error> {
let (lead, header_idx_tag, content) = self.prepare_data()?;

let mut header = Vec::with_capacity(128);
Expand All @@ -475,7 +473,7 @@ impl RPMBuilder {
header_and_content_digest: header_and_content_digest_md5,
header_digest_sha1,
header_digest_sha256,
} = RPMPackage::create_sig_header_digests(header.as_slice(), content.as_slice())?;
} = Package::create_sig_header_digests(header.as_slice(), content.as_slice())?;

Header::<IndexSignatureTag>::builder()
.add_digest(
Expand All @@ -486,20 +484,20 @@ impl RPMBuilder {
.build(header_and_content_len)
};

let metadata = RPMPackageMetadata {
let metadata = PackageMetadata {
lead,
signature: digest_header,
header: header_idx_tag,
};
let pkg = RPMPackage { metadata, content };
let pkg = Package { metadata, content };
Ok(pkg)
}

/// use an external signer to sing and build
///
/// See `signature::Signing` for more details.
#[cfg(feature = "signature-meta")]
pub fn build_and_sign<S>(self, signer: S) -> Result<RPMPackage, RPMError>
pub fn build_and_sign<S>(self, signer: S) -> Result<Package, Error>
where
S: signature::Signing<signature::algorithm::RSA>,
{
Expand All @@ -516,7 +514,7 @@ impl RPMBuilder {
header_and_content_digest: header_and_content_digest_md5,
header_digest_sha1,
header_digest_sha256,
} = RPMPackage::create_sig_header_digests(header.as_slice(), content.as_slice())?;
} = Package::create_sig_header_digests(header.as_slice(), content.as_slice())?;

let builder = Header::<IndexSignatureTag>::builder().add_digest(
header_digest_sha1.as_str(),
Expand All @@ -543,19 +541,19 @@ impl RPMBuilder {
.build(header_and_content_len)
};

let metadata = RPMPackageMetadata {
let metadata = PackageMetadata {
lead,
signature: signature_header,
header: header_idx_tag,
};
let pkg = RPMPackage { metadata, content };
let pkg = Package { metadata, content };
Ok(pkg)
}

/// prepare all rpm headers including content
///
/// @todo split this into multiple `fn`s, one per `IndexTag`-group.
fn prepare_data(mut self) -> Result<(Lead, Header<IndexTag>, Vec<u8>), RPMError> {
fn prepare_data(mut self) -> Result<(Lead, Header<IndexTag>, Vec<u8>), Error> {
// signature depends on header and payload. So we build these two first.
// then the signature. Then we stitch all together.
// Lead is not important. just build it here
Expand Down
Loading

0 comments on commit 765809c

Please sign in to comment.