mirror of
https://github.com/containers/bootc.git
synced 2026-02-05 15:45:53 +01:00
xtask: Add local-rust-deps command for auto-detecting path dependencies
Add `cargo xtask local-rust-deps` which uses `cargo metadata` to find local path dependencies outside the workspace (e.g., from [patch] sections) and outputs podman bind mount arguments. This enables a cleaner workflow for local development against modified dependencies like composefs-rs: 1. Add a [patch] section to Cargo.toml with real local paths 2. Run `just build` - the Justfile auto-detects and bind-mounts them Benefits over the previous BOOTC_extra_src approach: - No manual env var needed - Paths work for both local `cargo build` and container builds - No /run/extra-src indirection or Cargo.toml path munging required - Auto-detection means it Just Works™ The Justfile's build target now calls `cargo xtask local-rust-deps` to get bind mount args, falling back gracefully if there are no external deps. The old BOOTC_extra_src mechanism is still supported for backwards compat. Assisted-by: OpenCode (Opus 4.5) Signed-off-by: Colin Walters <walters@verbum.org>
This commit is contained in:
28
Cargo.lock
generated
28
Cargo.lock
generated
@@ -440,6 +440,29 @@ dependencies = [
|
||||
"uuid",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "cargo-platform"
|
||||
version = "0.1.9"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "e35af189006b9c0f00a064685c727031e3ed2d8020f7ba284d78cc2671bd36ea"
|
||||
dependencies = [
|
||||
"serde",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "cargo_metadata"
|
||||
version = "0.19.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "dd5eb614ed4c27c5d706420e4320fbe3216ab31fa1c33cd8246ac36dae4479ba"
|
||||
dependencies = [
|
||||
"camino",
|
||||
"cargo-platform",
|
||||
"semver",
|
||||
"serde",
|
||||
"serde_json",
|
||||
"thiserror 2.0.17",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "cc"
|
||||
version = "1.2.51"
|
||||
@@ -2275,6 +2298,10 @@ name = "semver"
|
||||
version = "1.0.27"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "d767eb0aabc880b29956c35734170f26ed551a859dbd361d140cdbeca61ab1e2"
|
||||
dependencies = [
|
||||
"serde",
|
||||
"serde_core",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "serde"
|
||||
@@ -3338,6 +3365,7 @@ dependencies = [
|
||||
"anstream",
|
||||
"anyhow",
|
||||
"camino",
|
||||
"cargo_metadata",
|
||||
"chrono",
|
||||
"clap",
|
||||
"fn-error-context",
|
||||
|
||||
13
Cargo.toml
13
Cargo.toml
@@ -43,16 +43,15 @@ clap_mangen = { version = "0.2.20" }
|
||||
# Reviewers (including AI tools): The composefs-rs git revision is duplicated for each crate.
|
||||
# If adding/removing crates here, also update docs/Dockerfile.mdbook and docs/src/internals.md.
|
||||
#
|
||||
# To develop against a local composefs-rs checkout:
|
||||
# 1. Set BOOTC_extra_src to your composefs-rs path when building:
|
||||
# BOOTC_extra_src=$HOME/src/composefs-rs just build
|
||||
# 2. Comment out the git refs below and uncomment the path refs:
|
||||
# To develop against a local composefs-rs checkout, add a [patch] section at the end of this file:
|
||||
# [patch."https://github.com/containers/composefs-rs"]
|
||||
# composefs = { path = "/home/user/src/composefs-rs/crates/composefs" }
|
||||
# composefs-boot = { path = "/home/user/src/composefs-rs/crates/composefs-boot" }
|
||||
# composefs-oci = { path = "/home/user/src/composefs-rs/crates/composefs-oci" }
|
||||
# The Justfile will auto-detect these and bind-mount them into container builds.
|
||||
composefs = { git = "https://github.com/containers/composefs-rs", rev = "4a060161e0122bd2727e639437c61e05ecc7cab3", package = "composefs", features = ["rhel9"] }
|
||||
composefs-boot = { git = "https://github.com/containers/composefs-rs", rev = "4a060161e0122bd2727e639437c61e05ecc7cab3", package = "composefs-boot" }
|
||||
composefs-oci = { git = "https://github.com/containers/composefs-rs", rev = "4a060161e0122bd2727e639437c61e05ecc7cab3", package = "composefs-oci" }
|
||||
# composefs = { path = "/run/extra-src/crates/composefs", package = "composefs", features = ["rhel9"] }
|
||||
# composefs-boot = { path = "/run/extra-src/crates/composefs-boot", package = "composefs-boot" }
|
||||
# composefs-oci = { path = "/run/extra-src/crates/composefs-oci", package = "composefs-oci" }
|
||||
fn-error-context = "0.2.1"
|
||||
hex = "0.4.3"
|
||||
indicatif = "0.18.0"
|
||||
|
||||
@@ -33,7 +33,7 @@ WORKDIR /src
|
||||
# See https://www.reddit.com/r/rust/comments/126xeyx/exploring_the_problem_of_faster_cargo_docker/
|
||||
# We aren't using the full recommendations there, just the simple bits.
|
||||
# First we download all of our Rust dependencies
|
||||
# Note: /run/extra-src is optionally bind-mounted via BOOTC_extra_src for local composefs-rs development
|
||||
# Note: Local path dependencies (from [patch] sections) are auto-detected and bind-mounted by the Justfile
|
||||
RUN --mount=type=tmpfs,target=/run --mount=type=tmpfs,target=/tmp --mount=type=cache,target=/src/target --mount=type=cache,target=/var/roothome cargo fetch
|
||||
|
||||
# We always do a "from scratch" build
|
||||
|
||||
10
Justfile
10
Justfile
@@ -25,7 +25,10 @@ base := env("BOOTC_base", "quay.io/centos-bootc/centos-bootc:stream10")
|
||||
# Buildroot base image
|
||||
buildroot_base := env("BOOTC_buildroot_base", "quay.io/centos/centos:stream10")
|
||||
# Optional: path to extra source (e.g. composefs-rs) for local development
|
||||
# DEPRECATED: Use [patch] sections in Cargo.toml instead, which are auto-detected
|
||||
extra_src := env("BOOTC_extra_src", "")
|
||||
# Set to "1" to disable auto-detection of local Rust dependencies
|
||||
no_auto_local_deps := env("BOOTC_no_auto_local_deps", "")
|
||||
|
||||
# Internal variables
|
||||
nocache := env("BOOTC_nocache", "")
|
||||
@@ -231,7 +234,12 @@ package:
|
||||
fi
|
||||
eval $(just _git-build-vars)
|
||||
echo "Building RPM with version: ${VERSION}"
|
||||
podman build {{base_buildargs}} --build-arg=SOURCE_DATE_EPOCH=${SOURCE_DATE_EPOCH} --build-arg=pkgversion=${VERSION} -t localhost/bootc-pkg --target=build .
|
||||
# Auto-detect local Rust path dependencies (e.g., from [patch] sections)
|
||||
local_deps_args=""
|
||||
if [[ -z "{{no_auto_local_deps}}" ]]; then
|
||||
local_deps_args=$(cargo xtask local-rust-deps)
|
||||
fi
|
||||
podman build {{base_buildargs}} --build-arg=SOURCE_DATE_EPOCH=${SOURCE_DATE_EPOCH} --build-arg=pkgversion=${VERSION} -t localhost/bootc-pkg --target=build $local_deps_args .
|
||||
mkdir -p "${packages}"
|
||||
rm -vf "${packages}"/*.rpm
|
||||
podman run --rm localhost/bootc-pkg tar -C /out/ -cf - . | tar -C "${packages}"/ -xvf -
|
||||
|
||||
@@ -27,6 +27,7 @@ toml = { workspace = true }
|
||||
xshell = { workspace = true }
|
||||
|
||||
# Crate-specific dependencies
|
||||
cargo_metadata = "0.19"
|
||||
mandown = "1.1.0"
|
||||
rand = "0.9"
|
||||
serde_yaml = "0.9"
|
||||
|
||||
@@ -56,6 +56,8 @@ enum Commands {
|
||||
CheckBuildsys,
|
||||
/// Validate composefs digests match between build-time and install-time views
|
||||
ValidateComposefsDigest(ValidateComposefsDigestArgs),
|
||||
/// Print podman bind mount arguments for local path dependencies
|
||||
LocalRustDeps(LocalRustDepsArgs),
|
||||
}
|
||||
|
||||
/// Arguments for validate-composefs-digest command
|
||||
@@ -65,6 +67,14 @@ pub(crate) struct ValidateComposefsDigestArgs {
|
||||
pub(crate) image: String,
|
||||
}
|
||||
|
||||
/// Arguments for local-rust-deps command
|
||||
#[derive(Debug, Args)]
|
||||
pub(crate) struct LocalRustDepsArgs {
|
||||
/// Output format: "podman" for -v arguments, "json" for structured data
|
||||
#[arg(long, default_value = "podman")]
|
||||
pub(crate) format: String,
|
||||
}
|
||||
|
||||
/// Arguments for run-tmt command
|
||||
#[derive(Debug, Args)]
|
||||
pub(crate) struct RunTmtArgs {
|
||||
@@ -149,6 +159,7 @@ fn try_main() -> Result<()> {
|
||||
Commands::TmtProvision(args) => tmt::tmt_provision(&sh, &args),
|
||||
Commands::CheckBuildsys => buildsys::check_buildsys(&sh, "Dockerfile".into()),
|
||||
Commands::ValidateComposefsDigest(args) => validate_composefs_digest(&sh, &args),
|
||||
Commands::LocalRustDeps(args) => local_rust_deps(&sh, &args),
|
||||
}
|
||||
}
|
||||
|
||||
@@ -417,6 +428,75 @@ fn update_generated(sh: &Shell) -> Result<()> {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Find local path dependencies outside the workspace and output podman bind mount arguments.
|
||||
///
|
||||
/// This uses `cargo metadata` to find all packages with no source (i.e., local path deps).
|
||||
/// For packages outside the workspace root, it computes the minimal set of directories
|
||||
/// to bind mount into the container.
|
||||
#[context("Finding local Rust dependencies")]
|
||||
fn local_rust_deps(_sh: &Shell, args: &LocalRustDepsArgs) -> Result<()> {
|
||||
let metadata = cargo_metadata::MetadataCommand::new()
|
||||
.exec()
|
||||
.context("Running cargo metadata")?;
|
||||
|
||||
let workspace_root = &metadata.workspace_root;
|
||||
|
||||
let mut external_roots: std::collections::BTreeSet<Utf8PathBuf> =
|
||||
std::collections::BTreeSet::new();
|
||||
|
||||
for pkg in &metadata.packages {
|
||||
// Packages with source are from registries/git, skip them
|
||||
if pkg.source.is_some() {
|
||||
continue;
|
||||
}
|
||||
|
||||
// Get the package directory (parent of Cargo.toml)
|
||||
let pkg_dir = pkg
|
||||
.manifest_path
|
||||
.parent()
|
||||
.ok_or_else(|| anyhow::anyhow!("No parent for manifest_path"))?;
|
||||
|
||||
// Skip packages inside the workspace
|
||||
if pkg_dir.starts_with(workspace_root) {
|
||||
continue;
|
||||
}
|
||||
|
||||
// Find the workspace root for this external package by running cargo metadata
|
||||
// in the package directory
|
||||
let external_metadata = cargo_metadata::MetadataCommand::new()
|
||||
.current_dir(pkg_dir)
|
||||
.exec()
|
||||
.with_context(|| format!("Running cargo metadata in {pkg_dir}"))?;
|
||||
|
||||
external_roots.insert(external_metadata.workspace_root.clone());
|
||||
}
|
||||
|
||||
match args.format.as_str() {
|
||||
"podman" => {
|
||||
// Output podman -v arguments
|
||||
let mut args_out = Vec::new();
|
||||
for root in &external_roots {
|
||||
// Mount read-only with SELinux disabled (for cross-context access)
|
||||
args_out.push("-v".to_string());
|
||||
args_out.push(format!("{}:{}:ro", root, root));
|
||||
args_out.push("--security-opt=label=disable".to_string());
|
||||
}
|
||||
if !args_out.is_empty() {
|
||||
println!("{}", args_out.join(" "));
|
||||
}
|
||||
}
|
||||
"json" => {
|
||||
let roots: Vec<&str> = external_roots.iter().map(|p| p.as_str()).collect();
|
||||
println!("{}", serde_json::to_string_pretty(&roots)?);
|
||||
}
|
||||
other => {
|
||||
anyhow::bail!("Unknown format: {other}. Use 'podman' or 'json'.");
|
||||
}
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Validate that composefs digests match between build-time and install-time views.
|
||||
///
|
||||
/// Compares dumpfiles generated from:
|
||||
|
||||
Reference in New Issue
Block a user