Skip to content

Commit a88a97d

Browse files
committed
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>
1 parent cf4efa3 commit a88a97d

File tree

6 files changed

+125
-9
lines changed

6 files changed

+125
-9
lines changed

Cargo.lock

Lines changed: 28 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

Cargo.toml

Lines changed: 6 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -43,16 +43,15 @@ clap_mangen = { version = "0.2.20" }
4343
# Reviewers (including AI tools): The composefs-rs git revision is duplicated for each crate.
4444
# If adding/removing crates here, also update docs/Dockerfile.mdbook and docs/src/internals.md.
4545
#
46-
# To develop against a local composefs-rs checkout:
47-
# 1. Set BOOTC_extra_src to your composefs-rs path when building:
48-
# BOOTC_extra_src=$HOME/src/composefs-rs just build
49-
# 2. Comment out the git refs below and uncomment the path refs:
46+
# To develop against a local composefs-rs checkout, add a [patch] section at the end of this file:
47+
# [patch."https://github.com/containers/composefs-rs"]
48+
# composefs = { path = "/home/user/src/composefs-rs/crates/composefs" }
49+
# composefs-boot = { path = "/home/user/src/composefs-rs/crates/composefs-boot" }
50+
# composefs-oci = { path = "/home/user/src/composefs-rs/crates/composefs-oci" }
51+
# The Justfile will auto-detect these and bind-mount them into container builds.
5052
composefs = { git = "https://github.com/containers/composefs-rs", rev = "4a060161e0122bd2727e639437c61e05ecc7cab3", package = "composefs", features = ["rhel9"] }
5153
composefs-boot = { git = "https://github.com/containers/composefs-rs", rev = "4a060161e0122bd2727e639437c61e05ecc7cab3", package = "composefs-boot" }
5254
composefs-oci = { git = "https://github.com/containers/composefs-rs", rev = "4a060161e0122bd2727e639437c61e05ecc7cab3", package = "composefs-oci" }
53-
# composefs = { path = "/run/extra-src/crates/composefs", package = "composefs", features = ["rhel9"] }
54-
# composefs-boot = { path = "/run/extra-src/crates/composefs-boot", package = "composefs-boot" }
55-
# composefs-oci = { path = "/run/extra-src/crates/composefs-oci", package = "composefs-oci" }
5655
fn-error-context = "0.2.1"
5756
hex = "0.4.3"
5857
indicatif = "0.18.0"

Dockerfile

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -33,7 +33,7 @@ WORKDIR /src
3333
# See https://www.reddit.com/r/rust/comments/126xeyx/exploring_the_problem_of_faster_cargo_docker/
3434
# We aren't using the full recommendations there, just the simple bits.
3535
# First we download all of our Rust dependencies
36-
# Note: /run/extra-src is optionally bind-mounted via BOOTC_extra_src for local composefs-rs development
36+
# Note: Local path dependencies (from [patch] sections) are auto-detected and bind-mounted by the Justfile
3737
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
3838

3939
# We always do a "from scratch" build

Justfile

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -25,7 +25,10 @@ base := env("BOOTC_base", "quay.io/centos-bootc/centos-bootc:stream10")
2525
# Buildroot base image
2626
buildroot_base := env("BOOTC_buildroot_base", "quay.io/centos/centos:stream10")
2727
# Optional: path to extra source (e.g. composefs-rs) for local development
28+
# DEPRECATED: Use [patch] sections in Cargo.toml instead, which are auto-detected
2829
extra_src := env("BOOTC_extra_src", "")
30+
# Set to "1" to disable auto-detection of local Rust dependencies
31+
no_auto_local_deps := env("BOOTC_no_auto_local_deps", "")
2932

