From 786f188923a968defed585b41fefa4a6965175de Mon Sep 17 00:00:00 2001 From: Lucas Fernandes Nogueira Date: Fri, 2 Aug 2024 10:04:08 -0300 Subject: [PATCH] feat(cli): allow using iOS device tun address for dev server (#10456) - Changes the IP prompt to also show IPV6 address ending with ::2 (usually device's address) - Adds --host option on ios dev to force the host - Also makes it work with our own dev server impl (builtin) --- examples/api/package.json | 3 +- examples/api/vite.config.js | 5 +- examples/api/yarn.lock | 55 +------------- tooling/cli/src/dev.rs | 12 ++- tooling/cli/src/mobile/android/dev.rs | 1 + tooling/cli/src/mobile/ios/dev.rs | 73 ++++++++++++++----- .../__example-api/tauri-app/package.json | 1 - .../__example-api/tauri-app/vite.config.js | 5 +- 8 files changed, 69 insertions(+), 86 deletions(-) diff --git a/examples/api/package.json b/examples/api/package.json index ff6f40177..b65d412c4 100644 --- a/examples/api/package.json +++ b/examples/api/package.json @@ -11,8 +11,7 @@ }, "dependencies": { "@tauri-apps/api": "../../tooling/api/dist", - "@zerodevx/svelte-json-view": "1.0.9", - "internal-ip": "^7.0.0" + "@zerodevx/svelte-json-view": "1.0.9" }, "devDependencies": { "@iconify-json/codicon": "^1.1.49", diff --git a/examples/api/vite.config.js b/examples/api/vite.config.js index 96f70723c..0609c048c 100644 --- a/examples/api/vite.config.js +++ b/examples/api/vite.config.js @@ -5,7 +5,6 @@ import { defineConfig } from 'vite' import Unocss from 'unocss/vite' import { svelte } from '@sveltejs/vite-plugin-svelte' -import { internalIpV4Sync } from 'internal-ip' const host = process.env.TAURI_DEV_HOST @@ -27,13 +26,13 @@ export default defineConfig({ clearScreen: false, // tauri expects a fixed port, fail if that port is not available server: { - host: host ? '0.0.0.0' : false, + host: host || false, port: 1420, strictPort: true, hmr: host ? { protocol: 'ws', - host: internalIpV4Sync(), + host: host, port: 1430 } : undefined, diff --git a/examples/api/yarn.lock b/examples/api/yarn.lock index bc6acf7ca..cf2e89d75 100644 --- a/examples/api/yarn.lock +++ b/examples/api/yarn.lock @@ -1015,13 +1015,6 @@ deepmerge@^4.3.1: resolved "https://registry.yarnpkg.com/deepmerge/-/deepmerge-4.3.1.tgz#44b5f2147cd3b00d4b56137685966f26fd25dd4a" integrity sha512-3sUqbMEc77XqpdNO7FRyRog+eW3ph+GYCbj+rK+uYyRMuwsVy0rMiVtPn+QJlKFvWP/1PYpapqYn0Me2knFn+A== -default-gateway@^6.0.3: - version "6.0.3" - resolved "https://registry.yarnpkg.com/default-gateway/-/default-gateway-6.0.3.tgz#819494c888053bdb743edbf343d6cdf7f2943a71" - integrity sha512-fwSOJsbbNzZ/CUFpqFBqYfYNLj1NbMPm8MMCIzHjC83iSJRBEGmDUxU+WP661BaBQImeC2yHwXtz+P/O9o+XEg== - dependencies: - execa "^5.0.0" - defu@^6.1.4: version "6.1.4" resolved "https://registry.yarnpkg.com/defu/-/defu-6.1.4.tgz#4e0c9cf9ff68fe5f3d7f2765cc1a012dfdcb0479" @@ -1098,7 +1091,7 @@ estree-walker@^3.0.0, estree-walker@^3.0.3: dependencies: "@types/estree" "^1.0.0" -execa@^5.0.0, execa@^5.1.1: +execa@^5.1.1: version "5.1.1" resolved "https://registry.yarnpkg.com/execa/-/execa-5.1.1.tgz#f80ad9cbf4298f7bd1d4c9555c21e93741c411dd" integrity sha512-8uSpZZocAZRBAPIEINJj3Lo9HyGitllczc27Eh5YYojjMFMn8yHMDMaUHE2Jqfq05D/wucwI4JGURyXt1vchyg== @@ -1190,26 +1183,6 @@ human-signals@^2.1.0: resolved "https://registry.yarnpkg.com/human-signals/-/human-signals-2.1.0.tgz#dc91fcba42e4d06e4abaed33b3e7a3c02f514ea0" integrity sha512-B4FFZ6q/T2jhhksgkbEW3HBvWIfDW85snkQgawt07S7J5QXTk6BkNV+0yAeZrM5QpMAdYlocGoljn0sJ/WQkFw== -internal-ip@^7.0.0: - version "7.0.0" - resolved "https://registry.yarnpkg.com/internal-ip/-/internal-ip-7.0.0.tgz#5b1c6a9d7e188aa73a1b69717daf50c8d8ed774f" - integrity sha512-qE4TeD4brqC45Vq/+VASeMiS1KRyfBkR6HT2sh9pZVVCzSjPkaCEfKFU+dL0PRv7NHJtvoKN2r82G6wTfzorkw== - dependencies: - default-gateway "^6.0.3" - ipaddr.js "^2.0.1" - is-ip "^3.1.0" - p-event "^4.2.0" - -ip-regex@^4.0.0: - version "4.3.0" - resolved "https://registry.yarnpkg.com/ip-regex/-/ip-regex-4.3.0.tgz#687275ab0f57fa76978ff8f4dddc8a23d5990db5" - integrity sha512-B9ZWJxHHOHUhUjCPrMpLD4xEq35bUTClHM1S6CBU5ixQnkZmwipwgc96vAd7AAGM9TGHvJR+Uss+/Ak6UphK+Q== - -ipaddr.js@^2.0.1: - version "2.2.0" - resolved "https://registry.yarnpkg.com/ipaddr.js/-/ipaddr.js-2.2.0.tgz#d33fa7bac284f4de7af949638c9d68157c6b92e8" - integrity sha512-Ag3wB2o37wslZS19hZqorUnrnzSkpOVy+IiiDEiTqNubEYpYuHWIf6K4psgN2ZWKExS4xhVCrRVfb/wfW8fWJA== - is-binary-path@~2.1.0: version "2.1.0" resolved "https://registry.yarnpkg.com/is-binary-path/-/is-binary-path-2.1.0.tgz#ea1f7f3b80f064236e83470f86c09c254fb45b09" @@ -1229,13 +1202,6 @@ is-glob@^4.0.1, is-glob@~4.0.1: dependencies: is-extglob "^2.1.1" -is-ip@^3.1.0: - version "3.1.0" - resolved "https://registry.yarnpkg.com/is-ip/-/is-ip-3.1.0.tgz#2ae5ddfafaf05cb8008a62093cf29734f657c5d8" - integrity sha512-35vd5necO7IitFPjd/YBeqwWnyDWbuLH9ZXQdMfDA8TEo7pv5X8yfrvVO3xbJbLUlERCMvf6X0hTUamQxCYJ9Q== - dependencies: - ip-regex "^4.0.0" - is-number@^7.0.0: version "7.0.0" resolved "https://registry.yarnpkg.com/is-number/-/is-number-7.0.0.tgz#7535345b896734d5f80c4d06c50955527a14f12b" @@ -1413,18 +1379,6 @@ onetime@^5.1.2: dependencies: mimic-fn "^2.1.0" -p-event@^4.2.0: - version "4.2.0" - resolved "https://registry.yarnpkg.com/p-event/-/p-event-4.2.0.tgz#af4b049c8acd91ae81083ebd1e6f5cae2044c1b5" - integrity sha512-KXatOjCRXXkSePPb1Nbi0p0m+gQAwdlbhi4wQKJPI1HsMQS9g+Sqp2o+QHziPr7eYJyOZet836KoHEVM1mwOrQ== - dependencies: - p-timeout "^3.1.0" - -p-finally@^1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/p-finally/-/p-finally-1.0.0.tgz#3fbcfb15b899a44123b34b6dcc18b724336a2cae" - integrity sha512-LICb2p9CB7FS+0eR1oqWnHhp0FljGLZCWBE9aix0Uye9W8LTQPwMTYVGWQWIw9RdQiDg4+epXQODwIYJtSJaow== - p-limit@^3.0.2: version "3.1.0" resolved "https://registry.yarnpkg.com/p-limit/-/p-limit-3.1.0.tgz#e1daccbe78d0d1388ca18c64fea38e3e57e3706b" @@ -1439,13 +1393,6 @@ p-locate@^5.0.0: dependencies: p-limit "^3.0.2" -p-timeout@^3.1.0: - version "3.2.0" - resolved "https://registry.yarnpkg.com/p-timeout/-/p-timeout-3.2.0.tgz#c7e17abc971d2a7962ef83626b35d635acf23dfe" - integrity sha512-rhIwUycgwwKcP9yTOOFK/AKsAopjjCakVqLHePO3CC6Mir1Z99xT+R63jZxAT5lFZLa2inS5h+ZS2GvR99/FBg== - dependencies: - p-finally "^1.0.0" - path-exists@^4.0.0: version "4.0.0" resolved "https://registry.yarnpkg.com/path-exists/-/path-exists-4.0.0.tgz#513bdbe2d3b95d7762e8c1137efa195c6c61b5b3" diff --git a/tooling/cli/src/dev.rs b/tooling/cli/src/dev.rs index 7597ed60e..0e00592b6 100644 --- a/tooling/cli/src/dev.rs +++ b/tooling/cli/src/dev.rs @@ -21,7 +21,7 @@ use tauri_utils::platform::Target; use std::{ env::set_current_dir, - net::Ipv4Addr, + net::{IpAddr, Ipv4Addr}, process::{exit, Command, Stdio}, sync::{ atomic::{AtomicBool, Ordering}, @@ -82,6 +82,9 @@ pub struct Options { /// Specify port for the built-in dev server for static files. Defaults to 1430. #[clap(long, env = "TAURI_CLI_PORT")] pub port: Option, + + #[clap(skip)] + pub host: Option, } pub fn command(options: Options) -> Result<()> { @@ -249,8 +252,11 @@ pub fn setup(interface: &AppInterface, options: &mut Options, config: ConfigHand if path.exists() { let path = path.canonicalize()?; - let server_url = - builtin_dev_server::start(path, Ipv4Addr::new(127, 0, 0, 1).into(), options.port)?; + let ip = options + .host + .unwrap_or_else(|| Ipv4Addr::new(127, 0, 0, 1).into()); + + let server_url = builtin_dev_server::start(path, ip, options.port)?; let server_url = format!("http://{server_url}"); dev_url = Some(server_url.parse().unwrap()); diff --git a/tooling/cli/src/mobile/android/dev.rs b/tooling/cli/src/mobile/android/dev.rs index bb55b2ce7..794dc6026 100644 --- a/tooling/cli/src/mobile/android/dev.rs +++ b/tooling/cli/src/mobile/android/dev.rs @@ -86,6 +86,7 @@ impl From for DevOptions { no_dev_server: options.no_dev_server, port: options.port, release_mode: options.release_mode, + host: None, } } } diff --git a/tooling/cli/src/mobile/ios/dev.rs b/tooling/cli/src/mobile/ios/dev.rs index 45f632928..7d2d7257c 100644 --- a/tooling/cli/src/mobile/ios/dev.rs +++ b/tooling/cli/src/mobile/ios/dev.rs @@ -32,7 +32,7 @@ use cargo_mobile2::{ use std::{ env::set_current_dir, - net::{IpAddr, Ipv4Addr}, + net::{IpAddr, Ipv4Addr, SocketAddr}, sync::OnceLock, }; @@ -76,6 +76,7 @@ pub struct Options { #[clap(long)] pub force_ip_prompt: bool, /// Use the public network address for the development server. + /// If an actual address it provided, it is used instead of prompting to pick one. /// /// This option is particularly useful along the `--open` flag when you intend on running on a physical device. /// @@ -87,7 +88,7 @@ pub struct Options { /// environment variable so you can check this on your framework's configuration to expose the development server /// on the public network address. #[clap(long)] - pub host: bool, + pub host: Option>, /// Disable the built-in dev server for static files. #[clap(long)] pub no_dev_server: bool, @@ -110,6 +111,7 @@ impl From for DevOptions { no_dev_server: options.no_dev_server, no_dev_server_wait: options.no_dev_server_wait, port: options.port, + host: None, } } } @@ -204,7 +206,8 @@ fn local_ip_address(force: bool) -> &'static IpAddr { .map(|(_, ipaddr)| ipaddr) .filter(|ipaddr| match ipaddr { IpAddr::V4(i) => i != &Ipv4Addr::LOCALHOST, - _ => false, + IpAddr::V6(i) => i.to_string().ends_with("::2"), + }) .collect(); match addresses.len() { @@ -239,8 +242,8 @@ fn local_ip_address(force: bool) -> &'static IpAddr { fn use_network_address_for_dev_url( config: &ConfigHandle, - config_extension: &mut Option, - force_ip_prompt: bool, + options: &mut Options, + dev_options: &mut DevOptions, ) -> crate::Result<()> { let mut dev_url = config .lock() @@ -251,7 +254,7 @@ fn use_network_address_for_dev_url( .dev_url .clone(); - if let Some(url) = &mut dev_url { + let ip = if let Some(url) = &mut dev_url { let localhost = match url.host() { Some(url::Host::Domain(d)) => d == "localhost", Some(url::Host::Ipv4(i)) => { @@ -261,15 +264,23 @@ fn use_network_address_for_dev_url( }; if localhost { - let ip = local_ip_address(force_ip_prompt).to_string(); - println!( - "Replacing devUrl host with {ip}. {}. {}.", - "If your frontend is not listening on that address, try configuring your development server to use 0.0.0.0 as host", - "When this is required, Tauri sets the TAURI_DEV_HOST environment variable" + let ip = options + .host + .unwrap_or_default() + .unwrap_or_else(|| *local_ip_address(options.force_ip_prompt)); + log::info!( + "Replacing devUrl host with {ip}. {}.", + "If your frontend is not listening on that address, try configuring your development server to use the `TAURI_DEV_HOST` environment variable or 0.0.0.0 as host" ); - url.set_host(Some(&ip)).unwrap(); - if let Some(c) = config_extension { + *url = url::Url::parse(&format!( + "{}://{}{}", + url.scheme(), + SocketAddr::new(ip, url.port_or_known_default().unwrap()), + url.path() + ))?; + + if let Some(c) = &mut options.config { if let Some(build) = c .0 .as_object_mut() @@ -282,11 +293,34 @@ fn use_network_address_for_dev_url( let mut build = serde_json::Map::new(); build.insert("devUrl".into(), url.to_string().into()); - config_extension.replace(crate::ConfigValue(serde_json::json!({ - "build": build - }))); + options + .config + .replace(crate::ConfigValue(serde_json::json!({ + "build": build + }))); } - reload_config(config_extension.as_ref().map(|c| &c.0))?; + reload_config(options.config.as_ref().map(|c| &c.0))?; + + Some(ip) + } else { + None + } + } else if !dev_options.no_dev_server { + let ip = options + .host + .unwrap_or_default() + .unwrap_or_else(|| *local_ip_address(options.force_ip_prompt)); + dev_options.host.replace(ip.clone()); + Some(ip) + } else { + None + }; + + if let Some(ip) = ip { + std::env::set_var("TAURI_DEV_HOST", ip.to_string()); + if ip.is_ipv6() { + // in this case we can't ping the server for some reason + dev_options.no_dev_server_wait = true; } } @@ -306,14 +340,13 @@ fn run_dev( noise_level: NoiseLevel, ) -> Result<()> { // when running on an actual device we must use the network IP - if options.host + if options.host.is_some() || device .as_ref() .map(|device| !matches!(device.kind(), DeviceKind::Simulator)) .unwrap_or(false) { - std::env::set_var("TAURI_DEV_HOST", "true"); - use_network_address_for_dev_url(&tauri_config, &mut options.config, options.force_ip_prompt)?; + use_network_address_for_dev_url(&tauri_config, &mut options, &mut dev_options)?; } crate::dev::setup(&interface, &mut dev_options, tauri_config.clone())?; diff --git a/tooling/cli/templates/plugin/__example-api/tauri-app/package.json b/tooling/cli/templates/plugin/__example-api/tauri-app/package.json index 8a6e5373f..e890ca1d3 100644 --- a/tooling/cli/templates/plugin/__example-api/tauri-app/package.json +++ b/tooling/cli/templates/plugin/__example-api/tauri-app/package.json @@ -15,7 +15,6 @@ }, "devDependencies": { "@sveltejs/vite-plugin-svelte": "^1.0.1", - "internal-ip": "^7.0.0", "svelte": "^3.49.0", "vite": "^3.0.2", "@tauri-apps/cli": "^2.0.0-alpha.17" diff --git a/tooling/cli/templates/plugin/__example-api/tauri-app/vite.config.js b/tooling/cli/templates/plugin/__example-api/tauri-app/vite.config.js index bfad25c62..3b85afb95 100644 --- a/tooling/cli/templates/plugin/__example-api/tauri-app/vite.config.js +++ b/tooling/cli/templates/plugin/__example-api/tauri-app/vite.config.js @@ -1,6 +1,5 @@ import { defineConfig } from "vite"; import { svelte } from "@sveltejs/vite-plugin-svelte"; -import { internalIpV4Sync } from 'internal-ip'; const host = process.env.TAURI_DEV_HOST; @@ -13,12 +12,12 @@ export default defineConfig({ clearScreen: false, // tauri expects a fixed port, fail if that port is not available server: { - host: host ? "0.0.0.0" : false, + host: host || false, port: 1420, strictPort: true, hmr: host ? { protocol: 'ws', - host: internalIpV4Sync(), + host, port: 1421 } : undefined, },