Skip to content
Merged
Show file tree
Hide file tree
Changes from 7 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
137 changes: 132 additions & 5 deletions crates/turborepo-lib/src/shim/local_turbo_state.rs
Original file line number Diff line number Diff line change
Expand Up @@ -59,10 +59,121 @@
// leading to the wrong place. We could separate the Windows
// implementation, but this workaround works for other platforms as
// well.
let canonical_path =
fs_canonicalize(root_path.as_path().join("node_modules").join("turbo")).ok()?;
let turbo_path = root_path.as_path().join("node_modules").join("turbo");
debug!(
"generate_linked_path: Attempting to canonicalize: {}",
turbo_path
);

AbsoluteSystemPathBuf::try_from(canonical_path.parent()?).ok()
match fs_canonicalize(&turbo_path) {
Ok(canonical_path) => {
debug!(
"generate_linked_path: Canonicalized to: {}",
canonical_path.display()
);
match canonical_path.parent() {
Some(parent) => {
debug!(
"generate_linked_path: Parent directory: {}",
parent.display()
);
match AbsoluteSystemPathBuf::try_from(parent) {
Ok(path) => {
debug!(
"generate_linked_path: Successfully created \
AbsoluteSystemPathBuf"
);
Some(path)
}
Err(e) => {
debug!(
"generate_linked_path: Failed to create \
AbsoluteSystemPathBuf: {:?}",
e
);
None
}
}
}
None => {
debug!("generate_linked_path: Canonicalized path has no parent");
None
}
}
}
Err(e) => {
debug!(
"generate_linked_path: Failed to canonicalize {}: {}",
turbo_path, e
);

// On Windows, canonicalize can fail with permission errors even when
// the symlink is valid. Try using read_link instead.
#[cfg(target_os = "windows")]
{
debug!("generate_linked_path: Attempting Windows fallback with read_link");
match fs::read_link(&turbo_path) {
Ok(link_target) => {
debug!(
"generate_linked_path: read_link succeeded, target: {}",
link_target.display()
);

// The link target is relative to the symlink location
// e.g., ".pnpm/[email protected]/node_modules/turbo"
// We need to resolve it relative to node_modules directory
let node_modules = root_path.as_path().join("node_modules");
let resolved = node_modules.join(&link_target);

Check failure on line 126 in crates/turborepo-lib/src/shim/local_turbo_state.rs

View workflow job for this annotation

GitHub Actions / Turborepo Rust testing on windows

the trait bound `PathBuf: AsRef<Utf8Path>` is not satisfied

Check failure on line 126 in crates/turborepo-lib/src/shim/local_turbo_state.rs

View workflow job for this annotation

GitHub Actions / Turborepo Integration (windows-latest)

the trait bound `PathBuf: AsRef<Utf8Path>` is not satisfied

debug!(
"generate_linked_path: Resolved path: {}",
resolved.display()

Check failure on line 130 in crates/turborepo-lib/src/shim/local_turbo_state.rs

View workflow job for this annotation

GitHub Actions / Turborepo Rust testing on windows

no method named `display` found for struct `Utf8PathBuf` in the current scope

Check failure on line 130 in crates/turborepo-lib/src/shim/local_turbo_state.rs

View workflow job for this annotation

GitHub Actions / Turborepo Integration (windows-latest)

no method named `display` found for struct `Utf8PathBuf` in the current scope
);

// Get the parent directory (should be .pnpm/[email protected]/node_modules)
match resolved.parent() {
Some(parent) => {
debug!(
"generate_linked_path: Parent directory: {}",
parent.display()

Check failure on line 138 in crates/turborepo-lib/src/shim/local_turbo_state.rs

View workflow job for this annotation

GitHub Actions / Turborepo Rust testing on windows

no method named `display` found for reference `&Utf8Path` in the current scope

Check failure on line 138 in crates/turborepo-lib/src/shim/local_turbo_state.rs

View workflow job for this annotation

GitHub Actions / Turborepo Integration (windows-latest)

no method named `display` found for reference `&Utf8Path` in the current scope
);
match AbsoluteSystemPathBuf::try_from(parent) {

Check failure on line 140 in crates/turborepo-lib/src/shim/local_turbo_state.rs

View workflow job for this annotation

GitHub Actions / Turborepo Rust testing on windows

the trait bound `AbsoluteSystemPathBuf: std::convert::From<&Utf8Path>` is not satisfied

Check failure on line 140 in crates/turborepo-lib/src/shim/local_turbo_state.rs

View workflow job for this annotation

GitHub Actions / Turborepo Rust testing on windows

the trait bound `AbsoluteSystemPathBuf: TryFrom<&Utf8Path>` is not satisfied

Check failure on line 140 in crates/turborepo-lib/src/shim/local_turbo_state.rs

View workflow job for this annotation

GitHub Actions / Turborepo Integration (windows-latest)

the trait bound `AbsoluteSystemPathBuf: std::convert::From<&Utf8Path>` is not satisfied

Check failure on line 140 in crates/turborepo-lib/src/shim/local_turbo_state.rs

View workflow job for this annotation

GitHub Actions / Turborepo Integration (windows-latest)

the trait bound `AbsoluteSystemPathBuf: TryFrom<&Utf8Path>` is not satisfied
Ok(path) => {
debug!(
"generate_linked_path: Successfully created \
AbsoluteSystemPathBuf from read_link fallback"
);
Some(path)
}
Err(e) => {
debug!(
"generate_linked_path: Failed to create \
AbsoluteSystemPathBuf: {:?}",
e
);
None
}
}
}
None => {
debug!("generate_linked_path: Resolved path has no parent");
None
}
}
}
Err(e) => {
debug!("generate_linked_path: read_link also failed: {}", e);
None
}
}
}

#[cfg(not(target_os = "windows"))]
{
None
}
}
}
}

