Skip to content

Commit 49a2b45

Browse files
authored
Merge pull request #104 from tower/tasks/do-not-inherit-full-environment
Remove inherited environments
1 parent 6acb28d commit 49a2b45

6 files changed

Lines changed: 104 additions & 0 deletions

File tree

crates/tower-runtime/tests/example-apps/04-app-with-secret/README.md

Whitespace-only changes.
Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
[app]
2+
name = "04-app-with-secret"
3+
script = "./main.py"
Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
import os
2+
3+
4+
def main():
5+
value = os.getenv("MY_SECRET", "default_value")
6+
print(f"The secret is: {value}")
7+
8+
9+
if __name__ == "__main__":
10+
main()
Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
[project]
2+
name = "04-app-with-secret"
3+
version = "0.1.0"
4+
description = "Add your description here"
5+
readme = "README.md"
6+
requires-python = ">=3.12"
7+
dependencies = []

crates/tower-runtime/tests/local_test.rs

Lines changed: 58 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -182,3 +182,61 @@ async fn test_running_legacy_app() {
182182
let status = app.status().await.expect("Failed to get app status");
183183
assert!(status == Status::Exited, "App should be running");
184184
}
185+
186+
#[tokio::test]
187+
async fn test_running_app_with_secret() {
188+
debug!("Running 04-app-with-secret");
189+
let app_dir = get_example_app_dir("04-app-with-secret");
190+
let package = build_package_from_dir(&app_dir).await;
191+
let (sender, mut receiver) = unbounded_channel();
192+
193+
let mut secrets = HashMap::new();
194+
secrets.insert("MY_SECRET".to_string(), "It's in the sauce!".to_string());
195+
196+
// We need to create the package, which will load the app
197+
let opts = StartOptions {
198+
ctx: tower_telemetry::Context::new(),
199+
package,
200+
output_sender: sender,
201+
cwd: None,
202+
environment: "local".to_string(),
203+
secrets: secrets,
204+
parameters: HashMap::new(),
205+
env_vars: HashMap::new(),
206+
};
207+
208+
// Start the app using the LocalApp runtime
209+
let app = LocalApp::start(opts).await.expect("Failed to start app");
210+
211+
// The status should be running
212+
let status = app.status().await.expect("Failed to get app status");
213+
assert!(status == Status::Running, "App should be running");
214+
215+
let mut count_setup = 0;
216+
let mut count_stdout = 0;
217+
218+
while let Some(output) = receiver.recv().await {
219+
match output.channel {
220+
tower_runtime::Channel::Setup => {
221+
// We always have some setup lines to count on.
222+
count_setup += 1;
223+
}
224+
tower_runtime::Channel::Program => {
225+
if output.line.starts_with("The secret is:") {
226+
// Indicate that we found the line.
227+
count_stdout += 1;
228+
229+
// Require that the right suffix is there.
230+
assert!(output.line.ends_with("It's in the sauce!"));
231+
}
232+
}
233+
}
234+
}
235+
236+
assert!(count_setup > 0, "There should be some setup output");
237+
assert!(count_stdout > 0, "should be more than one output");
238+
239+
// check the status once more, should be done.
240+
let status = app.status().await.expect("Failed to get app status");
241+
assert!(status == Status::Exited, "App should be running");
242+
}

crates/tower-uv/src/lib.rs

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -38,6 +38,27 @@ impl From<install::Error> for Error {
3838
}
3939
}
4040

41+
fn normalize_env_vars(env_vars: &HashMap<String, String>) -> HashMap<String, String> {
42+
#[cfg(windows)]
43+
{
44+
// we copy this locally so we can mutate the results.
45+
let mut env_vars = env_vars.clone();
46+
47+
// If we are running on Windows, we need to retain the SYSTEMROOT env var because Python
48+
// needs it to initialize it's random number generator. Fun fact!
49+
let systemroot = std::env::var("SYSTEMROOT").unwrap_or_default();
50+
env_vars.insert("SYSTEMROOT".to_string(), systemroot);
51+
return env_vars;
52+
}
53+
54+
#[cfg(not(windows))]
55+
{
56+
// On non-Windows platforms, we can just return the env vars as-is. We have to do this
57+
// clone thing to get rid fo the lifetime issues.
58+
return env_vars.clone();
59+
}
60+
}
61+
4162
async fn test_uv_path(path: &PathBuf) -> Result<(), Error> {
4263
let res = Command::new(&path)
4364
.arg("--color")
@@ -169,6 +190,10 @@ impl Uv {
169190
&self.uv_path, program, cwd
170191
);
171192

193+
// Sometimes, we need to copy some env vars out of the current environment and into the new
194+
// one!
195+
let env_vars = normalize_env_vars(env_vars);
196+
172197
let mut cmd = Command::new(&self.uv_path);
173198
cmd.kill_on_drop(true)
174199
.stdin(Stdio::null())
@@ -180,6 +205,7 @@ impl Uv {
180205
.arg("--no-progress")
181206
.arg("run")
182207
.arg(program)
208+
.env_clear()
183209
.envs(env_vars);
184210

185211
#[cfg(unix)]

0 commit comments

Comments
 (0)