Skip to content

Commit b274b31

Browse files
committed
Add cargo xtask and packaging infrastructure
First, this adds `cargo xtask` following https://github.com/matklad/cargo-xtask/ We use this to write "external glue scripts" in Rust, not bash. Specifically we now have e.g. `cargo xtask vendor` which just wraps running `cargo vendor-filterer`. Then build on that and add `cargo xtask package-srpm` which generates a `.src.rpm`. And build on that by adding the requisite glue to have Fedora's COPR be able to understand it, so that we can get auto-built and shipped packages there. This will make trying out bootc a bit easier. Signed-off-by: Colin Walters <[email protected]>
1 parent 51aef4d commit b274b31

File tree

9 files changed

+318
-3
lines changed

9 files changed

+318
-3
lines changed

.cargo/config.toml

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
[alias]
2+
xtask = "run --package xtask --"

.copr/Makefile

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
srpm:
2+
dnf -y install cargo git openssl-devel
3+
# similar to https://github.com/actions/checkout/issues/760, but for COPR
4+
git config --global --add safe.directory '*'
5+
cargo install cargo-vendor-filterer
6+
cargo xtask package-srpm
7+
mv target/*.src.rpm $$outdir

.github/workflows/packaging.yml

Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,36 @@
1+
# This is an unused WIP. Maybe we'll use it in the future
2+
# name: Packaging
3+
4+
# permissions:
5+
# actions: read
6+
7+
# on:
8+
# push:
9+
# branches: [main]
10+
# pull_request:
11+
# branches: [main]
12+
# types: [labeled, opened, synchronize, reopened]
13+
# workflow_dispatch: {}
14+
15+
# jobs:
16+
# srpm:
17+
# if: ${{ contains(github.event.pull_request.labels.*.name, 'ci/full') }}
18+
# runs-on: ubuntu-latest
19+
# container: quay.io/coreos-assembler/fcos-buildroot:testing-devel
20+
# steps:
21+
# - uses: actions/checkout@v3
22+
# - name: Mark git checkout as safe
23+
# run: git config --global --add safe.directory "$GITHUB_WORKSPACE"
24+
# - name: Cache Dependencies
25+
# uses: Swatinem/rust-cache@v2
26+
# with:
27+
# key: "srpm"
28+
# - name: Install vendor tool
29+
# run: cargo install cargo-vendor-filterer
30+
# - name: Build
31+
# run: cargo xtask package-srpm
32+
# - name: Upload
33+
# uses: actions/upload-artifact@v2
34+
# with:
35+
# name: bootc-srpm
36+
# path: target/*.src.rpm

Cargo.toml

Lines changed: 15 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,21 @@
11
[workspace]
2-
members = ["cli", "lib"]
2+
members = ["cli", "lib", "xtask"]
33

44
[profile.dev]
55
opt-level = 1 # No optimizations are too slow for us.
66

77
[profile.release]
8-
lto = "thin"
8+
# RPMs/debs/etc want debuginfo by default
9+
debug = true
10+
11+
# See https://github.com/coreos/cargo-vendor-filterer
12+
[workspace.metadata.vendor-filter]
13+
platforms = ["x86_64-unknown-linux-gnu", "aarch64-unknown-linux-gnu", "s390x-unknown-linux-gnu"]
14+
all-features = true
15+
exclude-crate-paths = [ { name = "libz-sys", exclude = "src/zlib" },
16+
{ name = "libz-sys", exclude = "src/zlib-ng" },
17+
# rustix includes pre-generated assembly for linux_raw, which we don't use
18+
{ name = "rustix", exclude = "src/imp/linux_raw" },
19+
# Test files that include binaries
20+
{ name = "system-deps", exclude = "src/tests" },
21+
]

Makefile

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,3 +11,11 @@ bin-archive: all
1111

1212
install-kola-tests:
1313
install -D -t $(DESTDIR)$(prefix)/lib/coreos-assembler/tests/kola/bootc tests/kolainst/basic
14+
15+
vendor:
16+
cargo xtask $@
17+
.PHONY: vendor
18+
19+
package-rpm:
20+
cargo xtask $@
21+
.PHONY: package-rpm

cli/Cargo.toml

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@ repository = "https://github.com/cgwalters/bootc"
77
readme = "README.md"
88
publish = false
99
rust-version = "1.63.0"
10+
default-run = "bootc"
1011

1112
# See https://github.com/coreos/cargo-vendor-filterer
1213
[package.metadata.vendor-filter]
@@ -17,7 +18,7 @@ platforms = ["x86_64-unknown-linux-gnu", "aarch64-unknown-linux-gnu", "powerpc64
1718

1819
[dependencies]
1920
anyhow = "1.0"
20-
bootc-lib = { path = "../lib" }
21+
bootc-lib = { version = "0.1", path = "../lib" }
2122
clap = "3.2"
2223
libc = "0.2.92"
2324
tokio = { version = "1", features = ["macros"] }

contrib/packaging/bootc.spec

Lines changed: 53 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,53 @@
1+
%bcond_without check
2+
3+
Name: bootc
4+
Version: 0.1
5+
Release: 1%{?dist}
6+
Summary: Boot containers
7+
8+
License: ASL 2.0
9+
URL: https://github.com/containers/bootc
10+
Source0: https://github.com/containers/bootc/releases/download/v%{version}/bootc-%{version}.tar.zstd
11+
Source1: https://github.com/containers/bootc/releases/download/v%{version}/bootc-%{version}-vendor.tar.zstd
12+
13+
BuildRequires: make
14+
BuildRequires: openssl-devel
15+
BuildRequires: cargo
16+
BuildRequires: systemd
17+
# For autosetup -Sgit
18+
BuildRequires: git
19+
BuildRequires: zlib-devel
20+
BuildRequires: ostree-devel
21+
BuildRequires: openssl-devel
22+
BuildRequires: systemd-devel
23+
24+
%description
25+
%{summary}
26+
27+
%files
28+
%license LICENSE-APACHE LICENSE-MIT
29+
%doc README.md
30+
%{_bindir}/bootc
31+
32+
%prep
33+
%autosetup -p1 -Sgit
34+
tar -xv -f %{SOURCE1}
35+
mkdir -p .cargo
36+
cat >>.cargo/config.toml << EOF
37+
[source.crates-io]
38+
replace-with = "vendored-sources"
39+
40+
[source.vendored-sources]
41+
directory = "vendor"
42+
EOF
43+
44+
%build
45+
make
46+
47+
%install
48+
%make_install INSTALL="install -p -c"
49+
50+
%changelog
51+
* Tue Oct 18 2022 Colin Walters <[email protected]>
52+
- Dummy changelog
53+

xtask/Cargo.toml

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
# See https://github.com/matklad/cargo-xtask
2+
# This is an implementation detail of bootc
3+
[package]
4+
name = "xtask"
5+
version = "0.1.0"
6+
license = "MIT OR Apache-2.0"
7+
edition = "2021"
8+
publish = false
9+
10+
[[bin]]
11+
name = "xtask"
12+
path = "src/xtask.rs"
13+
14+
[dependencies]
15+
anyhow = "1.0.68"
16+
camino = "1.0"
17+
fn-error-context = "0.2.0"
18+
tempfile = "3.3"
19+
xshell = { version = "0.2" }

xtask/src/xtask.rs

Lines changed: 176 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,176 @@
1+
use std::fs::File;
2+
use std::io::{BufRead, BufReader, BufWriter, Write};
3+
use std::process::{Command, Stdio};
4+
5+
use anyhow::{Context, Result};
6+
use camino::{Utf8Path, Utf8PathBuf};
7+
use fn_error_context::context;
8+
use xshell::{cmd, Shell};
9+
10+
const NAME: &str = "bootc";
11+
const VENDORPATH: &str = "target/vendor.tar.zstd";
12+
13+
fn main() {
14+
if let Err(e) = try_main() {
15+
eprintln!("{e:?}");
16+
std::process::exit(1);
17+
}
18+
}
19+
20+
fn try_main() -> Result<()> {
21+
let task = std::env::args().nth(1);
22+
let sh = xshell::Shell::new()?;
23+
if let Some(cmd) = task.as_deref() {
24+
let f = match cmd {
25+
"vendor" => vendor,
26+
"package" => package,
27+
"package-srpm" => package_srpm,
28+
_ => print_help,
29+
};
30+
f(&sh)?;
31+
} else {
32+
print_help(&sh)?;
33+
}
34+
Ok(())
35+
}
36+
37+
fn vendor(sh: &Shell) -> Result<()> {
38+
let target = VENDORPATH;
39+
cmd!(
40+
sh,
41+
"cargo vendor-filterer --prefix=vendor --format=tar.zstd {target}"
42+
)
43+
.run()?;
44+
Ok(())
45+
}
46+
47+
fn gitrev_to_version(v: &str) -> String {
48+
let v = v.trim().trim_start_matches('v');
49+
v.replace('-', ".")
50+
}
51+
52+
#[context("Finding gitrev")]
53+
fn gitrev(sh: &Shell) -> Result<String> {
54+
if let Ok(rev) = cmd!(sh, "git describe --tags").ignore_stderr().read() {
55+
Ok(gitrev_to_version(&rev))
56+
} else {
57+
let mut desc = cmd!(sh, "git describe --tags --always").read()?;
58+
desc.insert_str(0, "0.");
59+
Ok(desc)
60+
}
61+
}
62+
63+
struct Package {
64+
version: String,
65+
srcpath: Utf8PathBuf,
66+
}
67+
68+
#[context("Packaging")]
69+
fn impl_package(sh: &Shell) -> Result<Package> {
70+
let v = gitrev(sh)?;
71+
let namev = format!("{NAME}-{v}");
72+
let p = Utf8Path::new("target").join(format!("{namev}.tar.zstd"));
73+
let o = File::create(&p)?;
74+
let prefix = format!("{namev}/");
75+
let st = Command::new("git")
76+
.args([
77+
"archive",
78+
"--format=tar",
79+
"--prefix",
80+
prefix.as_str(),
81+
"HEAD",
82+
])
83+
.stdout(Stdio::from(o))
84+
.status()?;
85+
if !st.success() {
86+
anyhow::bail!("Failed to run {st:?}");
87+
}
88+
Ok(Package {
89+
version: v,
90+
srcpath: p,
91+
})
92+
}
93+
94+
fn package(sh: &Shell) -> Result<()> {
95+
let p = impl_package(sh)?.srcpath;
96+
println!("Generated: {p}");
97+
Ok(())
98+
}
99+
100+
fn impl_srpm(sh: &Shell) -> Result<Utf8PathBuf> {
101+
let pkg = impl_package(sh)?;
102+
vendor(sh)?;
103+
let td = tempfile::tempdir_in("target").context("Allocating tmpdir")?;
104+
let td = td.into_path();
105+
let td: &Utf8Path = td.as_path().try_into().unwrap();
106+
let srcpath = td.join(pkg.srcpath.file_name().unwrap());
107+
std::fs::rename(pkg.srcpath, srcpath)?;
108+
let v = pkg.version;
109+
let vendorpath = td.join(format!("{NAME}-{v}-vendor.tar.zstd"));
110+
std::fs::rename(VENDORPATH, vendorpath)?;
111+
{
112+
let specin = File::open(format!("contrib/packaging/{NAME}.spec"))
113+
.map(BufReader::new)
114+
.context("Opening spec")?;
115+
let mut o = File::create(td.join(format!("{NAME}.spec"))).map(BufWriter::new)?;
116+
for line in specin.lines() {
117+
let line = line?;
118+
if line.starts_with("Version:") {
119+
writeln!(o, "# Replaced by cargo xtask package-srpm")?;
120+
writeln!(o, "Version: {v}")?;
121+
} else {
122+
writeln!(o, "{}", line)?;
123+
}
124+
}
125+
}
126+
let d = sh.push_dir(td);
127+
let mut cmd = cmd!(sh, "rpmbuild");
128+
for k in [
129+
"_sourcedir",
130+
"_specdir",
131+
"_builddir",
132+
"_srcrpmdir",
133+
"_rpmdir",
134+
] {
135+
cmd = cmd.arg("--define");
136+
cmd = cmd.arg(format!("{k} {td}"));
137+
}
138+
cmd.arg("--define")
139+
.arg(format!("_buildrootdir {td}/.build"))
140+
.args(["-bs", "bootc.spec"])
141+
.run()?;
142+
drop(d);
143+
let mut srpm = None;
144+
for e in std::fs::read_dir(td)? {
145+
let e = e?;
146+
let n = e.file_name();
147+
let n = if let Some(n) = n.to_str() {
148+
n
149+
} else {
150+
continue;
151+
};
152+
if n.ends_with(".src.rpm") {
153+
srpm = Some(td.join(n));
154+
break;
155+
}
156+
}
157+
let srpm = srpm.ok_or_else(|| anyhow::anyhow!("Failed to find generated .src.rpm"))?;
158+
let dest = Utf8Path::new("target").join(srpm.file_name().unwrap());
159+
std::fs::rename(&srpm, &dest)?;
160+
Ok(dest)
161+
}
162+
163+
fn package_srpm(sh: &Shell) -> Result<()> {
164+
let srpm = impl_srpm(sh)?;
165+
println!("Generated: {srpm}");
166+
Ok(())
167+
}
168+
169+
fn print_help(_sh: &Shell) -> Result<()> {
170+
eprintln!(
171+
"Tasks:
172+
- vendor
173+
"
174+
);
175+
Ok(())
176+
}

0 commit comments

Comments
 (0)