Skip to content

Commit a2d5929

Browse files
feat(cli.rs): wait for dev URL to be reachable, exit if command fails (#3358)
Co-authored-by: Amr Bashir <amr.bashir2015@gmail.com>
1 parent 3035e45 commit a2d5929

File tree

4 files changed

+106
-19
lines changed

4 files changed

+106
-19
lines changed

.changes/dev-server-exit.md

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
---
2+
"cli.rs": patch
3+
---
4+
5+
Kill process if `beforeDevCommand` exits with a non-zero status code.

.changes/wait-dev-server-url.md

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
---
2+
"cli.rs": patch
3+
---
4+
5+
Wait for `devPath` URL to be reachable before starting the application. Skipped if the `TAURI_SKIP_DEVSERVER_CHECK` environment variable is set to `true`.

tooling/cli.rs/src/dev.rs

Lines changed: 95 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@ use crate::{
66
helpers::{
77
app_paths::{app_dir, tauri_dir},
88
command_env,
9-
config::{get as get_config, reload as reload_config},
9+
config::{get as get_config, reload as reload_config, AppUrl, WindowUrl},
1010
manifest::{get_workspace_members, rewrite_manifest},
1111
Logger,
1212
},
@@ -22,15 +22,15 @@ use shared_child::SharedChild;
2222
use std::{
2323
env::set_current_dir,
2424
ffi::OsStr,
25-
process::{exit, Child, Command},
25+
process::{exit, Command},
2626
sync::{
2727
mpsc::{channel, Receiver},
2828
Arc, Mutex,
2929
},
3030
time::Duration,
3131
};
3232

33-
static BEFORE_DEV: OnceCell<Mutex<Child>> = OnceCell::new();
33+
static BEFORE_DEV: OnceCell<Mutex<Arc<SharedChild>>> = OnceCell::new();
3434

3535
#[derive(Debug, Parser)]
3636
#[clap(about = "Tauri dev")]
@@ -107,22 +107,42 @@ pub fn command(options: Options) -> Result<()> {
107107
if !before_dev.is_empty() {
108108
logger.log(format!("Running `{}`", before_dev));
109109
#[cfg(target_os = "windows")]
110-
let child = Command::new("cmd")
111-
.arg("/S")
112-
.arg("/C")
113-
.arg(before_dev)
114-
.current_dir(app_dir())
115-
.envs(command_env(true)) // development build always includes debug information
116-
.spawn()
117-
.with_context(|| format!("failed to run `{}` with `cmd /C`", before_dev))?;
110+
let mut command = {
111+
let mut command = Command::new("cmd");
112+
command
113+
.arg("/S")
114+
.arg("/C")
115+
.arg(before_dev)
116+
.current_dir(app_dir())
117+
.envs(command_env(true)); // development build always includes debug information
118+
command
119+
};
118120
#[cfg(not(target_os = "windows"))]
119-
let child = Command::new("sh")
120-
.arg("-c")
121-
.arg(before_dev)
122-
.current_dir(app_dir())
123-
.envs(command_env(true)) // development build always includes debug information
124-
.spawn()
125-
.with_context(|| format!("failed to run `{}` with `sh -c`", before_dev))?;
121+
let mut command = {
122+
let mut command = Command::new("sh");
123+
command
124+
.arg("-c")
125+
.arg(before_dev)
126+
.current_dir(app_dir())
127+
.envs(command_env(true)); // development build always includes debug information
128+
command
129+
};
130+
131+
let child = SharedChild::spawn(&mut command)
132+
.unwrap_or_else(|_| panic!("failed to run `{}`", before_dev));
133+
let child = Arc::new(child);
134+
let child_ = child.clone();
135+
let logger_ = logger.clone();
136+
std::thread::spawn(move || {
137+
let status = child_
138+
.wait()
139+
.expect("failed to wait on \"beforeDevCommand\"");
140+
if !status.success() {
141+
logger_.error("The \"beforeDevCommand\" terminated with a non-zero status code.");
142+
exit(status.code().unwrap_or(1));
143+
}
144+
});
145+
126146
BEFORE_DEV.set(Mutex::new(child)).unwrap();
127147
}
128148
}
@@ -169,6 +189,62 @@ pub fn command(options: Options) -> Result<()> {
169189
let (child_wait_tx, child_wait_rx) = channel();
170190
let child_wait_rx = Arc::new(Mutex::new(child_wait_rx));
171191

192+
if std::env::var_os("TAURI_SKIP_DEVSERVER_CHECK") != Some("true".into()) {
193+
if let AppUrl::Url(WindowUrl::External(dev_server_url)) = config
194+
.lock()
195+
.unwrap()
196+
.as_ref()
197+
.unwrap()
198+
.build
199+
.dev_path
200+
.clone()
201+
{
202+
let host = dev_server_url
203+
.host()
204+
.unwrap_or_else(|| panic!("No host name in the URL"));
205+
let port = dev_server_url
206+
.port_or_known_default()
207+
.unwrap_or_else(|| panic!("No port number in the URL"));
208+
let addrs;
209+
let addr;
210+
let addrs = match host {
211+
url::Host::Domain(domain) => {
212+
use std::net::ToSocketAddrs;
213+
addrs = (domain, port).to_socket_addrs()?;
214+
addrs.as_slice()
215+
}
216+
url::Host::Ipv4(ip) => {
217+
addr = (ip, port).into();
218+
std::slice::from_ref(&addr)
219+
}
220+
url::Host::Ipv6(ip) => {
221+
addr = (ip, port).into();
222+
std::slice::from_ref(&addr)
223+
}
224+
};
225+
let mut i = 0;
226+
let sleep_interval = std::time::Duration::from_secs(2);
227+
let max_attempts = 90;
228+
loop {
229+
if std::net::TcpStream::connect(addrs).is_ok() {
230+
break;
231+
}
232+
if i % 3 == 0 {
233+
logger.warn("Waiting for your dev server to start...");
234+
}
235+
i += 1;
236+
if i == max_attempts {
237+
logger.error(format!(
238+
"Could not connect to `{}` after {}s. Please make sure that is the URL to your dev server.",
239+
dev_server_url, i * sleep_interval.as_secs()
240+
));
241+
exit(1);
242+
}
243+
std::thread::sleep(sleep_interval);
244+
}
245+
}
246+
}
247+
172248
let mut process = start_app(&options, &runner, &cargo_features, child_wait_rx.clone());
173249

174250
let (tx, rx) = channel();
@@ -221,7 +297,7 @@ pub fn command(options: Options) -> Result<()> {
221297

222298
fn kill_before_dev_process() {
223299
if let Some(child) = BEFORE_DEV.get() {
224-
let mut child = child.lock().unwrap();
300+
let child = child.lock().unwrap();
225301
#[cfg(windows)]
226302
let _ = Command::new("powershell")
227303
.arg("-NoProfile")

tooling/cli.rs/src/helpers/logger.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@
44

55
use colored::Colorize;
66

7+
#[derive(Clone)]
78
pub struct Logger<'a> {
89
context: &'a str,
910
}

0 commit comments

Comments
 (0)