diff --git a/.changes/bundler-github-mirror-template.md b/.changes/bundler-github-mirror-template.md new file mode 100644 index 000000000..70cb0cfaf --- /dev/null +++ b/.changes/bundler-github-mirror-template.md @@ -0,0 +1,6 @@ +--- +"tauri-bundler": patch:feat +"tauri-cli": patch:feat +--- + +Add the `TAURI_BUNDLER_TOOLS_GITHUB_MIRROR_TEMPLATE` environment variable to specify a more accessible mirror template, facilitating companies, organizations, or individuals who cannot access GitHub to download the necessary files through their own mirror servers. diff --git a/crates/tauri-bundler/Cargo.toml b/crates/tauri-bundler/Cargo.toml index b098e4a68..13bf85f8c 100644 --- a/crates/tauri-bundler/Cargo.toml +++ b/crates/tauri-bundler/Cargo.toml @@ -44,6 +44,7 @@ zip = { version = "2.0", default-features = false, features = ["deflate"] } dunce = "1" url = "2" uuid = { version = "1", features = ["v4", "v5"] } +regex = "1" [target."cfg(target_os = \"windows\")".dependencies] bitness = "0.4" @@ -60,9 +61,6 @@ time = { version = "0.3", features = ["formatting"] } plist = "1" tauri-macos-sign = { version = "0.1.1-rc.0", path = "../tauri-macos-sign" } -[target."cfg(any(target_os = \"macos\", target_os = \"windows\"))".dependencies] -regex = "1" - [target."cfg(target_os = \"linux\")".dependencies] heck = "0.5" ar = "0.9.0" diff --git a/crates/tauri-bundler/src/bundle/windows/util.rs b/crates/tauri-bundler/src/bundle/windows/util.rs index a8b693d4e..bb114623f 100644 --- a/crates/tauri-bundler/src/bundle/windows/util.rs +++ b/crates/tauri-bundler/src/bundle/windows/util.rs @@ -8,8 +8,8 @@ use std::{ path::{Path, PathBuf}, }; +use regex::Regex; use sha2::Digest; -use ureq::AgentBuilder; use url::Url; use zip::ZipArchive; @@ -69,24 +69,53 @@ pub fn download_webview2_offline_installer(base_path: &Path, arch: &str) -> crat Ok(file_path) } -fn create_agent_and_url(url: &str) -> crate::Result<(ureq::Agent, String)> { - match std::env::var("TAURI_BUNDLER_TOOLS_GITHUB_MIRROR") { - Ok(cdn) if url.starts_with("https://github.com/") => { - let mut parsed_cdn = Url::parse(&cdn)?; - parsed_cdn.set_path(url); - Ok((AgentBuilder::new().build(), parsed_cdn.into())) - } - _ => Ok(( - AgentBuilder::new().try_proxy_from_env(true).build(), - url.to_owned(), - )), +fn generate_github_mirror_url_from_template(github_url: &str) -> Option { + std::env::var("TAURI_BUNDLER_TOOLS_GITHUB_MIRROR_TEMPLATE") + .ok() + .and_then(|template| { + let re = + Regex::new(r"https://github.com/([^/]+)/([^/]+)/releases/download/([^/]+)/(.*)").unwrap(); + re.captures(github_url).map(|caps| { + template + .replace("", &caps[1]) + .replace("", &caps[2]) + .replace("", &caps[3]) + .replace("", &caps[4]) + }) + }) +} + +fn generate_github_mirror_url_from_base(github_url: &str) -> Option { + std::env::var("TAURI_BUNDLER_TOOLS_GITHUB_MIRROR") + .ok() + .and_then(|cdn| Url::parse(&cdn).ok()) + .map(|mut cdn| { + cdn.set_path(github_url); + cdn.to_string() + }) +} + +fn generate_github_alternative_url(url: &str) -> Option<(ureq::Agent, String)> { + if !url.starts_with("https://github.com/") { + return None; } + + generate_github_mirror_url_from_template(url) + .or_else(|| generate_github_mirror_url_from_base(url)) + .map(|alt_url| (ureq::AgentBuilder::new().build(), alt_url)) +} + +fn create_agent_and_url(url: &str) -> (ureq::Agent, String) { + generate_github_alternative_url(url).unwrap_or(( + ureq::AgentBuilder::new().try_proxy_from_env(true).build(), + url.to_owned(), + )) } pub fn download(url: &str) -> crate::Result> { - log::info!(action = "Downloading"; "{}", url); + let (agent, final_url) = create_agent_and_url(url); - let (agent, final_url) = create_agent_and_url(url)?; + log::info!(action = "Downloading"; "{}", final_url); let response = agent.get(&final_url).call().map_err(Box::new)?; let mut bytes = Vec::new(); @@ -196,3 +225,57 @@ pub fn os_bitness<'a>() -> Option<&'a str> { _ => None, } } + +#[cfg(test)] +mod tests { + use super::generate_github_mirror_url_from_template; + use std::env; + + const GITHUB_ASSET_URL: &str = + "https://github.com/wixtoolset/wix3/releases/download/wix3112rtm/wix311-binaries.zip"; + const NON_GITHUB_ASSET_URL: &str = "https://someotherwebsite.com/somefile.zip"; + + #[test] + fn test_generate_mirror_url_no_env_var() { + env::remove_var("TAURI_BUNDLER_TOOLS_GITHUB_MIRROR_TEMPLATE"); + + assert!(generate_github_mirror_url_from_template(GITHUB_ASSET_URL).is_none()); + } + + #[test] + fn test_generate_mirror_url_non_github_url() { + env::set_var( + "TAURI_BUNDLER_TOOLS_GITHUB_MIRROR_TEMPLATE", + "https://mirror.example.com///releases/download//", + ); + + assert!(generate_github_mirror_url_from_template(NON_GITHUB_ASSET_URL).is_none()); + } + + struct TestCase { + template: &'static str, + expected_url: &'static str, + } + + #[test] + fn test_generate_mirror_url_correctly() { + let test_cases = vec![ + TestCase { + template: "https://mirror.example.com///releases/download//", + expected_url: "https://mirror.example.com/wixtoolset/wix3/releases/download/wix3112rtm/wix311-binaries.zip", + }, + TestCase { + template: "https://mirror.example.com/", + expected_url: "https://mirror.example.com/wix311-binaries.zip", + }, + ]; + + for case in test_cases { + env::set_var("TAURI_BUNDLER_TOOLS_GITHUB_MIRROR_TEMPLATE", case.template); + assert_eq!( + generate_github_mirror_url_from_template(GITHUB_ASSET_URL), + Some(case.expected_url.to_string()) + ); + } + } +} diff --git a/crates/tauri-cli/ENVIRONMENT_VARIABLES.md b/crates/tauri-cli/ENVIRONMENT_VARIABLES.md index 65ae17749..ed40ed865 100644 --- a/crates/tauri-cli/ENVIRONMENT_VARIABLES.md +++ b/crates/tauri-cli/ENVIRONMENT_VARIABLES.md @@ -16,6 +16,7 @@ These environment variables are inputs to the CLI which may have an equivalent C - `TAURI_LINUX_AYATANA_APPINDICATOR` — Set this var to `true` or `1` to force usage of `libayatana-appindicator` for system tray on Linux. - `TAURI_BUNDLER_WIX_FIPS_COMPLIANT` — Specify the bundler's WiX `FipsCompliant` option. - `TAURI_BUNDLER_TOOLS_GITHUB_MIRROR` - Specify a GitHub mirror to download files and tools used by tauri bundler. +- `TAURI_BUNDLER_TOOLS_GITHUB_MIRROR_TEMPLATE` - Specify a GitHub mirror template to download files and tools used by tauri bundler, for example: `https://mirror.example.com///releases/download//`. - `TAURI_SKIP_SIDECAR_SIGNATURE_CHECK` - Skip signing sidecars. - `TAURI_SIGNING_PRIVATE_KEY` — Private key used to sign your app bundles, can be either a string or a path to the file. - `TAURI_SIGNING_PRIVATE_KEY_PASSWORD` — The signing private key password, see `TAURI_SIGNING_PRIVATE_KEY`.