// The unplugged directory doesn't have a fixed path.
Expand Down Expand Up @@ -117,6 +228,11 @@
let platform_package_name = TurboState::platform_package_name();
let binary_name = TurboState::binary_name();

debug!(
"Searching for local turbo. Platform: {}, Binary: {}",
platform_package_name, binary_name
);

let platform_package_json_path_components = [platform_package_name, "package.json"];
let platform_package_executable_path_components =
[platform_package_name, "bin", binary_name];
Expand All @@ -131,13 +247,20 @@

// Detecting the package manager is more expensive than just doing an exhaustive
// search.
for root in search_functions
for (idx, root) in search_functions
.iter()
.filter_map(|search_function| search_function(root_path))
.enumerate()
{
debug!("Trying search path #{}: {}", idx + 1, root);
// Needs borrow because of the loop.
#[allow(clippy::needless_borrow)]
let bin_path = root.join_components(&platform_package_executable_path_components);
debug!("Looking for binary at: {}", bin_path);

let exists = bin_path.exists();
debug!("Binary exists: {}", exists);

match fs_canonicalize(&bin_path) {
Ok(bin_path) => {
let resolved_package_json_path =
Expand All @@ -153,10 +276,14 @@
version: local_version,
});
}
Err(_) => debug!("No local turbo binary found at: {}", bin_path),
Err(e) => debug!(
"No local turbo binary found at: {} (error: {})",
bin_path, e
),
}
}

debug!("No local turbo found after searching all paths");
None
}

Expand Down

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

41 changes: 37 additions & 4 deletions turborepo-tests/integration/tests/find-turbo/linked.t
Original file line number Diff line number Diff line change
@@ -1,17 +1,50 @@
Setup
$ . ${TESTDIR}/../../../helpers/setup.sh
$ . ${TESTDIR}/setup.sh $(pwd) "linked"
* (glob)
$ echo "=== Verifying fixture state after setup ==="
=== Verifying fixture state after setup ===
$ ls -la node_modules/turbo 2>&1 || echo "node_modules/turbo does not exist"
* (glob)
$ ls -la node_modules/.pnpm 2>&1 | head -5
* (glob)
$ echo "=== End verification ==="
=== End verification ===

Make sure we use local, but do not pass --skip-infer to old binary
$ ${TESTDIR}/set_version.sh $(pwd) "1.0.0"
$ ${TURBO} build --filter foo -vv > out.log 2>&1
$ grep --quiet -F "Local turbo version: 1.0.0" out.log
$ echo "Running turbo with verbose output..."
Running turbo with verbose output...
$ ${TURBO} build --filter foo -vv 2>&1 | tee out.log
* (glob)
$ echo "=== Full output from out.log ==="
=== Full output from out.log ===
$ cat out.log
* (glob)
$ echo "=== Checking for version string ==="
=== Checking for version string ===
$ grep -F "Local turbo version: 1.0.0" out.log || echo "VERSION STRING NOT FOUND"
* (glob)
$ echo "=== Last line of output ==="
=== Last line of output ===
$ cat out.log | tail -n1
build --filter foo -vv --

