use std::collections::BTreeMap;
use std::env;
use tokio::process::Command;
pub(super) const BASE_ENV: &[&str] = &["PATH", "HOME", "LANG", "LC_ALL", "TZ"];
pub(super) fn apply(
child: &mut Command,
sheep: &str,
passthrough: &[String],
spec_env: &BTreeMap<String, String>,
) {
child.env_clear();
for key in BASE_ENV {
if let Some(value) = env::var_os(key) {
child.env(key, value);
}
}
for key in passthrough {
match env::var_os(key) {
Some(value) => {
child.env(key, value);
}
None => eprintln!(
"shep-deploy: {sheep}: passthrough names {key}, which is not set in this dog's \
environment, so the build does not get it"
),
}
}
child.envs(spec_env);
}
#[cfg(test)]
mod tests {
use super::super::test_support::{TEST_BUILD_BUDGET, build};
use super::super::{BuildSpec, run};
use crate::fixtures;
use std::collections::BTreeMap;
#[tokio::test]
async fn the_dogs_own_environment_does_not_reach_a_build() {
assert!(
std::env::var("CARGO_PKG_NAME").is_ok(),
"the probe variable must exist in this process or the test proves nothing"
);
let rel = fixtures::fixture_release(&[]);
let spec = BuildSpec {
command: Some("printenv CARGO_PKG_NAME > leaked.txt; true".into()),
env: BTreeMap::new(),
artifacts: vec![],
};
build(rel.path(), &spec).await.expect("builds");
let leaked = std::fs::read_to_string(rel.path().join("leaked.txt")).unwrap_or_default();
assert!(
leaked.trim().is_empty(),
"the build saw CARGO_PKG_NAME = {leaked:?}, so the environment was inherited"
);
}
#[tokio::test]
async fn the_base_environment_reaches_the_build() {
let rel = fixtures::fixture_release(&[]);
let spec = BuildSpec {
command: Some("printenv PATH > path.txt; printenv HOME > home.txt; true".into()),
env: BTreeMap::new(),
artifacts: vec![],
};
build(rel.path(), &spec).await.expect("builds");
for name in ["path.txt", "home.txt"] {
let value = std::fs::read_to_string(rel.path().join(name)).unwrap_or_default();
assert!(
!value.trim().is_empty(),
"{name} is empty, so a BASE_ENV variable did not reach the build"
);
}
}
#[tokio::test]
async fn a_named_passthrough_variable_reaches_the_build() {
let rel = fixtures::fixture_release(&[]);
let spec = BuildSpec {
command: Some("printenv CARGO_PKG_NAME > passed.txt; true".into()),
env: BTreeMap::new(),
artifacts: vec![],
};
run(
"web",
rel.path(),
&spec,
None,
&["CARGO_PKG_NAME".to_owned()],
fixtures::tempdir().path(),
TEST_BUILD_BUDGET,
)
.await
.expect("builds");
assert_eq!(
std::fs::read_to_string(rel.path().join("passed.txt"))
.expect("the build wrote it")
.trim(),
"shep-deploy"
);
}
}