From b50745a67a31317320c8f3f8b529ddbad57ae0b1 Mon Sep 17 00:00:00 2001 From: SFG545 Date: Mon, 9 Mar 2026 00:45:33 -0500 Subject: [PATCH] feat: add support for delegating live root filesystem installations to child processes --- src/commands.rs | 99 +++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 99 insertions(+) diff --git a/src/commands.rs b/src/commands.rs index a5b43b5..4cffa4c 100644 --- a/src/commands.rs +++ b/src/commands.rs @@ -29,6 +29,10 @@ fn should_reexec_with_sudo(cli: &Cli) -> bool { && command_requires_live_root(&cli.command) } +fn should_delegate_live_rootfs_installs(rootfs: &Path) -> bool { + !crate::fakeroot::is_root() && rootfs_is_system_root(rootfs) +} + fn maybe_reexec_with_sudo(cli: &Cli) -> Result { if !should_reexec_with_sudo(cli) { return Ok(false); @@ -49,6 +53,44 @@ fn maybe_reexec_with_sudo(cli: &Cli) -> Result { } } +fn run_child_install_command( + install_request: &Path, + rootfs: &Path, + options: InstallPlanExecutionOptions<'_>, +) -> Result<()> { + let exe = std::env::current_exe().context("Failed to locate depot executable")?; + let mut cmd = std::process::Command::new(&exe); + cmd.arg("-r").arg(rootfs); + cmd.arg("--no-deps"); + cmd.arg("--yes"); + if options.no_flags { + cmd.arg("--no-flags"); + } + if let Some(p) = options.cross_prefix { + cmd.arg("--cross-prefix").arg(p); + } + if options.clean { + cmd.arg("--clean"); + } + cmd.arg("install").arg(install_request); + + let status = cmd.status().with_context(|| { + format!( + "Failed to spawn child install for {}", + install_request.display() + ) + })?; + if status.success() { + Ok(()) + } else { + anyhow::bail!( + "Child install failed for {} with status {}", + install_request.display(), + status + ); + } +} + fn parse_licenses_from_toml(metadata: &toml::Value) -> Vec { if let Some(s) = metadata.get("license").and_then(|v| v.as_str()) { return vec![s.to_string()]; @@ -1112,6 +1154,7 @@ fn print_plan_summary(plan: &planner::ExecutionPlan) { } } +#[derive(Clone, Copy)] struct InstallPlanExecutionOptions<'a> { no_flags: bool, cross_prefix: Option<&'a str>, @@ -1160,6 +1203,31 @@ fn execute_install_plan_with_child_commands( return Ok(()); } + if should_delegate_live_rootfs_installs(rootfs) { + for step in actionable_steps { + match &step.origin { + planner::PlanOrigin::Source { path, .. } => { + run_child_install_command(path, rootfs, options)?; + } + planner::PlanOrigin::Binary { repo_name, record } => { + let repo_cfg = config.binary_repos.get(repo_name).with_context(|| { + format!("Binary repo '{}' not found in config", repo_name) + })?; + let archive_path = db::repo::fetch_binary_package_archive( + repo_name, + repo_cfg, + rootfs, + record, + &config.package_cache_dir, + )?; + run_child_install_command(&archive_path, rootfs, options)?; + } + planner::PlanOrigin::Installed => {} + } + } + return Ok(()); + } + let mut binary_phase_items = Vec::new(); for step in &actionable_steps { if let planner::PlanOrigin::Binary { repo_name, record } = &step.origin { @@ -2090,6 +2158,26 @@ pub fn run(cli: Cli) -> Result<()> { )); } if ui::prompt_package_action("installation", &install_targets, false)? { + if should_delegate_live_rootfs_installs(&cli.rootfs) { + for archive in &created_files { + run_child_install_command( + archive, + &cli.rootfs, + InstallPlanExecutionOptions { + no_flags: cli.no_flags, + cross_prefix: cli.cross_prefix.as_deref(), + clean: cli.clean, + dry_run: cli.dry_run, + confirm_installation: false, + }, + )?; + } + if cli.clean { + clean_build_workspace(&config)?; + } + return Ok(()); + } + let mut transaction_plans = Vec::new(); if !cli.lib32_only { let output_plans = @@ -2842,4 +2930,15 @@ mod tests { files: false, })); } + + #[test] + fn should_delegate_live_rootfs_installs_only_for_live_root_when_non_root() { + assert_eq!( + should_delegate_live_rootfs_installs(Path::new("/")), + !crate::fakeroot::is_root() + ); + assert!(!should_delegate_live_rootfs_installs(Path::new( + "/tmp/depot-test-rootfs" + ))); + } }