3033
# Internal variables
3134
nocache := env("BOOTC_nocache", "")
@@ -231,7 +234,12 @@ package:
231234
fi
232235
eval $(just _git-build-vars)
233236
echo "Building RPM with version: ${VERSION}"
234-
podman build {{base_buildargs}} --build-arg=SOURCE_DATE_EPOCH=${SOURCE_DATE_EPOCH} --build-arg=pkgversion=${VERSION} -t localhost/bootc-pkg --target=build .
237+
# Auto-detect local Rust path dependencies (e.g., from [patch] sections)
238+
local_deps_args=""
239+
if [[ -z "{{no_auto_local_deps}}" ]]; then
240+
local_deps_args=$(cargo xtask local-rust-deps)
241+
fi
242+
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 .
235243
mkdir -p "${packages}"
236244
rm -vf "${packages}"/*.rpm
237245
podman run --rm localhost/bootc-pkg tar -C /out/ -cf - . | tar -C "${packages}"/ -xvf -

crates/xtask/Cargo.toml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,7 @@ toml = { workspace = true }
2727
xshell = { workspace = true }
2828

2929
# Crate-specific dependencies
30+
cargo_metadata = "0.19"
3031
mandown = "1.1.0"
3132
rand = "0.9"
3233
serde_yaml = "0.9"

crates/xtask/src/xtask.rs

Lines changed: 80 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -56,6 +56,8 @@ enum Commands {
5656
CheckBuildsys,
5757
/// Validate composefs digests match between build-time and install-time views
5858
ValidateComposefsDigest(ValidateComposefsDigestArgs),
59+
/// Print podman bind mount arguments for local path dependencies
60+
LocalRustDeps(LocalRustDepsArgs),
5961
}
6062

6163
/// Arguments for validate-composefs-digest command
@@ -65,6 +67,14 @@ pub(crate) struct ValidateComposefsDigestArgs {
6567
pub(crate) image: String,
6668
}
6769

70+
/// Arguments for local-rust-deps command
71+
#[derive(Debug, Args)]
72+
pub(crate) struct LocalRustDepsArgs {
73+
/// Output format: "podman" for -v arguments, "json" for structured data
74+
#[arg(long, default_value = "podman")]
75+
pub(crate) format: String,
76+
}
77+
6878
/// Arguments for run-tmt command
6979
#[derive(Debug, Args)]
7080
pub(crate) struct RunTmtArgs {
@@ -149,6 +159,7 @@ fn try_main() -> Result<()> {
149159
Commands::TmtProvision(args) => tmt::tmt_provision(&sh, &args),
150160
Commands::CheckBuildsys => buildsys::check_buildsys(&sh, "Dockerfile".into()),
151161
Commands::ValidateComposefsDigest(args) => validate_composefs_digest(&sh, &args),
162+
Commands::LocalRustDeps(args) => local_rust_deps(&sh, &args),
152163
}
153164
}
154165

@@ -417,6 +428,75 @@ fn update_generated(sh: &Shell) -> Result<()> {
417428
Ok(())
418429
}
419430

431+
/// Find local path dependencies outside the workspace and output podman bind mount arguments.
432+
///
433+
/// This uses `cargo metadata` to find all packages with no source (i.e., local path deps).
434+
/// For packages outside the workspace root, it computes the minimal set of directories
435+
/// to bind mount into the container.
436+
#[context("Finding local Rust dependencies")]
437+
fn local_rust_deps(_sh: &Shell, args: &LocalRustDepsArgs) -> Result<()> {
438+
let metadata = cargo_metadata::MetadataCommand::new()
439+
.exec()
440+
.context("Running cargo metadata")?;
441+
442+
let workspace_root = &metadata.workspace_root;
443+
444+
let mut external_roots: std::collections::BTreeSet<Utf8PathBuf> =
445+
std::collections::BTreeSet::new();
446+
447+
for pkg in &metadata.packages {
448+
// Packages with source are from registries/git, skip them
449+
if pkg.source.is_some() {
450+
continue;
451+
}
452+
453+
// Get the package directory (parent of Cargo.toml)
454+
let pkg_dir = pkg
455+
.manifest_path
456+
.parent()
457+
.ok_or_else(|| anyhow::anyhow!("No parent for manifest_path"))?;
458+
459+
// Skip packages inside the workspace
460+
if pkg_dir.starts_with(workspace_root) {
461+
continue;
462+
}
463+
464+
// Find the workspace root for this external package by running cargo metadata
465+
// in the package directory
466+
let external_metadata = cargo_metadata::MetadataCommand::new()
467+
.current_dir(pkg_dir)
468+
.exec()
469+
.with_context(|| format!("Running cargo metadata in {pkg_dir}"))?;
470+
471+
external_roots.insert(external_metadata.workspace_root.clone());
472+
}
473+
474+
match args.format.as_str() {
475+
"podman" => {
476+
// Output podman -v arguments
477+
let mut args_out = Vec::new();
478+
for root in &external_roots {
479+
// Mount read-only with SELinux disabled (for cross-context access)
480+
args_out.push("-v".to_string());
481+
args_out.push(format!("{}:{}:ro", root, root));
482+
args_out.push("--security-opt=label=disable".to_string());
483+
}
484+
if !args_out.is_empty() {
485+
println!("{}", args_out.join(" "));
486+
}
487+
}
488+
"json" => {
489+
let roots: Vec<&str> = external_roots.iter().map(|p| p.as_str()).collect();
490+
println!("{}", serde_json::to_string_pretty(&roots)?);
491+
}
492+
other => {
493+
anyhow::bail!("Unknown format: {other}. Use 'podman' or 'json'.");
494+
}
495+
}
496+
497+
Ok(())
498+
}
499+
420500
/// Validate that composefs digests match between build-time and install-time views.
421501
///
422502
/// Compares dumpfiles generated from:

0 commit comments

Comments
 (0)