Skip to content

Commit

Permalink
feat: add http cache
Browse files Browse the repository at this point in the history
  • Loading branch information
acesyde committed Jan 19, 2025
1 parent f675c8f commit 53c5308
Show file tree
Hide file tree
Showing 4 changed files with 41 additions and 27 deletions.
7 changes: 5 additions & 2 deletions docs/tasks/toml-tasks.md
Original file line number Diff line number Diff line change
Expand Up @@ -341,8 +341,11 @@ Task files can be fetched via http:
file = "https://example.com/build.sh"
```

Currently, they're fetched everytime they're executed, but we may add some cache support later.
This could be extended with other protocols like mentioned in [this ticket](https://github.com/jdx/mise/issues/2488) if there were interest.
Each task file is cached in the `MISE_CACHE_DIR` directory. If the file is updated, it will not be re-downloaded unless the cache is cleared.

:::tip
You can reset the cache by running `mise cache clear`.
:::

## Arguments

Expand Down
2 changes: 1 addition & 1 deletion src/cli/run.rs
Original file line number Diff line number Diff line change
Expand Up @@ -873,7 +873,7 @@ impl Run {
}

fn fetch_tasks(&self, tasks: &mut Vec<Task>) -> Result<()> {
let task_file_providers = TaskFileProviders::new(self.tmpdir.clone());
let task_file_providers = TaskFileProviders;

for t in tasks {
if let Some(file) = &t.file {
Expand Down
37 changes: 25 additions & 12 deletions src/task/file_providers/http_file_provider.rs
Original file line number Diff line number Diff line change
@@ -1,17 +1,20 @@
use std::path::PathBuf;

use md5::Digest;
use sha2::Sha256;

use crate::{file, http::HTTP};

use super::TaskFileProvider;

#[derive(Debug)]
pub struct HttpTaskFileProvider {
tmpdir: PathBuf,
cache_path: PathBuf,
}

impl HttpTaskFileProvider {
pub fn new(tmpdir: PathBuf) -> Self {
Self { tmpdir }
pub fn new(cache_path: PathBuf) -> Self {
Self { cache_path }
}
}

Expand All @@ -21,15 +24,25 @@ impl TaskFileProvider for HttpTaskFileProvider {
}

fn get_local_path(&self, file: &str) -> Result<PathBuf, Box<dyn std::error::Error>> {
let url = url::Url::parse(file)?;
let filename = url
.path_segments()
.and_then(|segments| segments.last())
.unwrap();
let tmp_path = self.tmpdir.join(filename);
HTTP.download_file(file, &tmp_path, None)?;
file::make_executable(&tmp_path)?;
Ok(tmp_path)
// Cache key is the full URL in sha256
let mut hasher = Sha256::new();
hasher.update(file);
let cache_key = format!("{:x}", hasher.finalize());
let cached_file_path = self.cache_path.join(&cache_key);

if cached_file_path.exists() {
debug!("Using cached file: {:?}", cached_file_path);
if let Ok(path) = cached_file_path.canonicalize() {
return Ok(path);
}
}

debug!("Downloading file: {}", file);

HTTP.download_file(file, &cached_file_path, None)?;
file::make_executable(&cached_file_path)?;

Ok(cached_file_path)
}
}

Expand Down
22 changes: 10 additions & 12 deletions src/task/file_providers/mod.rs
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
use std::sync::LazyLock as Lazy;
use std::{fmt::Debug, path::PathBuf};

mod http_file_provider;
Expand All @@ -6,48 +7,45 @@ mod local_file_provider;
pub use http_file_provider::HttpTaskFileProvider;
pub use local_file_provider::LocalTaskFileProvider;

use crate::dirs;

static CACHE_FILE_PROVIDERS: Lazy<PathBuf> = Lazy::new(|| dirs::CACHE.join("tasks-file-provider"));

pub trait TaskFileProvider: Debug {
fn is_match(&self, file: &str) -> bool;
fn get_local_path(&self, file: &str) -> Result<PathBuf, Box<dyn std::error::Error>>;
}

pub struct TaskFileProviders {
tmpdir: PathBuf,
}
pub struct TaskFileProviders;

impl TaskFileProviders {
fn get_providers(&self) -> Vec<Box<dyn TaskFileProvider>> {
vec![
Box::new(HttpTaskFileProvider::new(self.tmpdir.clone())),
Box::new(HttpTaskFileProvider::new(CACHE_FILE_PROVIDERS.clone())),
Box::new(LocalTaskFileProvider), // Must be the last provider
]
}

pub fn new(tmpdir: PathBuf) -> Self {
Self { tmpdir }
}

pub fn get_provider(&self, file: &str) -> Option<Box<dyn TaskFileProvider>> {
self.get_providers().into_iter().find(|p| p.is_match(file))
}
}

#[cfg(test)]
mod tests {
use std::env;

use super::*;

#[test]
fn test_get_providers() {
let task_file_providers = TaskFileProviders::new(env::temp_dir());
let task_file_providers = TaskFileProviders;
let providers = task_file_providers.get_providers();
assert_eq!(providers.len(), 2);
}

#[test]
fn test_local_file_match_local_provider() {
let task_file_providers = TaskFileProviders::new(env::temp_dir());
let task_file_providers = TaskFileProviders;
let cases = vec!["file.txt", "./file.txt", "../file.txt", "/file.txt"];

for file in cases {
Expand All @@ -59,7 +57,7 @@ mod tests {

#[test]
fn test_http_file_match_http_provider() {
let task_file_providers = TaskFileProviders::new(env::temp_dir());
let task_file_providers = TaskFileProviders;
let cases = vec![
"http://example.com/file.txt",
"https://example.com/file.txt",
Expand Down

0 comments on commit 53c5308

Please sign in to comment.