Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
22 changes: 11 additions & 11 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ resolver = "2"

[workspace.package]
edition = "2021"
version = "0.3.35"
version = "0.3.36"
description = "Tower is the best way to host Python data apps in production"
rust-version = "1.81"
authors = ["Brad Heller <brad@tower.dev>"]
Expand Down
14 changes: 14 additions & 0 deletions crates/config/src/lib.rs
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
use std::path::PathBuf;
use serde::{Deserialize, Serialize};
use tower_api::apis::configuration::Configuration;
use url::Url;
Expand All @@ -20,6 +21,9 @@ pub struct Config {

#[serde(skip_serializing, skip_deserializing)]
pub session: Option<Session>,

// cache_dir is the directory that we should cache uv artifacts within.
pub cache_dir: Option<PathBuf>,
}

impl Config {
Expand All @@ -29,6 +33,7 @@ impl Config {
tower_url: default_tower_url(),
json: false,
session: None,
cache_dir: Some(default_cache_dir()),
}
}

Expand All @@ -45,6 +50,7 @@ impl Config {
tower_url,
json: false,
session: None,
cache_dir: Some(default_cache_dir()),
}
}

Expand Down Expand Up @@ -72,6 +78,7 @@ impl Config {
tower_url: sess.tower_url.clone(),
json: self.json,
session: Some(sess),
cache_dir: Some(default_cache_dir()),
}
}

Expand Down Expand Up @@ -200,3 +207,10 @@ impl From<&Config> for Configuration {
config.make_api_configuration()
}
}

// default_cache_dir gets the path the default cache location for dependencies, etc. Note
// that you don't have to create underlying directory, uv will do that automagically for us.
pub fn default_cache_dir() -> PathBuf {
let dir = dirs::data_local_dir().unwrap();
dir.join("tower").join("cache")
}
1 change: 1 addition & 0 deletions crates/tower-cmd/src/run.rs
Original file line number Diff line number Diff line change
Expand Up @@ -161,6 +161,7 @@ where
secrets,
params,
env_vars,
config.cache_dir,
)
.await?;

