Skip to content

Commit 6e26842

Browse files
Fix Rust in-process CI failures
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> Copilot-Session: da0a9335-969e-4a77-838a-daac9206a454
1 parent fbeb45f commit 6e26842

4 files changed

Lines changed: 55 additions & 14 deletions

File tree

rust/src/ffi.rs

Lines changed: 51 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -235,15 +235,17 @@ impl FfiHost {
235235
environment: Vec<(String, String)>,
236236
args: Vec<String>,
237237
) -> Result<Self, Error> {
238-
let entrypoint = std::fs::canonicalize(entrypoint).map_err(|e| {
239-
Error::with_message(
240-
ErrorKind::InvalidConfig,
241-
format!(
242-
"failed to resolve in-process CLI entrypoint '{}': {e}",
243-
entrypoint.display()
244-
),
245-
)
246-
})?;
238+
let entrypoint = std::fs::canonicalize(entrypoint)
239+
.map(path_for_child_process)
240+
.map_err(|e| {
241+
Error::with_message(
242+
ErrorKind::InvalidConfig,
243+
format!(
244+
"failed to resolve in-process CLI entrypoint '{}': {e}",
245+
entrypoint.display()
246+
),
247+
)
248+
})?;
247249
let library_path =
248250
std::fs::canonicalize(resolve_library_path(&entrypoint)?).map_err(|e| {
249251
Error::with_message(
@@ -491,6 +493,33 @@ fn resolve_library_path(entrypoint: &Path) -> Result<PathBuf, Error> {
491493
))
492494
}
493495

496+
#[cfg(windows)]
497+
fn path_for_child_process(path: PathBuf) -> PathBuf {
498+
use std::ffi::OsString;
499+
use std::os::windows::ffi::{OsStrExt, OsStringExt};
500+
501+
const VERBATIM_PREFIX: &[u16] = &[b'\\' as u16, b'\\' as u16, b'?' as u16, b'\\' as u16];
502+
const UNC_PREFIX: &[u16] = &[b'U' as u16, b'N' as u16, b'C' as u16, b'\\' as u16];
503+
504+
let encoded: Vec<u16> = path.as_os_str().encode_wide().collect();
505+
let Some(stripped) = encoded.strip_prefix(VERBATIM_PREFIX) else {
506+
return path;
507+
};
508+
let normalized = if let Some(unc_path) = stripped.strip_prefix(UNC_PREFIX) {
509+
let mut result = vec![b'\\' as u16, b'\\' as u16];
510+
result.extend_from_slice(unc_path);
511+
result
512+
} else {
513+
stripped.to_vec()
514+
};
515+
PathBuf::from(OsString::from_wide(&normalized))
516+
}
517+
518+
#[cfg(not(windows))]
519+
fn path_for_child_process(path: PathBuf) -> PathBuf {
520+
path
521+
}
522+
494523
fn build_argv_json(entrypoint: &Path, extra_args: &[String]) -> Vec<u8> {
495524
// A `.js` entrypoint (dev / dist-cli) is launched via node; the packaged
496525
// single-file CLI binary embeds its own Node and is invoked directly.
@@ -563,6 +592,19 @@ mod tests {
563592
);
564593
}
565594

595+
#[cfg(windows)]
596+
#[test]
597+
fn child_process_path_removes_windows_verbatim_prefix() {
598+
assert_eq!(
599+
path_for_child_process(PathBuf::from(r"\\?\D:\a\copilot-sdk\index.js")),
600+
PathBuf::from(r"D:\a\copilot-sdk\index.js")
601+
);
602+
assert_eq!(
603+
path_for_child_process(PathBuf::from(r"\\?\UNC\server\share\copilot-sdk\index.js")),
604+
PathBuf::from(r"\\server\share\copilot-sdk\index.js")
605+
);
606+
}
607+
566608
#[test]
567609
fn environment_is_omitted_when_empty() {
568610
assert_eq!(build_env_json(&[]), None);

rust/src/lib.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -925,7 +925,7 @@ fn resolve_default_transport(options: &ClientOptions) -> Result<Transport> {
925925
}
926926

927927
fn resolve_default_transport_value(value: Option<&str>) -> Result<Transport> {
928-
match value.as_deref() {
928+
match value {
929929
None => Ok(Transport::Stdio { env: None }),
930930
Some(v) if v.is_empty() || v.eq_ignore_ascii_case("stdio") => {
931931
Ok(Transport::Stdio { env: None })

rust/tests/e2e.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -37,6 +37,7 @@ mod github_telemetry;
3737
mod hooks;
3838
#[path = "e2e/hooks_extended.rs"]
3939
mod hooks_extended;
40+
#[cfg(feature = "bundled-in-process")]
4041
#[path = "e2e/inprocess.rs"]
4142
mod inprocess;
4243
#[path = "e2e/mcp_and_agents.rs"]

rust/tests/e2e/inprocess.rs

Lines changed: 2 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,7 @@
11
use super::support::with_e2e_context;
22

3-
/// Mirrors the .NET `Should_Start_And_Connect_Over_InProcess_Ffi`: start a
4-
/// client that hosts the runtime in-process over FFI, perform a simple
5-
/// round-trip, and stop cleanly. Fails hard (does not skip) if the in-process
6-
/// runtime library can't be loaded.
3+
/// Starts an in-process client, performs a round-trip, and stops cleanly.
4+
/// Fails hard if the in-process runtime library cannot be loaded.
75
#[tokio::test]
86
async fn should_start_ping_and_stop_inprocess_client() {
97
with_e2e_context("client", "should_start_ping_and_stop_stdio_client", |ctx| {

0 commit comments

Comments
 (0)