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
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")
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I wonder if this should be a hidden folder (prefixed with .)

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Typically it ends up in the .local dir in your home directory (which is way data_local_dir resolves to in this package) so it's kinda hidden.

}
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.
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is protected mode something that comes from UV itself or our own terminology?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It's my own fancy new term :) We'll need to iterate on this a bit.

// In protected mode, the UV instance do things like clear the environment variables before
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
// In protected mode, the UV instance do things like clear the environment variables before
// In protected mode, the UV instance does 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);
}
Loading