A pure rust library for parsing and creating RPM files.
- Easy to use API
- Pure rust to make it easy to use in larger Projects
- Independence of Spec files. Pure programmatic interface for Packaging.
- Compatibility from Enterprise Linux 8 (RHEL, Alma, Rocky, CentOS Stream) to Fedora (I may extend test cases for SUSE)
RPM has a lot of cryptic features. I do not want to re-implement all of them. This library focuses on the ones that I assume as useful. This library does not build software like rpmbuild. It is meant for finished artifacts that need to be packaged as RPM.
- RPM Creation
- Basic RPM Reading
- RPM Signing and Signature Verification
- High Level API for RPM Reading
use rpm::signature::pgp::{Signer, Verifier};
let pkg = rpm::Package::open("tests/assets/RPMS/v6/noarch/rpm-basic-2.3.4-5.el9.noarch.rpm")?;
let name = pkg.metadata.get_name()?;
let version = pkg.metadata.get_version()?;
let release = pkg.metadata.get_release()?;
let arch = pkg.metadata.get_arch()?;
println!("{}-{}-{}.{}", name, version, release, arch);
for changelog in pkg.metadata.get_changelog_entries()? {
println!("{}\n{}\n", changelog.name, changelog.description);
}use rpm::signature::pgp::{Signer, Verifier};
let raw_secret_key = std::fs::read("./tests/assets/signing_keys/v6/rpm-testkey-v6-rsa4k.secret")?;
let raw_pub_key = std::fs::read("./tests/assets/signing_keys/v6/rpm-testkey-v6-rsa4k.asc")?;
let mut pkg = rpm::Package::open("./tests/assets/RPMS/v6/signed/rpm-basic-with-rsa4k-2.3.4-5.el9.noarch.rpm")?;
pkg.sign(&raw_secret_key)?;
pkg.write_file("./with_signature.rpm")?;
let pkg = rpm::Package::open("./with_signature.rpm")?;
pkg.verify_signature(Verifier::load_from_asc_bytes(&raw_pub_key)?)?;use rpm::signature::pgp::Signer;
let raw_secret_key = std::fs::read("./tests/assets/signing_keys/v6/rpm-testkey-v6-ed25519.secret")?;
let subkey_fingerprint = hex::decode("1F9A6321E1C5B4600BC2F6D8130FD47580C5CC7701DD8BE59983C1F79325EBF9")?;
let signer = Signer::load_from_asc_bytes(&raw_secret_key)?
.with_signing_key(&subkey_fingerprint)?;
let mut pkg = rpm::Package::open("./tests/assets/RPMS/v6/noarch/rpm-basic-2.3.4-5.el9.noarch.rpm")?;
pkg.sign(signer)?;use rpm::signature::pgp::Signer;
let build_config = rpm::BuildConfig::default().compression(rpm::CompressionType::Gzip);
let raw_secret_key = std::fs::read("./tests/assets/signing_keys/secret_ed25519.asc")?;
// It's recommended to use timestamp of last commit in your VCS
let source_date = 1_600_000_000;
let pkg = rpm::PackageBuilder::new("test", "1.0.0", "MIT", "x86_64", "some awesome package")
.using_config(build_config)
// set default ownership and permissions for files and directories, similar to %defattr
// in an RPM spec file. Pass None for any field to leave it unchanged (like `-` in %defattr).
.default_file_attrs(Some(0o644), Some("myuser".into()), Some("mygroup".into()))
.default_dir_attrs(Some(0o755), Some("myuser".into()), Some("mygroup".into()))
// add a file with no special options
// by default, files will be owned by the "root" user and group, and inherit their permissions
// from the on-disk file.
.with_file(
"./tests/assets/SOURCES/multiplication_tables.py",
rpm::FileOptions::new("/usr/bin/awesome"),
)?
// you can set permissions, capabilities and other metadata (user, group, etc.) manually
.with_file(
"./tests/assets/SOURCES/example_config.toml",
rpm::FileOptions::new("/etc/awesome/second.toml")
.permissions(0o644)
.caps("cap_sys_admin,cap_net_admin=pe")?
.user("hugo"),
)?
// Add a file - setting flags on it equivalent to `%config(noreplace)`
.with_file(
"./tests/assets/SOURCES/example_config.toml",
rpm::FileOptions::new("/etc/awesome/config.toml")
.config().noreplace(),
)?
// symlinks don't require a source file
.with_symlink(
rpm::FileOptions::symlink("/usr/bin/awesome_link", "/usr/bin/awesome"),
)?
// directories can be created with explicit ownership and permissions
// this does not add any directory contents, just declares a directory
.with_dir_entry(
rpm::FileOptions::dir("/var/log/awesome").permissions(0o750),
)?
// ghost files / directories are not included in the package payload, but their metadata
// (ownership, permissions, etc.) is tracked by RPM. This is commonly used for files
// created at runtime (e.g. log files, PID files).
.with_ghost(
rpm::FileOptions::ghost("/var/log/awesome/app.log"),
)?
.pre_install_script("echo preinst")
// Alternatively, use scriptlet builder api to specify flags and interpreter/arguments
.post_trans_script(
Scriptlet::new("echo posttrans")
.flags(ScriptletFlags::EXPAND)
.prog(vec!["/bin/blah/bash", "-c"])
)
// If you don't need reproducible builds, you can remove the following line
.source_date(source_date)
.build_host(gethostname::gethostname().to_str().unwrap_or("host"))
.add_changelog_entry(
"Max Mustermann <max@example.com> - 0.1-29",
"- was awesome, eh?",
chrono::DateTime::parse_from_rfc2822("Wed, 19 Apr 2023 23:16:09 GMT")
.expect("Date 1 is correct. qed"),
)
.add_changelog_entry(
"Charlie Yom <test2@example.com> - 0.1-28",
"- yeah, it was",
// Raw timestamp for 1996-08-14 05:20:00
840_000_000,
)
.requires(rpm::Dependency::any("wget"))
.vendor("corporation or individual")
.url("www.github.com/repo")
.vcs("git:repo=example_repo:branch=example_branch:sha=example_sha")
.build_and_sign(Signer::load_from_asc_bytes(&raw_secret_key)?)?;
pkg.write_file("./awesome.rpm")?;