Skip to content

Commit 2f60a5d

Browse files
committed
Use 'url' crate to manage file URLs
Treating them as strings works fine for *nix OSes, but breaks for Windows. Also, standardize on "URL" vs "URI" in spin-trigger crate. Signed-off-by: Lann Martin <lann.martin@fermyon.com>
1 parent b5b08a7 commit 2f60a5d

File tree

4 files changed

+31
-24
lines changed

4 files changed

+31
-24
lines changed

crates/trigger/src/lib.rs

Lines changed: 13 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@ use std::{
99
path::{Path, PathBuf},
1010
};
1111

12-
use anyhow::{Context, Result};
12+
use anyhow::{anyhow, Context, Result};
1313
pub use async_trait::async_trait;
1414
use serde::de::DeserializeOwned;
1515

@@ -130,9 +130,11 @@ impl<Executor: TriggerExecutor> TriggerExecutorBuilder<Executor> {
130130

131131
pub fn default_config_providers(&self, app_uri: &str) -> Vec<Box<dyn Provider>> {
132132
// EnvProvider
133-
let dotenv_path = app_uri
134-
.strip_prefix("file://")
135-
.and_then(|path| Path::new(path).parent())
133+
// Look for a .env file in either the manifest parent directory for local apps
134+
// or the current directory for remote (e.g. bindle) apps.
135+
let dotenv_path = parse_file_url(app_uri)
136+
.as_deref()
137+
.ok()
136138
.unwrap_or_else(|| Path::new("."))
137139
.join(".env");
138140
vec![Box::new(EnvProvider::new(
@@ -280,3 +282,10 @@ impl<Executor: TriggerExecutor> TriggerAppEngine<Executor> {
280282
Ok((instance, store))
281283
}
282284
}
285+
286+
pub(crate) fn parse_file_url(url: &str) -> Result<PathBuf> {
287+
url::Url::parse(url)
288+
.with_context(|| format!("Invalid URL: {url:?}"))?
289+
.to_file_path()
290+
.map_err(|_| anyhow!("Invalid file URL path: {url:?}"))
291+
}

crates/trigger/src/loader.rs

Lines changed: 9 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
#![allow(dead_code)] // Refactor WIP
22

3-
use std::path::{Path, PathBuf};
3+
use std::path::PathBuf;
44

55
use anyhow::{ensure, Context, Result};
66
use async_trait::async_trait;
@@ -10,6 +10,8 @@ use spin_app::{
1010
};
1111
use spin_core::StoreBuilder;
1212

13+
use crate::parse_file_url;
14+
1315
pub struct TriggerLoader {
1416
working_dir: PathBuf,
1517
allow_transient_write: bool,
@@ -26,10 +28,10 @@ impl TriggerLoader {
2628

2729
#[async_trait]
2830
impl Loader for TriggerLoader {
29-
async fn load_app(&self, uri: &str) -> Result<LockedApp> {
30-
let path = unwrap_file_uri(uri)?;
31+
async fn load_app(&self, url: &str) -> Result<LockedApp> {
32+
let path = parse_file_url(url)?;
3133
let contents =
32-
std::fs::read(path).with_context(|| format!("failed to read manifest at {path:?}"))?;
34+
std::fs::read(&path).with_context(|| format!("failed to read manifest at {path:?}"))?;
3335
let app =
3436
serde_json::from_slice(&contents).context("failed to parse app lock file JSON")?;
3537
Ok(app)
@@ -45,8 +47,8 @@ impl Loader for TriggerLoader {
4547
.source
4648
.as_ref()
4749
.context("LockedComponentSource missing source field")?;
48-
let path = unwrap_file_uri(source)?;
49-
spin_core::Module::from_file(engine, path)
50+
let path = parse_file_url(source)?;
51+
spin_core::Module::from_file(engine, &path)
5052
.with_context(|| format!("loading module {path:?}"))
5153
}
5254

@@ -61,7 +63,7 @@ impl Loader for TriggerLoader {
6163
.source
6264
.as_deref()
6365
.with_context(|| format!("Missing 'source' on files mount {content_dir:?}"))?;
64-
let source_path = self.working_dir.join(unwrap_file_uri(source_uri)?);
66+
let source_path = self.working_dir.join(parse_file_url(source_uri)?);
6567
ensure!(
6668
source_path.is_dir(),
6769
"TriggerLoader only supports directory mounts; {source_path:?} is not a directory"
@@ -76,10 +78,3 @@ impl Loader for TriggerLoader {
7678
Ok(())
7779
}
7880
}
79-
80-
fn unwrap_file_uri(uri: &str) -> Result<&Path> {
81-
Ok(Path::new(
82-
uri.strip_prefix("file://")
83-
.context("TriggerLoader supports only file:// URIs")?,
84-
))
85-
}

crates/trigger/src/locked.rs

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -47,7 +47,7 @@ impl LockedAppBuilder {
4747
.string("version", &info.version)
4848
.string_option("description", info.description.as_deref())
4949
.serializable("trigger", info.trigger)?;
50-
// Convert ApplicationOrigin to a URI
50+
// Convert ApplicationOrigin to a URL
5151
builder.string(
5252
"origin",
5353
match info.origin {
@@ -185,13 +185,13 @@ fn content_ref_path(path: &Path) -> Result<ContentRef> {
185185

186186
fn file_uri(path: &Path) -> Result<String> {
187187
let path = path.canonicalize()?;
188-
let uri = if path.is_dir() {
188+
let url = if path.is_dir() {
189189
url::Url::from_directory_path(&path)
190190
} else {
191191
url::Url::from_file_path(&path)
192192
}
193-
.map_err(|_| anyhow!("Could not construct file URI for {path:?}"))?;
194-
Ok(uri.to_string())
193+
.map_err(|_| anyhow!("Could not construct file URL for {path:?}"))?;
194+
Ok(url.to_string())
195195
}
196196

197197
#[cfg(test)]

src/commands/up.rs

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -4,8 +4,9 @@ use std::{
44
path::{Path, PathBuf},
55
};
66

7-
use anyhow::{bail, Context, Result};
7+
use anyhow::{anyhow, bail, Context, Result};
88
use clap::{CommandFactory, Parser};
9+
use reqwest::Url;
910
use spin_loader::bindle::BindleConnectionInfo;
1011
use spin_manifest::ApplicationTrigger;
1112
use spin_trigger::cli::{SPIN_LOCKED_URL, SPIN_WORKING_DIR};
@@ -154,7 +155,9 @@ impl UpCommand {
154155
serde_json::to_vec_pretty(&locked_app).context("failed to serialize locked app")?;
155156
std::fs::write(&locked_path, locked_app_contents)
156157
.with_context(|| format!("failed to write {:?}", locked_path))?;
157-
let locked_url = format!("file://{}", locked_path.to_string_lossy());
158+
let locked_url = Url::from_file_path(&locked_path)
159+
.map_err(|_| anyhow!("cannot convert to file URL: {locked_path:?}"))?
160+
.to_string();
158161

159162
// For `spin up --help`, we just want the executor to dump its own argument usage info
160163
let trigger_args = if self.help {

0 commit comments

Comments
 (0)