diff --git a/crates/socket-patch-core/src/vendor/go_consumer_sync.rs b/crates/socket-patch-core/src/vendor/go_consumer_sync.rs index ce7649ab9..cf28d93a2 100644 --- a/crates/socket-patch-core/src/vendor/go_consumer_sync.rs +++ b/crates/socket-patch-core/src/vendor/go_consumer_sync.rs @@ -463,12 +463,16 @@ async fn workspace_issues(project_root: &Path, replaces: &[ReplaceEntry]) -> Vec } /// A directory as go matches a module root against the workspace's `use` -/// list: absolute, with symlinks resolved when it exists, else lexically -/// normalized. +/// list: absolute, with symlinks resolved when it exists. A path that only +/// exists once folded (`./x/../app` with no `x`, which go's `filepath.Join` +/// folds) is folded lexically and then resolved, so it still matches a +/// project under a symlinked dir (macOS `/var` -> `/private/var`). fn same_dir_key(dir: &Path) -> PathBuf { let abs = std::env::current_dir().unwrap_or_default().join(dir); - std::fs::canonicalize(&abs) - .unwrap_or_else(|_| crate::utils::relpath::normalize_lexically_keeping_escapes(&abs)) + std::fs::canonicalize(&abs).unwrap_or_else(|_| { + let folded = crate::utils::relpath::normalize_lexically_keeping_escapes(&abs); + std::fs::canonicalize(&folded).unwrap_or(folded) + }) } /// Where a replace sends the module, compared as go compares replacements @@ -924,6 +928,26 @@ mod tests { } } + /// A workspace reached through a symlink (macOS temp dirs live under + /// `/var` -> `/private/var`) still counts the project as a member when + /// the `use` path only exists once folded (`./x/../app`, no `x`). + #[cfg(unix)] + #[tokio::test] + async fn go_work_member_matches_through_a_symlinked_workspace() { + let dir = tempfile::tempdir().unwrap(); + let real = dir.path().join("real"); + std::fs::create_dir(&real).unwrap(); + let ws = dir.path().join("link"); + std::os::unix::fs::symlink(&real, &ws).unwrap(); + let root = ws.join("app"); + consumer(&root, REPLACE); + let none = HashMap::new(); + for use_dir in ["./app", "./x/../app"] { + write(&ws, "go.work", &format!("go 1.21\n\nuse {use_dir}\n")); + assert_eq!(audit(&root, &none).await, vec![], "{use_dir}"); + } + } + /// #531: two members replacing one module@version with different /// directories make go refuse the workspace. The same hosted module in /// both, or the same directory, is fine, and so is a replace of