feat: update depot version to 0.12.0 and refactor environment variable handling in tests
This commit is contained in:
Generated
+1
-1
@@ -382,7 +382,7 @@ checksum = "807800ff3288b621186fe0a8f3392c4652068257302709c24efd918c3dffcdc2"
|
||||
|
||||
[[package]]
|
||||
name = "depot"
|
||||
version = "0.11.1"
|
||||
version = "0.12.0"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"ar",
|
||||
|
||||
+1
-1
@@ -1,6 +1,6 @@
|
||||
[package]
|
||||
name = "depot"
|
||||
version = "0.11.1"
|
||||
version = "0.12.0"
|
||||
edition = "2024"
|
||||
|
||||
[lints.rust]
|
||||
|
||||
+1
-1
@@ -1,6 +1,6 @@
|
||||
project(
|
||||
'depot',
|
||||
version: '0.11.0',
|
||||
version: '0.12.0',
|
||||
meson_version: '>=0.60.0',
|
||||
)
|
||||
|
||||
|
||||
@@ -388,12 +388,14 @@ fn resolve_actual_src(
|
||||
mod tests {
|
||||
use super::*;
|
||||
use crate::package::{Build, BuildFlags, BuildType, PackageInfo, PackageSpec};
|
||||
use crate::test_support::TestEnv;
|
||||
use tempfile::tempdir;
|
||||
|
||||
#[test]
|
||||
fn test_expand_env_vars_replaces_vars() {
|
||||
// Set a test env var
|
||||
unsafe { std::env::set_var("DEPOT_TEST_FOO", "bar") };
|
||||
let mut env = TestEnv::new();
|
||||
env.set_var("DEPOT_TEST_FOO", "bar");
|
||||
let input = "$DEPOT_TEST_FOO and ${DEPOT_TEST_FOO}";
|
||||
let out = expand_env_vars(input);
|
||||
assert!(out.contains("bar"));
|
||||
|
||||
+17
-21
@@ -326,6 +326,7 @@ pub fn build(
|
||||
mod tests {
|
||||
use super::*;
|
||||
use crate::package::{Build, BuildFlags, BuildType, Dependencies, PackageInfo, PackageSpec};
|
||||
use crate::test_support::TestEnv;
|
||||
use std::collections::HashMap;
|
||||
use std::ffi::OsStr;
|
||||
use std::path::PathBuf;
|
||||
@@ -373,14 +374,13 @@ mod tests {
|
||||
// Set an env var that should be cleared
|
||||
cmd.env("FORBIDDEN", "value");
|
||||
// Set PATH manually in the current process to ensure it's picked up if it exists
|
||||
unsafe {
|
||||
std::env::set_var("PATH", "/usr/bin");
|
||||
std::env::set_var("HOME", "/home/test");
|
||||
std::env::set_var("SHELL", "/bin/zsh");
|
||||
std::env::set_var("DEPOT_ROOTFS", "/my/rootfs");
|
||||
std::env::set_var("TERM", "xterm-256color");
|
||||
std::env::set_var("CLICOLOR_FORCE", "1");
|
||||
}
|
||||
let mut env = TestEnv::new();
|
||||
env.set_var("PATH", "/usr/bin");
|
||||
env.set_var("HOME", "/home/test");
|
||||
env.set_var("SHELL", "/bin/zsh");
|
||||
env.set_var("DEPOT_ROOTFS", "/my/rootfs");
|
||||
env.set_var("TERM", "xterm-256color");
|
||||
env.set_var("CLICOLOR_FORCE", "1");
|
||||
|
||||
prepare_command(&mut cmd, &vec![("MYVAR".to_string(), "myval".to_string())]);
|
||||
|
||||
@@ -416,9 +416,8 @@ mod tests {
|
||||
#[test]
|
||||
fn test_prepare_command_preserves_destdir() {
|
||||
let mut cmd = std::process::Command::new("ls");
|
||||
unsafe {
|
||||
std::env::set_var("DESTDIR", "/tmp/dest");
|
||||
}
|
||||
let mut env = TestEnv::new();
|
||||
env.set_var("DESTDIR", "/tmp/dest");
|
||||
prepare_command(&mut cmd, &Vec::new());
|
||||
let envs: HashMap<_, _> = cmd.get_envs().collect();
|
||||
assert_eq!(
|
||||
@@ -430,10 +429,9 @@ mod tests {
|
||||
#[test]
|
||||
fn test_prepare_command_preserves_rust_toolchain_homes() {
|
||||
let mut cmd = std::process::Command::new("ls");
|
||||
unsafe {
|
||||
std::env::set_var("CARGO_HOME", "/var/cache/cargo-home");
|
||||
std::env::set_var("RUSTUP_HOME", "/var/cache/rustup-home");
|
||||
}
|
||||
let mut env = TestEnv::new();
|
||||
env.set_var("CARGO_HOME", "/var/cache/cargo-home");
|
||||
env.set_var("RUSTUP_HOME", "/var/cache/rustup-home");
|
||||
prepare_command(&mut cmd, &Vec::new());
|
||||
let envs: HashMap<_, _> = cmd.get_envs().collect();
|
||||
assert_eq!(
|
||||
@@ -581,9 +579,8 @@ mod tests {
|
||||
let mut spec = mk_spec(Vec::new(), Vec::new());
|
||||
spec.build.flags.passthrough_env = vec!["RUSTFLAGS".into()];
|
||||
|
||||
unsafe {
|
||||
std::env::set_var("RUSTFLAGS", "-C target-cpu=native");
|
||||
}
|
||||
let mut env = TestEnv::new();
|
||||
env.set_var("RUSTFLAGS", "-C target-cpu=native");
|
||||
|
||||
let env = standard_build_env(&spec, None, false, true);
|
||||
assert!(
|
||||
@@ -599,9 +596,8 @@ mod tests {
|
||||
spec.build.flags.cc = "spec-cc".to_string();
|
||||
spec.build.flags.passthrough_env = vec!["CC".into()];
|
||||
|
||||
unsafe {
|
||||
std::env::set_var("CC", "host-cc");
|
||||
}
|
||||
let mut env = TestEnv::new();
|
||||
env.set_var("CC", "host-cc");
|
||||
|
||||
let env = standard_build_env(&spec, None, true, true);
|
||||
assert!(
|
||||
|
||||
+3
-7
@@ -276,6 +276,7 @@ fn has_assignment_prefix(args: &[String], name: &str) -> bool {
|
||||
mod tests {
|
||||
use super::*;
|
||||
use crate::package::{Build, BuildFlags, BuildType, Dependencies, PackageInfo};
|
||||
use crate::test_support::TestEnv;
|
||||
use std::os::unix::fs::PermissionsExt;
|
||||
use tempfile::tempdir;
|
||||
|
||||
@@ -378,10 +379,9 @@ exec "$@"
|
||||
"#,
|
||||
)?;
|
||||
|
||||
let mut env = TestEnv::new();
|
||||
let old_path = std::env::var("PATH").unwrap_or_default();
|
||||
unsafe {
|
||||
std::env::set_var("PATH", format!("{}:{}", tools_path.display(), old_path));
|
||||
}
|
||||
env.set_var("PATH", format!("{}:{}", tools_path.display(), old_path));
|
||||
|
||||
let mut spec = mk_spec("perl-test", "1.0");
|
||||
spec.build.flags.make_exec = tools_path.join("make").to_string_lossy().into_owned();
|
||||
@@ -389,10 +389,6 @@ exec "$@"
|
||||
|
||||
let build_result = build(&spec, src_path, dest_path, None, true);
|
||||
|
||||
unsafe {
|
||||
std::env::set_var("PATH", old_path);
|
||||
}
|
||||
|
||||
build_result?;
|
||||
|
||||
let perl_args = fs::read_to_string(src_path.join("perl-args.log"))?;
|
||||
|
||||
+142
-61
@@ -53,16 +53,42 @@ fn maybe_reexec_with_sudo(cli: &Cli) -> Result<bool> {
|
||||
}
|
||||
}
|
||||
|
||||
fn run_child_install_command(
|
||||
install_request: &Path,
|
||||
#[derive(Clone, Copy)]
|
||||
struct ChildInstallCommandOptions<'a> {
|
||||
no_deps: bool,
|
||||
assume_yes: bool,
|
||||
no_flags: bool,
|
||||
cross_prefix: Option<&'a str>,
|
||||
clean: bool,
|
||||
dep_chain: Option<&'a str>,
|
||||
}
|
||||
|
||||
fn install_request_display(install_requests: &[PathBuf]) -> String {
|
||||
install_requests
|
||||
.iter()
|
||||
.map(|path| path.display().to_string())
|
||||
.collect::<Vec<_>>()
|
||||
.join(", ")
|
||||
}
|
||||
|
||||
fn run_install_command_with_program(
|
||||
program: &Path,
|
||||
install_requests: &[PathBuf],
|
||||
rootfs: &Path,
|
||||
options: InstallPlanExecutionOptions<'_>,
|
||||
options: ChildInstallCommandOptions<'_>,
|
||||
) -> Result<()> {
|
||||
let exe = std::env::current_exe().context("Failed to locate depot executable")?;
|
||||
let mut cmd = std::process::Command::new(&exe);
|
||||
if install_requests.is_empty() {
|
||||
return Ok(());
|
||||
}
|
||||
|
||||
let mut cmd = std::process::Command::new(program);
|
||||
cmd.arg("-r").arg(rootfs);
|
||||
if options.no_deps {
|
||||
cmd.arg("--no-deps");
|
||||
}
|
||||
if options.assume_yes {
|
||||
cmd.arg("--yes");
|
||||
}
|
||||
if options.no_flags {
|
||||
cmd.arg("--no-flags");
|
||||
}
|
||||
@@ -72,12 +98,16 @@ fn run_child_install_command(
|
||||
if options.clean {
|
||||
cmd.arg("--clean");
|
||||
}
|
||||
cmd.arg("install").arg(install_request);
|
||||
cmd.arg("install");
|
||||
cmd.args(install_requests);
|
||||
if let Some(dep_chain) = options.dep_chain {
|
||||
cmd.env("DEPOT_DEPCHAIN", dep_chain);
|
||||
}
|
||||
|
||||
let status = cmd.status().with_context(|| {
|
||||
format!(
|
||||
"Failed to spawn child install for {}",
|
||||
install_request.display()
|
||||
install_request_display(install_requests)
|
||||
)
|
||||
})?;
|
||||
if status.success() {
|
||||
@@ -85,12 +115,33 @@ fn run_child_install_command(
|
||||
} else {
|
||||
anyhow::bail!(
|
||||
"Child install failed for {} with status {}",
|
||||
install_request.display(),
|
||||
install_request_display(install_requests),
|
||||
status
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
fn run_child_install_command(
|
||||
install_requests: &[PathBuf],
|
||||
rootfs: &Path,
|
||||
options: InstallPlanExecutionOptions<'_>,
|
||||
) -> Result<()> {
|
||||
let exe = std::env::current_exe().context("Failed to locate depot executable")?;
|
||||
run_install_command_with_program(
|
||||
&exe,
|
||||
install_requests,
|
||||
rootfs,
|
||||
ChildInstallCommandOptions {
|
||||
no_deps: true,
|
||||
assume_yes: true,
|
||||
no_flags: options.no_flags,
|
||||
cross_prefix: options.cross_prefix,
|
||||
clean: options.clean,
|
||||
dep_chain: None,
|
||||
},
|
||||
)
|
||||
}
|
||||
|
||||
fn parse_licenses_from_toml(metadata: &toml::Value) -> Vec<String> {
|
||||
if let Some(s) = metadata.get("license").and_then(|v| v.as_str()) {
|
||||
return vec![s.to_string()];
|
||||
@@ -1250,10 +1301,11 @@ fn execute_install_plan_with_child_commands(
|
||||
}
|
||||
|
||||
if should_delegate_live_rootfs_installs(rootfs) {
|
||||
let mut install_requests = Vec::new();
|
||||
for step in actionable_steps {
|
||||
match &step.origin {
|
||||
planner::PlanOrigin::Source { path, .. } => {
|
||||
run_child_install_command(path, rootfs, options)?;
|
||||
install_requests.push(path.clone());
|
||||
}
|
||||
planner::PlanOrigin::Binary { repo_name, record } => {
|
||||
let repo_cfg = config.binary_repos.get(repo_name).with_context(|| {
|
||||
@@ -1266,11 +1318,12 @@ fn execute_install_plan_with_child_commands(
|
||||
record,
|
||||
&config.package_cache_dir,
|
||||
)?;
|
||||
run_child_install_command(&archive_path, rootfs, options)?;
|
||||
install_requests.push(archive_path);
|
||||
}
|
||||
planner::PlanOrigin::Installed => {}
|
||||
}
|
||||
}
|
||||
run_child_install_command(&install_requests, rootfs, options)?;
|
||||
return Ok(());
|
||||
}
|
||||
|
||||
@@ -1712,42 +1765,35 @@ fn run_direct_install_request(
|
||||
format!("{},{}", dep_chain, pkg_spec.package.name)
|
||||
};
|
||||
|
||||
// Attempt to install missing deps
|
||||
let mut dep_spec_paths = Vec::new();
|
||||
for dep in missing {
|
||||
// Use package index for O(1) lookup
|
||||
let candidate = pkg_index.find(&dep);
|
||||
|
||||
if let Some(dep_spec_path) = candidate {
|
||||
ui::info(format!("Installing dependency: {}...", dep));
|
||||
|
||||
let mut cmd = std::process::Command::new(std::env::current_exe()?);
|
||||
cmd.arg("-r").arg(options.rootfs);
|
||||
|
||||
if options.no_deps {
|
||||
cmd.arg("--no-deps");
|
||||
}
|
||||
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(&dep_spec_path);
|
||||
cmd.env("DEPOT_DEPCHAIN", &new_chain);
|
||||
|
||||
let status = cmd.status()?;
|
||||
|
||||
if !status.success() {
|
||||
anyhow::bail!("Failed to install dependency: {}", dep);
|
||||
}
|
||||
dep_spec_paths.push(dep_spec_path);
|
||||
} else {
|
||||
anyhow::bail!("Could not find package spec for dependency: {}", dep);
|
||||
}
|
||||
}
|
||||
ui::info(format!(
|
||||
"Installing dependencies: {}",
|
||||
install_request_display(&dep_spec_paths)
|
||||
));
|
||||
let exe = std::env::current_exe().context("Failed to locate depot executable")?;
|
||||
run_install_command_with_program(
|
||||
&exe,
|
||||
&dep_spec_paths,
|
||||
options.rootfs,
|
||||
ChildInstallCommandOptions {
|
||||
no_deps: options.no_deps,
|
||||
assume_yes: false,
|
||||
no_flags: options.no_flags,
|
||||
cross_prefix: options.cross_prefix,
|
||||
clean: options.clean,
|
||||
dep_chain: Some(&new_chain),
|
||||
},
|
||||
)?;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2206,9 +2252,8 @@ 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,
|
||||
&created_files,
|
||||
&cli.rootfs,
|
||||
InstallPlanExecutionOptions {
|
||||
no_flags: cli.no_flags,
|
||||
@@ -2218,7 +2263,6 @@ pub fn run(cli: Cli) -> Result<()> {
|
||||
confirm_installation: false,
|
||||
},
|
||||
)?;
|
||||
}
|
||||
if cli.clean {
|
||||
clean_build_workspace(&config)?;
|
||||
}
|
||||
@@ -2915,28 +2959,7 @@ mod tests {
|
||||
let mut cfg = config::Config::for_rootfs(rootfs.path());
|
||||
cfg.build_dir = rootfs.path().join("var/cache/depot/build");
|
||||
|
||||
let blocked_tmp = rootfs.path().join("blocked-tmp");
|
||||
fs::create_dir_all(&blocked_tmp)
|
||||
.with_context(|| format!("Failed to create {}", blocked_tmp.display()))?;
|
||||
#[cfg(unix)]
|
||||
{
|
||||
use std::os::unix::fs::PermissionsExt;
|
||||
fs::set_permissions(&blocked_tmp, fs::Permissions::from_mode(0o555))
|
||||
.with_context(|| format!("Failed to chmod {}", blocked_tmp.display()))?;
|
||||
}
|
||||
|
||||
let previous_tmpdir = std::env::var_os("TMPDIR");
|
||||
unsafe {
|
||||
std::env::set_var("TMPDIR", &blocked_tmp);
|
||||
}
|
||||
let staged = extract_package_archive_to_staging(&cfg, &archive_path)?;
|
||||
unsafe {
|
||||
if let Some(value) = previous_tmpdir {
|
||||
std::env::set_var("TMPDIR", value);
|
||||
} else {
|
||||
std::env::remove_var("TMPDIR");
|
||||
}
|
||||
}
|
||||
|
||||
assert!(staged.path().starts_with(staging_temp_root(&cfg)));
|
||||
assert!(staged.path().join("usr/bin/hello").exists());
|
||||
@@ -3143,6 +3166,64 @@ optional = []
|
||||
assert_eq!(merged, vec!["make", "pkgconf", "glibc", "openssl", "zlib"]);
|
||||
}
|
||||
|
||||
#[test]
|
||||
#[cfg(unix)]
|
||||
fn child_install_command_batches_multiple_requests_in_one_invocation() -> Result<()> {
|
||||
use std::os::unix::fs::PermissionsExt;
|
||||
|
||||
let temp = tempfile::tempdir().context("Failed to create temp dir")?;
|
||||
let script_path = temp.path().join("capture-child-install.sh");
|
||||
let args_path = temp.path().join("args.txt");
|
||||
let env_path = temp.path().join("env.txt");
|
||||
let script = format!(
|
||||
"#!/bin/sh\nprintf '%s\\n' \"$@\" > \"{}\"\nprintf '%s' \"${{DEPOT_DEPCHAIN:-}}\" > \"{}\"\n",
|
||||
args_path.display(),
|
||||
env_path.display()
|
||||
);
|
||||
fs::write(&script_path, script)
|
||||
.with_context(|| format!("Failed to write {}", script_path.display()))?;
|
||||
fs::set_permissions(&script_path, fs::Permissions::from_mode(0o755))
|
||||
.with_context(|| format!("Failed to chmod {}", script_path.display()))?;
|
||||
|
||||
let requests = vec![
|
||||
PathBuf::from("/tmp/pkg-a.toml"),
|
||||
PathBuf::from("/tmp/pkg-b.toml"),
|
||||
];
|
||||
let rootfs = Path::new("/");
|
||||
run_install_command_with_program(
|
||||
&script_path,
|
||||
&requests,
|
||||
rootfs,
|
||||
ChildInstallCommandOptions {
|
||||
no_deps: false,
|
||||
assume_yes: false,
|
||||
no_flags: true,
|
||||
cross_prefix: Some("x86_64-linux-musl"),
|
||||
clean: true,
|
||||
dep_chain: Some("parent"),
|
||||
},
|
||||
)?;
|
||||
|
||||
let captured_args = fs::read_to_string(&args_path)
|
||||
.with_context(|| format!("Failed to read {}", args_path.display()))?;
|
||||
assert_eq!(
|
||||
captured_args.lines().collect::<Vec<_>>(),
|
||||
vec![
|
||||
"-r",
|
||||
"/",
|
||||
"--no-flags",
|
||||
"--cross-prefix",
|
||||
"x86_64-linux-musl",
|
||||
"--clean",
|
||||
"install",
|
||||
"/tmp/pkg-a.toml",
|
||||
"/tmp/pkg-b.toml",
|
||||
]
|
||||
);
|
||||
assert_eq!(fs::read_to_string(&env_path)?, "parent");
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn rootfs_is_system_root_detects_live_rootfs() {
|
||||
assert!(rootfs_is_system_root(Path::new("/")));
|
||||
|
||||
+3
-17
@@ -511,6 +511,7 @@ impl Default for Config {
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
use crate::test_support::TestEnv;
|
||||
|
||||
#[test]
|
||||
fn test_config_for_rootfs() {
|
||||
@@ -616,13 +617,9 @@ cflags += ["-g"]
|
||||
|
||||
#[test]
|
||||
fn test_config_non_root_fallback() {
|
||||
// Make the test deterministic by overriding HOME for the duration of the check.
|
||||
// Save and restore the original value so other tests aren't affected.
|
||||
let orig_home = std::env::var_os("HOME");
|
||||
let fake_home = tempfile::tempdir().unwrap();
|
||||
unsafe {
|
||||
std::env::set_var("HOME", fake_home.path());
|
||||
}
|
||||
let mut env = TestEnv::new();
|
||||
env.set_var("HOME", fake_home.path());
|
||||
|
||||
let config = Config::for_rootfs(Path::new("/"));
|
||||
|
||||
@@ -639,17 +636,6 @@ cflags += ["-g"]
|
||||
// If running as root (e.g. in some CI), it should use /var
|
||||
assert_eq!(config.cache_dir, PathBuf::from("/var/cache/depot/sources"));
|
||||
}
|
||||
|
||||
// restore original HOME
|
||||
if let Some(v) = orig_home {
|
||||
unsafe {
|
||||
std::env::set_var("HOME", v);
|
||||
}
|
||||
} else {
|
||||
unsafe {
|
||||
std::env::remove_var("HOME");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
|
||||
@@ -20,6 +20,8 @@ mod shell_helpers;
|
||||
mod signing;
|
||||
mod source;
|
||||
mod staging;
|
||||
#[cfg(test)]
|
||||
mod test_support;
|
||||
mod ui;
|
||||
|
||||
use anyhow::Result;
|
||||
|
||||
@@ -0,0 +1,50 @@
|
||||
use std::collections::BTreeMap;
|
||||
use std::env;
|
||||
use std::ffi::{OsStr, OsString};
|
||||
use std::sync::{Mutex, MutexGuard, OnceLock};
|
||||
|
||||
fn env_lock() -> &'static Mutex<()> {
|
||||
static ENV_LOCK: OnceLock<Mutex<()>> = OnceLock::new();
|
||||
ENV_LOCK.get_or_init(|| Mutex::new(()))
|
||||
}
|
||||
|
||||
pub(crate) struct TestEnv {
|
||||
_guard: MutexGuard<'static, ()>,
|
||||
saved: BTreeMap<String, Option<OsString>>,
|
||||
}
|
||||
|
||||
impl TestEnv {
|
||||
pub(crate) fn new() -> Self {
|
||||
Self {
|
||||
_guard: env_lock().lock().expect("test env lock poisoned"),
|
||||
saved: BTreeMap::new(),
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) fn set_var(&mut self, key: &str, value: impl AsRef<OsStr>) {
|
||||
self.snapshot(key);
|
||||
unsafe {
|
||||
env::set_var(key, value);
|
||||
}
|
||||
}
|
||||
|
||||
fn snapshot(&mut self, key: &str) {
|
||||
self.saved
|
||||
.entry(key.to_string())
|
||||
.or_insert_with(|| env::var_os(key));
|
||||
}
|
||||
}
|
||||
|
||||
impl Drop for TestEnv {
|
||||
fn drop(&mut self) {
|
||||
for (key, value) in &self.saved {
|
||||
unsafe {
|
||||
if let Some(value) = value {
|
||||
env::set_var(key, value);
|
||||
} else {
|
||||
env::remove_var(key);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user