From 31fe6dc52443a4dfbae0c45f2ee770f8970f8d58 Mon Sep 17 00:00:00 2001 From: Valentin Haudiquet Date: Sun, 20 Sep 2026 18:35:41 +0200 Subject: [PATCH] context: run commands in the C locale by default Subprocesses inherit the session environment, so a translated host locale leaked into builds: perl-based packaging tools (dpkg-architecture, dpkg-parsechangelog, quilt, ...) warned about missing locale settings, change their output with the environment, and dpkg-buildpackage treats some of that output as data. Default every command to LANG=C and LC_ALL=C; a caller can still override explicitly through envs(). --- src/context/api.rs | 39 ++++++++++++++++++++++++++++++++++++++- 1 file changed, 38 insertions(+), 1 deletion(-) diff --git a/src/context/api.rs b/src/context/api.rs index 3e10f71..9d61ea9 100644 --- a/src/context/api.rs +++ b/src/context/api.rs @@ -243,12 +243,22 @@ impl Context { } /// Make a command inside context + /// + /// Build tooling must not inherit the session's locale: dpkg-family + /// tools and perl-based packaging scripts change their output (and + /// dpkg-buildpackage treats some of it as data) with the environment, + /// and a translated or mixed locale leaks host state into builds. The + /// C locale is the default; a caller can still override it by setting + /// LANG/LC_ALL through [`ContextCommand::envs`] afterwards. pub fn command>(&self, program: S) -> ContextCommand<'_> { ContextCommand { context: self, program: program.as_ref().to_string_lossy().to_string(), args: Vec::new(), - env: Vec::new(), + env: vec![ + ("LANG".to_string(), "C".to_string()), + ("LC_ALL".to_string(), "C".to_string()), + ], cwd: None, sink: None, } @@ -530,4 +540,31 @@ mod endpoint_tests { let err = ContextConfig::from_endpoint("host:99999").unwrap_err(); assert_eq!(err, "Invalid port number"); } + + /// Commands run in the C locale whatever the session environment + /// carries: host locale variables must not leak into builds. An + /// explicit caller override still wins. + #[test] + fn commands_default_to_the_c_locale() { + let ctx = Context::new(ContextConfig::Local).unwrap(); + + let locale = ctx + .command("sh") + .arg("-c") + .arg("printf '%s' \"${LC_ALL:-unset}:${LANG:-unset}\"") + .output() + .unwrap() + .stdout; + assert_eq!(String::from_utf8_lossy(&locale), "C:C"); + + let locale = ctx + .command("sh") + .arg("-c") + .arg("printf '%s' \"$LC_ALL\"") + .env("LC_ALL", "C.UTF-8") + .output() + .unwrap() + .stdout; + assert_eq!(String::from_utf8_lossy(&locale), "C.UTF-8"); + } }