Make sure we use local, and DO pass --skip-infer to newer binary
$ ${TESTDIR}/set_version.sh $(pwd) "1.8.0"
$ ${TURBO} build --filter foo -vv > out.log 2>&1
$ grep --quiet -F "Local turbo version: 1.8.0" out.log
$ echo "Running turbo with newer version..."
Running turbo with newer version...
$ ${TURBO} build --filter foo -vv 2>&1 | tee out.log
* (glob)
$ echo "=== Full output from out.log ==="
=== Full output from out.log ===
$ cat out.log
* (glob)
$ echo "=== Checking for version string ==="
=== Checking for version string ===
$ grep -F "Local turbo version: 1.8.0" out.log || echo "VERSION STRING NOT FOUND"
* (glob)
$ echo "=== Last line of output ==="
=== Last line of output ===
$ cat out.log | tail -n1
--skip-infer build --filter foo -vv --single-package --
49 changes: 47 additions & 2 deletions turborepo-tests/integration/tests/find-turbo/setup.sh
Original file line number Diff line number Diff line change
Expand Up @@ -5,25 +5,70 @@ SCRIPT_DIR=$(dirname ${BASH_SOURCE[0]})
TARGET_DIR=$1
FIXTURE_DIR=$2

echo "=== Setup starting for fixture: $FIXTURE_DIR ==="
echo "OSTYPE: $OSTYPE"
echo "TARGET_DIR: $TARGET_DIR"

cp -a ${SCRIPT_DIR}/../../fixtures/find_turbo/$FIXTURE_DIR/. ${TARGET_DIR}/

# Verify what got copied
echo "Checking .pnpm contents after cp:"
ls -la ${TARGET_DIR}/node_modules/.pnpm/ 2>&1 || echo ".pnpm directory does not exist"
if [ -d "${TARGET_DIR}/node_modules/.pnpm/[email protected]" ]; then
echo "[email protected] directory exists"
ls -la ${TARGET_DIR}/node_modules/.pnpm/[email protected]/node_modules/ 2>&1 || echo "node_modules subdirectory missing"
else
echo "ERROR: [email protected] directory was NOT copied!"
fi

# We need to symlink: turbo -> .pnpm/[email protected]/node_modules/turbo
# where `turbo` is the symlink
# and `.pnpm/[email protected]/node_modules/turbo` is the path to symlink to
# Note: using a nested if so it's easy to find the Windows checks in scripts around the codebase.
if [[ "$OSTYPE" == "msys" ]]; then
echo "Running on Windows (msys)"
if [[ $FIXTURE_DIR == "linked" ]]; then
echo "Setting up linked fixture for Windows..."

# Check what exists before we start
echo "Before setup:"
ls -la node_modules/turbo 2>&1 || echo "node_modules/turbo does not exist"
ls -la node_modules/.pnpm/[email protected]/node_modules/turbo 2>&1 || echo "pnpm turbo directory does not exist"

# Delete the existing turbo directory or file, whatever exists there
echo "Removing existing node_modules/turbo..."
rm -rf node_modules/turbo

# Let's enter the node_modules directory
# echo "entering node_modules directory"
echo "Entering node_modules directory..."
pushd node_modules > /dev/null || exit 1

# Use pnpx to run symlnk-dir because installing globally doesn't work with pnpm.
pnpx symlink-dir .pnpm/[email protected]/node_modules/turbo turbo > /dev/null 2>&1
echo "Attempting to create symlink with: pnpx symlink-dir .pnpm/[email protected]/node_modules/turbo turbo"
if pnpx symlink-dir .pnpm/[email protected]/node_modules/turbo turbo; then
echo "✓ Symlink created successfully"
else
EXIT_CODE=$?
echo "✗ Symlink creation FAILED with exit code: $EXIT_CODE"
fi

# Get outta there
popd > /dev/null || exit 1

# Verify what we ended up with
echo "After setup:"
ls -la node_modules/turbo 2>&1 || echo "node_modules/turbo still does not exist"
if [ -L node_modules/turbo ]; then
LINK_TARGET=$(readlink node_modules/turbo)
echo "node_modules/turbo is a symlink pointing to: $LINK_TARGET"
echo "Checking if symlink target exists:"
ls -la "node_modules/$LINK_TARGET" 2>&1 || echo "ERROR: Symlink target does NOT exist!"
echo "Checking if we can find turbo-windows-64 from the symlink target:"
ls -la "node_modules/$LINK_TARGET/../turbo-windows-64" 2>&1 || echo "ERROR: turbo-windows-64 not found relative to symlink target"
fi

echo "=== Setup complete ==="
fi
else
echo "Not running on Windows, skipping Windows-specific setup"
fi
Loading