fix(windows): resolve daemon startup failures and Git Bash compatibility (#582)
* fix(windows): resolve daemon startup failures and Git Bash compatibility Three root causes behind 27 open Windows issues: 1. Path::canonicalize() returns \\?\ prefixed paths on Windows that Node.js cannot parse, preventing daemon startup. Strip the prefix before passing to Node. (fixes #522, #390, #56, #25, #37, #89) 2. Git Bash/MSYS2 translates Unix-style paths and resolves node to a shell wrapper script. Use node.exe explicitly and set MSYS_NO_PATHCONV/MSYS2_ARG_CONV_EXCL to prevent argument mangling. (fixes #148, #108, #171) 3. postinstall fixWindowsShims() hardcoded x64 arch and did not verify the native binary exists before rewriting shims. Now detects arch dynamically and validates the binary path. (fixes #262) Also: - Error messages now show TCP port on Windows instead of Unix socket path - Windows CI expanded to test full daemon lifecycle (open, snapshot, close) * fix(windows): strip \\?\ prefix in auth-cli path (fixes #579) Same canonicalize() issue as the daemon spawn path, but in run_auth_cli() which passes the script path to Node.js.
This commit is contained in:
+30
-8
@@ -355,6 +355,17 @@ pub fn ensure_daemon(
|
||||
let exe_path = env::current_exe().map_err(|e| e.to_string())?;
|
||||
// Canonicalize to resolve symlinks (e.g., npm global bin symlink -> actual binary)
|
||||
let exe_path = exe_path.canonicalize().unwrap_or(exe_path);
|
||||
// On Windows, canonicalize() returns \\?\ prefixed extended-length paths.
|
||||
// Node.js cannot handle these, so strip the prefix.
|
||||
#[cfg(windows)]
|
||||
let exe_path = {
|
||||
let p = exe_path.to_string_lossy();
|
||||
if let Some(stripped) = p.strip_prefix(r"\\?\") {
|
||||
PathBuf::from(stripped)
|
||||
} else {
|
||||
exe_path
|
||||
}
|
||||
};
|
||||
let exe_dir = exe_path.parent().unwrap();
|
||||
|
||||
let mut daemon_paths = vec![
|
||||
@@ -404,10 +415,11 @@ pub fn ensure_daemon(
|
||||
{
|
||||
use std::os::windows::process::CommandExt;
|
||||
|
||||
// On Windows, call node directly. Command::new handles PATH resolution (node.exe or node.cmd)
|
||||
// and automatically quotes arguments containing spaces.
|
||||
let mut cmd = Command::new("node");
|
||||
cmd.arg(daemon_path);
|
||||
// Use node.exe explicitly to avoid Git Bash/MSYS2 shell wrapper resolution
|
||||
let mut cmd = Command::new("node.exe");
|
||||
cmd.arg(daemon_path)
|
||||
.env("MSYS_NO_PATHCONV", "1")
|
||||
.env("MSYS2_ARG_CONV_EXCL", "*");
|
||||
apply_daemon_env(&mut cmd, session, opts);
|
||||
|
||||
// CREATE_NEW_PROCESS_GROUP | DETACHED_PROCESS
|
||||
@@ -431,10 +443,20 @@ pub fn ensure_daemon(
|
||||
thread::sleep(Duration::from_millis(100));
|
||||
}
|
||||
|
||||
Err(format!(
|
||||
"Daemon failed to start (socket: {})",
|
||||
get_socket_dir().join(format!("{}.sock", session)).display()
|
||||
))
|
||||
#[cfg(unix)]
|
||||
let endpoint_info = format!(
|
||||
"socket: {}",
|
||||
get_socket_dir()
|
||||
.join(format!("{}.sock", session))
|
||||
.display()
|
||||
);
|
||||
#[cfg(windows)]
|
||||
let endpoint_info = format!(
|
||||
"port: 127.0.0.1:{}",
|
||||
get_port_for_session(session)
|
||||
);
|
||||
|
||||
Err(format!("Daemon failed to start ({})", endpoint_info))
|
||||
}
|
||||
|
||||
fn connect(session: &str) -> Result<Connection, String> {
|
||||
|
||||
@@ -31,6 +31,15 @@ use std::process::Command as ProcessCommand;
|
||||
fn run_auth_cli(cmd: &serde_json::Value, json_mode: bool) -> ! {
|
||||
let exe_path = env::current_exe().unwrap_or_default();
|
||||
let exe_path = exe_path.canonicalize().unwrap_or(exe_path);
|
||||
#[cfg(windows)]
|
||||
let exe_path = {
|
||||
let p = exe_path.to_string_lossy();
|
||||
if let Some(stripped) = p.strip_prefix(r"\\?\") {
|
||||
PathBuf::from(stripped)
|
||||
} else {
|
||||
exe_path
|
||||
}
|
||||
};
|
||||
let exe_dir = exe_path.parent().unwrap_or(std::path::Path::new("."));
|
||||
|
||||
let mut script_paths = vec![
|
||||
@@ -233,6 +242,13 @@ fn main() {
|
||||
libc::signal(libc::SIGPIPE, libc::SIG_DFL);
|
||||
}
|
||||
|
||||
// Prevent MSYS/Git Bash path translation from mangling arguments
|
||||
#[cfg(windows)]
|
||||
{
|
||||
env::set_var("MSYS_NO_PATHCONV", "1");
|
||||
env::set_var("MSYS2_ARG_CONV_EXCL", "*");
|
||||
}
|
||||
|
||||
let args: Vec<String> = env::args().skip(1).collect();
|
||||
let flags = parse_flags(&args);
|
||||
let clean = clean_args(&args);
|
||||
|
||||
Reference in New Issue
Block a user