Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

fix: symlink workspaces #8120

Draft
wants to merge 2 commits into
base: main
Choose a base branch
from
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
46 changes: 41 additions & 5 deletions crates/turborepo-globwalk/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,10 @@ use path_slash::PathExt;
use rayon::prelude::*;
use regex::Regex;
use turbopath::{AbsoluteSystemPath, AbsoluteSystemPathBuf, PathError};
use wax::{walk::FileIterator, BuildError, Glob};
use wax::{
walk::{FileIterator, LinkBehavior},
BuildError, Glob,
};

#[derive(Debug, PartialEq, Clone, Copy)]
pub enum WalkType {
Expand Down Expand Up @@ -296,15 +299,39 @@ pub fn globwalk(
) -> Result<HashSet<AbsoluteSystemPathBuf>, WalkError> {
let include = include.iter().map(|i| i.inner.clone()).collect::<Vec<_>>();
let exclude = exclude.iter().map(|e| e.inner.clone()).collect::<Vec<_>>();
globwalk_internal(base_path, &include, &exclude, walk_type)
globwalk_internal(
base_path,
&include,
&exclude,
walk_type,
LinkBehavior::ReadFile,
)
}

pub fn globwalk_follow_symlink(
base_path: &AbsoluteSystemPath,
include: &[ValidatedGlob],
exclude: &[ValidatedGlob],
walk_type: WalkType,
) -> Result<HashSet<AbsoluteSystemPathBuf>, WalkError> {
let include = include.iter().map(|i| i.inner.clone()).collect::<Vec<_>>();
let exclude = exclude.iter().map(|e| e.inner.clone()).collect::<Vec<_>>();
globwalk_internal(
base_path,
&include,
&exclude,
walk_type,
LinkBehavior::ReadTarget,
)
}

#[tracing::instrument]
pub fn globwalk_internal(
fn globwalk_internal(
base_path: &AbsoluteSystemPath,
include: &[String],
exclude: &[String],
walk_type: WalkType,
link_behavior: LinkBehavior,
) -> Result<HashSet<AbsoluteSystemPathBuf>, WalkError> {
let (base_path_new, include_paths, exclude_paths) =
preprocess_paths_and_globs(base_path, include, exclude)?;
Expand All @@ -324,18 +351,27 @@ pub fn globwalk_internal(
// Use flat_map_iter as we only want parallelism for walking the globs and not iterating
// over the results.
// See https://docs.rs/rayon/latest/rayon/iter/trait.ParallelIterator.html#method.flat_map_iter
.flat_map_iter(|glob| walk_glob(walk_type, &base_path_new, ex_patterns.clone(), glob))
.flat_map_iter(|glob| {
walk_glob(
walk_type,
link_behavior,
&base_path_new,
ex_patterns.clone(),
glob,
)
})
.collect()
}

#[tracing::instrument(skip(ex_patterns), fields(glob=glob.to_string().as_str()))]
fn walk_glob(
walk_type: WalkType,
link_behavior: LinkBehavior,
base_path_new: &Path,
ex_patterns: Vec<Glob>,
glob: Glob,
) -> Vec<Result<AbsoluteSystemPathBuf, WalkError>> {
glob.walk(base_path_new)
glob.walk_with_behavior(base_path_new, link_behavior)
.not(ex_patterns)
.unwrap_or_else(|e| {
// Per docs, only fails if exclusion list is too large, since we're using
Expand Down
42 changes: 41 additions & 1 deletion crates/turborepo-repository/src/package_manager/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -487,7 +487,7 @@ impl PackageManager {
) -> Result<impl Iterator<Item = AbsoluteSystemPathBuf>, Error> {
let globs = self.get_workspace_globs(repo_root)?;

let files = globwalk::globwalk(
let files = globwalk::globwalk_follow_symlink(
repo_root,
&globs.package_json_inclusions,
&globs.validated_exclusions,
Expand Down Expand Up @@ -675,6 +675,46 @@ mod tests {
}
}

#[test]
fn test_get_package_jsons_behind_symlink() {
let tmpdir = tempdir().unwrap();
let top_dir = AbsoluteSystemPathBuf::try_from(tmpdir.path()).unwrap();
let repo = top_dir.join_component("my-repo");
let root_json = repo.join_component("package.json");
let pkg_foo_json = repo.join_components(&["packages", "foo", "package.json"]);
let bar_symlink = repo.join_components(&["packages", "bar"]);
let pkg_bar_json = top_dir.join_components(&["bar", "package.json"]);

pkg_foo_json.ensure_dir().unwrap();
pkg_bar_json.ensure_dir().unwrap();

root_json.create_with_contents(r#"{"name": "my-repo", "packageManager": "[email protected]", "workspaces": ["packages/*"]}"#).unwrap();
pkg_foo_json
.create_with_contents(r#"{"name": "foo"}"#)
.unwrap();
pkg_bar_json
.create_with_contents(r#"{"name": "bar"}"#)
.unwrap();
bar_symlink
.symlink_to_dir(["..", "..", "bar"].join(std::path::MAIN_SEPARATOR_STR))
.unwrap();

let mut packages = PackageManager::Npm
.get_package_jsons(&repo)
.unwrap()
.collect::<Vec<_>>();

packages.sort();

assert_eq!(
packages,
vec![
top_dir.join_components(&["my-repo", "packages", "bar", "package.json"]),
top_dir.join_components(&["my-repo", "packages", "foo", "package.json"]),
]
);
}

#[test]
fn test_get_workspace_ignores() {
let root = repo_root();
Expand Down
Loading