-
Notifications
You must be signed in to change notification settings - Fork 14
contrib/packaging: New spec file #109
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,66 @@ | ||
| %bcond_without check | ||
|
|
||
| Name: bcvk | ||
| Version: 0.5.3 | ||
| Release: 1%{?dist} | ||
| Summary: Bootable container VM toolkit | ||
|
|
||
| # Apache-2.0 OR MIT | ||
| License: Apache-2.0 OR MIT | ||
| URL: https://github.com/bootc-dev/bcvk | ||
| Source0: %{url}/releases/download/v%{version}/bcvk-%{version}.tar.zstd | ||
| Source1: %{url}/releases/download/v%{version}/bcvk-%{version}-vendor.tar.zstd | ||
|
|
||
| # https://fedoraproject.org/wiki/Changes/EncourageI686LeafRemoval | ||
| ExcludeArch: %{ix86} | ||
|
|
||
| BuildRequires: make | ||
| BuildRequires: openssl-devel | ||
| BuildRequires: go-md2man | ||
| %if 0%{?rhel} | ||
| BuildRequires: rust-toolset | ||
| %else | ||
| BuildRequires: cargo-rpm-macros >= 25 | ||
| %endif | ||
|
|
||
| %description | ||
| %{summary} | ||
|
|
||
| %prep | ||
| %autosetup -p1 -a1 | ||
| # Default -v vendor config doesn't support non-crates.io deps (i.e. git) | ||
| cp .cargo/vendor-config.toml . | ||
| %cargo_prep -N | ||
| cat vendor-config.toml >> .cargo/config.toml | ||
| rm vendor-config.toml | ||
|
|
||
| %build | ||
| %cargo_build | ||
|
|
||
| make manpages | ||
|
|
||
| %cargo_vendor_manifest | ||
| # https://pagure.io/fedora-rust/rust-packaging/issue/33 | ||
| sed -i -e '/https:\/\//d' cargo-vendor.txt | ||
| %cargo_license_summary | ||
| %{cargo_license} > LICENSE.dependencies | ||
|
|
||
| %install | ||
| %make_install INSTALL="install -p -c" | ||
|
|
||
| %if %{with check} | ||
| %check | ||
| %cargo_test | ||
| %endif | ||
|
|
||
| %files | ||
| %license LICENSE-MIT | ||
| %license LICENSE-APACHE | ||
| %license LICENSE.dependencies | ||
| %license cargo-vendor.txt | ||
| %doc README.md | ||
| %{_bindir}/bcvk | ||
| %{_mandir}/man*/*bcvk* | ||
|
|
||
| %changelog | ||
| %autochangelog | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -15,6 +15,8 @@ const TASKS: &[(&str, fn(&Shell) -> Result<()>)] = &[ | |
| ("update-manpages", update_manpages), | ||
| ("sync-manpages", sync_manpages), | ||
| ("package", package), | ||
| ("package-srpm", package_srpm), | ||
| ("spec", spec), | ||
| ]; | ||
|
|
||
| fn install_tracing() { | ||
|
|
@@ -86,65 +88,254 @@ fn sync_manpages(sh: &Shell) -> Result<()> { | |
| man::sync_all_man_pages(sh) | ||
| } | ||
|
|
||
| fn package(sh: &Shell) -> Result<()> { | ||
| use std::env; | ||
| const NAME: &str = "bcvk"; | ||
| const TAR_REPRODUCIBLE_OPTS: &[&str] = &[ | ||
|
Collaborator
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. We will probably want to figure out how to keep this code in sync with other repos going forward. |
||
| "--sort=name", | ||
| "--owner=0", | ||
| "--group=0", | ||
| "--numeric-owner", | ||
| "--pax-option=exthdr.name=%d/PaxHeaders/%f,delete=atime,delete=ctime", | ||
| ]; | ||
|
|
||
| fn gitrev_to_version(v: &str) -> String { | ||
| let v = v.trim().trim_start_matches('v'); | ||
| v.replace('-', ".") | ||
| } | ||
|
|
||
| fn gitrev(sh: &Shell) -> Result<String> { | ||
| use xshell::cmd; | ||
| if let Ok(rev) = cmd!(sh, "git describe --tags --exact-match") | ||
| .ignore_stderr() | ||
| .read() | ||
| { | ||
| Ok(gitrev_to_version(&rev)) | ||
| } else { | ||
| // Just use the version from Cargo.toml | ||
| man::get_raw_package_version() | ||
| } | ||
| } | ||
|
|
||
| // Get version from Cargo.toml | ||
| let version = man::get_raw_package_version()?; | ||
| /// Return the timestamp of the latest git commit in seconds since the Unix epoch. | ||
| fn git_source_date_epoch(dir: &str) -> Result<u64> { | ||
| let o = Command::new("git") | ||
| .args(["log", "-1", "--pretty=%ct"]) | ||
| .current_dir(dir) | ||
| .output()?; | ||
| if !o.status.success() { | ||
| return Err(eyre!("git exited with an error: {:?}", o)); | ||
| } | ||
| let buf = String::from_utf8(o.stdout).context("Failed to parse git log output")?; | ||
| let r = buf.trim().parse()?; | ||
| Ok(r) | ||
| } | ||
|
|
||
| println!("Creating release archives for version {}", version); | ||
| /// When using cargo-vendor-filterer --format=tar, the config generated has a bogus source | ||
| /// directory. This edits it to refer to vendor/ as a stable relative reference. | ||
| fn edit_vendor_config(config: &str) -> Result<String> { | ||
| let mut config: toml::Value = toml::from_str(config)?; | ||
| let config = config | ||
| .as_table_mut() | ||
| .ok_or_else(|| eyre!("Vendor config is not a table"))?; | ||
| let source_table = config | ||
| .get_mut("source") | ||
| .ok_or_else(|| eyre!("Missing 'source' key in vendor config"))? | ||
| .as_table_mut() | ||
| .ok_or_else(|| eyre!("'source' is not a table"))?; | ||
| let vendored_sources = source_table | ||
| .get_mut("vendored-sources") | ||
| .ok_or_else(|| eyre!("Missing 'vendored-sources' key"))? | ||
| .as_table_mut() | ||
| .ok_or_else(|| eyre!("'vendored-sources' is not a table"))?; | ||
| let previous = | ||
| vendored_sources.insert("directory".into(), toml::Value::String("vendor".into())); | ||
| if previous.is_none() { | ||
| return Err(eyre!("Expected 'directory' key in vendored-sources")); | ||
| } | ||
|
|
||
| // Get the git commit timestamp for reproducible builds | ||
| let source_date_epoch = cmd!(sh, "git log -1 --format=%ct").read()?; | ||
| env::set_var("SOURCE_DATE_EPOCH", source_date_epoch.trim()); | ||
| Ok(config.to_string()) | ||
| } | ||
|
|
||
| // Create target directory if it doesn't exist | ||
| sh.create_dir("target")?; | ||
| struct Package { | ||
| version: String, | ||
| srcpath: camino::Utf8PathBuf, | ||
| vendorpath: camino::Utf8PathBuf, | ||
| } | ||
|
|
||
| // Create temporary directory for intermediate files | ||
| let tempdir = tempfile::tempdir()?; | ||
| let temp_tar = tempdir.path().join(format!("bcvk-{}.tar", version)); | ||
| fn impl_package(sh: &Shell) -> Result<Package> { | ||
| use camino::Utf8Path; | ||
| use xshell::cmd; | ||
|
|
||
| // Create source archive using git archive (uncompressed initially) | ||
| let source_archive = format!("target/bcvk-{}.tar.zstd", version); | ||
| cmd!( | ||
| sh, | ||
| "git archive --format=tar --prefix=bcvk-{version}/ HEAD -o {temp_tar}" | ||
| ) | ||
| .run()?; | ||
| let source_date_epoch = git_source_date_epoch(".")?; | ||
| let v = gitrev(sh)?; | ||
|
|
||
| // Create vendor archive | ||
| let vendor_archive = format!("target/bcvk-{}-vendor.tar.zstd", version); | ||
| cmd!( | ||
| let namev = format!("{NAME}-{v}"); | ||
| let p = Utf8Path::new("target").join(format!("{namev}.tar")); | ||
| let prefix = format!("{namev}/"); | ||
| cmd!(sh, "git archive --format=tar --prefix={prefix} -o {p} HEAD").run()?; | ||
| // Generate the vendor directory now, as we want to embed the generated config to use | ||
| // it in our source. | ||
| let vendorpath = Utf8Path::new("target").join(format!("{namev}-vendor.tar.zstd")); | ||
| let vendor_config = cmd!( | ||
| sh, | ||
| "cargo vendor-filterer --format=tar.zstd {vendor_archive}" | ||
| "cargo vendor-filterer --prefix=vendor --format=tar.zstd {vendorpath}" | ||
| ) | ||
| .run()?; | ||
| .read()?; | ||
| let vendor_config = edit_vendor_config(&vendor_config)?; | ||
| // Append .cargo/vendor-config.toml (a made up filename) into the tar archive. | ||
| { | ||
| let tmpdir = tempfile::tempdir_in("target")?; | ||
| let tmpdir_path = tmpdir.path(); | ||
| let path = tmpdir_path.join("vendor-config.toml"); | ||
| std::fs::write(&path, vendor_config)?; | ||
| let source_date_epoch = format!("{source_date_epoch}"); | ||
| cmd!( | ||
| sh, | ||
| "tar -r -C {tmpdir_path} {TAR_REPRODUCIBLE_OPTS...} --mtime=@{source_date_epoch} --transform=s,^,{prefix}.cargo/, -f {p} vendor-config.toml" | ||
| ) | ||
| .run()?; | ||
| } | ||
| // Compress with zstd | ||
| let srcpath: camino::Utf8PathBuf = format!("{p}.zstd").into(); | ||
| cmd!(sh, "zstd --rm -f {p} -o {srcpath}").run()?; | ||
|
|
||
| println!("Created vendor archive: {}", vendor_archive); | ||
| Ok(Package { | ||
| version: v, | ||
| srcpath, | ||
| vendorpath, | ||
| }) | ||
| } | ||
|
|
||
| // Create vendor config for the source archive | ||
| let vendor_config_content = r#"[source.crates-io] | ||
| replace-with = "vendored-sources" | ||
| fn package(sh: &Shell) -> Result<()> { | ||
| let p = impl_package(sh)?.srcpath; | ||
| println!("Generated: {p}"); | ||
| Ok(()) | ||
| } | ||
|
|
||
| [source.vendored-sources] | ||
| directory = "vendor" | ||
| "#; | ||
| let vendor_config_path = tempdir.path().join(".cargo-vendor-config.toml"); | ||
| std::fs::write(&vendor_config_path, vendor_config_content)?; | ||
| fn update_spec(sh: &Shell) -> Result<camino::Utf8PathBuf> { | ||
| use camino::Utf8Path; | ||
| use std::fs::File; | ||
| use std::io::{BufRead, BufReader, BufWriter, Write}; | ||
|
|
||
| // Add vendor config to source archive | ||
| cmd!(sh, "tar --owner=0 --group=0 --numeric-owner --sort=name --mtime=@{source_date_epoch} -rf {temp_tar} --transform='s|.*/.cargo-vendor-config.toml|bcvk-{version}/.cargo/vendor-config.toml|' {vendor_config_path}").run()?; | ||
| let p = Utf8Path::new("target"); | ||
| let pkg = impl_package(sh)?; | ||
|
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. The |
||
| let srcpath = pkg.srcpath.file_name().unwrap(); | ||
| let v = pkg.version; | ||
| let src_vendorpath = pkg.vendorpath.file_name().unwrap(); | ||
| { | ||
| let specin = File::open(format!("contrib/packaging/{NAME}.spec")) | ||
| .map(BufReader::new) | ||
| .context("Opening spec")?; | ||
| let mut o = File::create(p.join(format!("{NAME}.spec"))).map(BufWriter::new)?; | ||
| for line in specin.lines() { | ||
| let line = line?; | ||
| if line.starts_with("Version:") { | ||
| writeln!(o, "# Replaced by cargo xtask spec")?; | ||
| writeln!(o, "Version: {v}")?; | ||
| } else if line.starts_with("Source0") { | ||
| writeln!(o, "Source0: {srcpath}")?; | ||
| } else if line.starts_with("Source1") { | ||
| writeln!(o, "Source1: {src_vendorpath}")?; | ||
| } else { | ||
| writeln!(o, "{line}")?; | ||
| } | ||
| } | ||
| } | ||
| let spec_path = p.join(format!("{NAME}.spec")); | ||
| Ok(spec_path) | ||
| } | ||
|
|
||
| // Compress the final source archive | ||
| cmd!(sh, "zstd {temp_tar} -f -o {source_archive}").run()?; | ||
| fn spec(sh: &Shell) -> Result<()> { | ||
| let s = update_spec(sh)?; | ||
| println!("Generated: {s}"); | ||
| Ok(()) | ||
| } | ||
|
|
||
| println!("Created source archive: {}", source_archive); | ||
| fn impl_srpm(sh: &Shell) -> Result<camino::Utf8PathBuf> { | ||
| use camino::Utf8Path; | ||
| use std::fs::File; | ||
| use std::io::{BufRead, BufReader, BufWriter, Write}; | ||
| use xshell::cmd; | ||
|
|
||
| println!("Release archives created successfully:"); | ||
| println!(" Source: {}", source_archive); | ||
| println!(" Vendor: {}", vendor_archive); | ||
| { | ||
| let _g = sh.push_dir("target"); | ||
| for name in sh.read_dir(".")? { | ||
| if let Some(name) = name.to_str() { | ||
| if name.ends_with(".src.rpm") { | ||
| sh.remove_path(name)?; | ||
| } | ||
| } | ||
| } | ||
| } | ||
| let pkg = impl_package(sh)?; | ||
| let td = tempfile::tempdir_in("target").context("Allocating tmpdir")?; | ||
| let td = td.keep(); | ||
| let td: &Utf8Path = td | ||
| .as_path() | ||
| .try_into() | ||
| .context("Converting tmpdir path to UTF-8")?; | ||
| let srcpath = &pkg.srcpath; | ||
| cmd!(sh, "mv {srcpath} {td}").run()?; | ||
| let v = pkg.version; | ||
| let src_vendorpath = &pkg.vendorpath; | ||
| cmd!(sh, "mv {src_vendorpath} {td}").run()?; | ||
| let srcpath_filename = pkg.srcpath.file_name().unwrap(); | ||
| let vendorpath_filename = pkg.vendorpath.file_name().unwrap(); | ||
| { | ||
| let specin = File::open(format!("contrib/packaging/{NAME}.spec")) | ||
| .map(BufReader::new) | ||
| .context("Opening spec")?; | ||
| let mut o = File::create(td.join(format!("{NAME}.spec"))).map(BufWriter::new)?; | ||
| for line in specin.lines() { | ||
| let line = line?; | ||
| if line.starts_with("Version:") { | ||
| writeln!(o, "# Replaced by cargo xtask package-srpm")?; | ||
| writeln!(o, "Version: {v}")?; | ||
| } else if line.starts_with("Source0") { | ||
| writeln!(o, "Source0: {srcpath_filename}")?; | ||
| } else if line.starts_with("Source1") { | ||
| writeln!(o, "Source1: {vendorpath_filename}")?; | ||
| } else { | ||
| writeln!(o, "{line}")?; | ||
| } | ||
| } | ||
| } | ||
| let d = sh.push_dir(td); | ||
| let mut cmd = cmd!(sh, "rpmbuild"); | ||
| for k in [ | ||
| "_sourcedir", | ||
| "_specdir", | ||
| "_builddir", | ||
| "_srcrpmdir", | ||
| "_rpmdir", | ||
| ] { | ||
| cmd = cmd.arg("--define"); | ||
| cmd = cmd.arg(format!("{k} {td}")); | ||
| } | ||
| cmd.arg("--define") | ||
| .arg(format!("_buildrootdir {td}/.build")) | ||
| .args(["-bs", &format!("{NAME}.spec")]) | ||
| .run()?; | ||
| drop(d); | ||
| let mut srpm = None; | ||
| for e in std::fs::read_dir(td)? { | ||
| let e = e?; | ||
| let n = e.file_name(); | ||
| let Some(n) = n.to_str() else { | ||
| continue; | ||
| }; | ||
| if n.ends_with(".src.rpm") { | ||
| srpm = Some(Utf8Path::new(td).join(n)); | ||
| break; | ||
| } | ||
| } | ||
| let srpm = srpm.ok_or_else(|| eyre!("Failed to find generated .src.rpm"))?; | ||
| let dest = Utf8Path::new("target").join(srpm.file_name().unwrap()); | ||
| std::fs::rename(&srpm, &dest)?; | ||
| Ok(dest) | ||
| } | ||
|
|
||
| fn package_srpm(sh: &Shell) -> Result<()> { | ||
| let srpm = impl_srpm(sh)?; | ||
| println!("Generated: {srpm}"); | ||
| Ok(()) | ||
| } | ||
Uh oh!
There was an error while loading. Please reload this page.