Expand Down
3 changes: 3 additions & 0 deletions crates/tower-runtime/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -80,6 +80,7 @@ impl<A: App> AppLauncher<A> {
secrets: HashMap<String, String>,
parameters: HashMap<String, String>,
env_vars: HashMap<String, String>,
cache_dir: Option<PathBuf>,
) -> Result<(), Error> {
let cwd = package.unpacked_path.clone().unwrap().to_path_buf();

Expand All @@ -92,6 +93,7 @@ impl<A: App> AppLauncher<A> {
parameters,
package,
env_vars,
cache_dir,
};

// NOTE: This is a really awful hack to force any existing app to drop itself. Not certain
Expand Down Expand Up @@ -134,6 +136,7 @@ pub struct StartOptions {
pub parameters: HashMap<String, String>,
pub env_vars: HashMap<String, String>,
pub output_sender: OutputSender,
pub cache_dir: Option<PathBuf>,
}

pub struct ExecuteOptions {
Expand Down
5 changes: 4 additions & 1 deletion crates/tower-runtime/src/local.rs
Original file line number Diff line number Diff line change
Expand Up @@ -178,7 +178,10 @@ async fn execute_local_app(

let _ = sx.send(wait_for_process(ctx.clone(), &cancel_token, child).await);
} else {
let uv = Uv::new().await?;
// we put Uv in to protected mode when there's no caching configured/enabled.
let protected_mode = opts.cache_dir.is_none();

let uv = Uv::new(opts.cache_dir, protected_mode).await?;
let env_vars = make_env_vars(
&ctx,
&environment,
Expand Down
6 changes: 6 additions & 0 deletions crates/tower-runtime/tests/local_test.rs
Original file line number Diff line number Diff line change
Expand Up @@ -56,6 +56,7 @@ async fn test_running_hello_world() {
secrets: HashMap::new(),
parameters: HashMap::new(),
env_vars: HashMap::new(),
cache_dir: Some(config::default_cache_dir())
};

// Start the app using the LocalApp runtime
Expand Down Expand Up @@ -100,6 +101,7 @@ async fn test_running_use_faker() {
secrets: HashMap::new(),
parameters: HashMap::new(),
env_vars: HashMap::new(),
cache_dir: Some(config::default_cache_dir()),
};

// Start the app using the LocalApp runtime
Expand Down Expand Up @@ -151,6 +153,7 @@ async fn test_running_legacy_app() {
secrets: HashMap::new(),
parameters: HashMap::new(),
env_vars: HashMap::new(),
cache_dir: Some(config::default_cache_dir())
};

// Start the app using the LocalApp runtime
Expand Down Expand Up @@ -210,6 +213,9 @@ async fn test_running_app_with_secret() {
secrets: secrets,
parameters: HashMap::new(),
env_vars: HashMap::new(),

// NOTE: No cache dir indicates that we want to run in protected mode.
cache_dir: None,
};

// Start the app using the LocalApp runtime
Expand Down
50 changes: 40 additions & 10 deletions crates/tower-uv/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -94,14 +94,22 @@ async fn test_uv_path(path: &PathBuf) -> Result<(), Error> {

pub struct Uv {
pub uv_path: PathBuf,

// cache_dir is the directory that dependencies should be cached in.
cache_dir: Option<PathBuf>,

// protected_mode is a flag that indicates whether the UV instance is in protected mode.
// In protected mode, the UV instance do things like clear the environment variables before
// use, etc.
protected_mode: bool,
}

impl Uv {
pub async fn new() -> Result<Self, Error> {
pub async fn new(cache_dir: Option<PathBuf>, protected_mode: bool) -> Result<Self, Error> {
match install::find_or_setup_uv().await {
Ok(uv_path) => {
test_uv_path(&uv_path).await?;
Ok(Uv { uv_path })
Ok(Uv { uv_path, cache_dir, protected_mode })
}
Err(e) => {
debug!("Error setting up UV: {:?}", e);
Expand All @@ -117,15 +125,20 @@ impl Uv {
) -> Result<Child, Error> {
debug!("Executing UV ({:?}) venv in {:?}", &self.uv_path, cwd);

let child = Command::new(&self.uv_path)
.kill_on_drop(true)
let mut cmd = Command::new(&self.uv_path);
cmd.kill_on_drop(true)
.stdin(Stdio::null())
.stdout(Stdio::piped())
.stderr(Stdio::piped())
.current_dir(cwd)
.arg("venv")
.envs(env_vars)
.spawn()?;
.envs(env_vars);

if let Some(dir) = &self.cache_dir {
cmd.arg("--cache-dir").arg(dir);
}

let child = cmd.spawn()?;

Ok(child)
}
Expand Down Expand Up @@ -156,6 +169,10 @@ impl Uv {
cmd.process_group(0);
}

if let Some(dir) = &self.cache_dir {
cmd.arg("--cache-dir").arg(dir);
}

let child = cmd.spawn()?;

Ok(child)
Expand Down Expand Up @@ -185,6 +202,10 @@ impl Uv {
cmd.process_group(0);
}

if let Some(dir) = &self.cache_dir {
cmd.arg("--cache-dir").arg(dir);
}

let child = cmd.spawn()?;

Ok(child)
Expand Down Expand Up @@ -219,15 +240,24 @@ impl Uv {
.arg("never")
.arg("--no-progress")
.arg("run")
.arg(program)
.env_clear()
.envs(env_vars);

.arg(program);

#[cfg(unix)]
{
cmd.process_group(0);
}

if self.protected_mode {
cmd.env_clear();
}

// Need to do this after env_clear intentionally.
cmd.envs(env_vars);

if let Some(dir) = &self.cache_dir {
cmd.arg("--cache-dir").arg(dir);
}

let child = cmd.spawn()?;
Ok(child)
}
Expand Down
2 changes: 1 addition & 1 deletion crates/tower-uv/tests/install_test.rs
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,6 @@ async fn test_installing_uv() {
let _ = tokio::fs::remove_dir_all(&default_uv_bin_dir).await;

// Now if we instantiate a Uv instance, it should install the `uv` binary.
let uv = Uv::new().await.expect("Failed to create a Uv instance");
let uv = Uv::new(None, false).await.expect("Failed to create a Uv instance");
assert!(uv.is_valid().await);
}
2 changes: 1 addition & 1 deletion pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ build-backend = "maturin"

[project]
name = "tower"
version = "0.3.35"
version = "0.3.36"
description = "Tower CLI and runtime environment for Tower."
authors = [{ name = "Tower Computing Inc.", email = "brad@tower.dev" }]
readme = "README.md"
Expand Down
2 changes: 1 addition & 1 deletion uv.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Loading