Skip to content
Merged
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
87 changes: 45 additions & 42 deletions shell_hook.sh
Original file line number Diff line number Diff line change
Expand Up @@ -34,58 +34,61 @@ TOOL_FOLDER=".luca"
# It is designed to be idempotent and prevent PATH duplication.
# When navigating to subdirectories of a project, it keeps the project's tool path in PATH.
update_path() {
# Look for the active tools directory in the current location
local tool_bin_dir="$(pwd)/$TOOL_FOLDER/tools"
local current_pwd="$(pwd)"
# Walk up the directory tree to find the nearest ancestor with .luca/tools.
# This allows navigating directly into a project subdirectory and still
# picking up the project's tools without having to visit the root first.
# Stop at $HOME (exclusive): $HOME/.luca/tools is the global Luca installation
# and must not be treated as a project-specific tools directory.
local tool_bin_dir=""
local dir="$(pwd)"
while [ "$dir" != "/" ] && [ "$dir" != "$HOME" ]; do
if [ -d "$dir/$TOOL_FOLDER/tools" ]; then
tool_bin_dir="$dir/$TOOL_FOLDER/tools"
break
fi
dir="$(dirname "$dir")"
done

# Always clean up stale .luca/tools entries from PATH first.
# Keep only the entry that matches the one we just resolved (if any).
if [[ ":$PATH:" == *"/$TOOL_FOLDER/tools:"* ]]; then
local p
local new_path=""
local current_path="$PATH"

while [ -n "$current_path" ]; do
p="${current_path%%:*}"

if [ "$current_path" = "$p" ]; then
current_path=""
else
current_path="${current_path#*:}"
fi

# Only proceed if the tool directory exists in the current directory
if [ -d "$tool_bin_dir" ]; then
# Check if tool_bin_dir is already in PATH to avoid duplicates
if [[ "$p" == *"/$TOOL_FOLDER/tools" ]]; then
# Keep only the entry for the currently active project
if [ "$p" = "$tool_bin_dir" ]; then
new_path="${new_path:+$new_path:}$p"
fi
else
new_path="${new_path:+$new_path:}$p"
fi
done
export PATH="$new_path"
fi

# Add the resolved tool_bin_dir if it isn't already in PATH
if [ -n "$tool_bin_dir" ]; then
case ":$PATH:" in
*":$tool_bin_dir:"*)
*":$tool_bin_dir:"*)
# Already in PATH, do nothing (idempotent behavior)
return 0
;;
*)
# Not in PATH, add it to the front for priority
export PATH="$tool_bin_dir:$PATH"
# Optional: Uncomment the line below for debugging
# echo "$TOOL_NAME: Added $tool_bin_dir to PATH"
;;
esac
else
# Current directory doesn't have .luca/tools
# Check if there are any .luca/tools entries in PATH that should be cleaned up
if [[ ":$PATH:" == *"/$TOOL_FOLDER/tools:"* ]]; then
local p
local new_path=""
local current_path="$PATH"

while [ -n "$current_path" ]; do
p="${current_path%%:*}"

if [ "$current_path" = "$p" ]; then
current_path=""
else
current_path="${current_path#*:}"
fi

if [[ "$p" == *"/$TOOL_FOLDER/tools" ]]; then
# This is a tool directory entry - check if we should keep it
# Get the project root (parent of .luca/tools)
local project_root="${p%/$TOOL_FOLDER/tools}"
# Keep this entry if current directory is the project root or a subdirectory of it
if [[ "$current_pwd" == "$project_root" || "$current_pwd" == "$project_root/"* ]]; then
new_path="${new_path:+$new_path:}$p"
fi
# Otherwise, skip this entry (effectively removing it from PATH)
else
# Not a tool directory, keep it in PATH
new_path="${new_path:+$new_path:}$p"
fi
done
export PATH="$new_path"
fi
fi
}

Expand Down
93 changes: 92 additions & 1 deletion tests/shell_hook.bats
Original file line number Diff line number Diff line change
Expand Up @@ -86,7 +86,7 @@ setup() {
refute_output --partial "$project_a/.luca/tools"
}

@test "update_path: keeps .luca/tools entry when in subdirectory of project" {
@test "update_path: keeps .luca/tools entry when navigating into a project subdirectory" {
local project="$BATS_TEST_TMPDIR/myproject"
local subdir="$project/src/lib"
mkdir -p "$project/.luca/tools" "$subdir"
Expand All @@ -105,6 +105,97 @@ setup() {
assert_output --partial "$project/.luca/tools"
}

@test "update_path: adds .luca/tools when entering a project subdirectory directly" {
local project="$BATS_TEST_TMPDIR/myproject"
local subdir="$project/src/lib"
mkdir -p "$project/.luca/tools" "$subdir"

run bash -c "
export HOME='$TEST_HOME'
export SHELL=/bin/bash
source '$REPO_ROOT/shell_hook.sh'
# Navigate directly into a subdir without visiting the project root first
cd '$subdir'
update_path
echo \"\$PATH\"
"

assert_output --partial "$project/.luca/tools"
}

@test "update_path: replaces entry when switching between two projects with .luca/tools" {
local project_a="$BATS_TEST_TMPDIR/project_a"
local project_b="$BATS_TEST_TMPDIR/project_b"
mkdir -p "$project_a/.luca/tools" "$project_b/.luca/tools"

run bash -c "
export HOME='$TEST_HOME'
export SHELL=/bin/bash
source '$REPO_ROOT/shell_hook.sh'
cd '$project_a' && update_path
cd '$project_b' && update_path
echo \"\$PATH\"
"

assert_output --partial "$project_b/.luca/tools"
refute_output --partial "$project_a/.luca/tools"
}

@test "update_path: switching back to first project removes second project entry" {
local project_a="$BATS_TEST_TMPDIR/project_a"
local project_b="$BATS_TEST_TMPDIR/project_b"
mkdir -p "$project_a/.luca/tools" "$project_b/.luca/tools"

run bash -c "
export HOME='$TEST_HOME'
export SHELL=/bin/bash
source '$REPO_ROOT/shell_hook.sh'
cd '$project_a' && update_path
cd '$project_b' && update_path
cd '$project_a' && update_path
echo \"\$PATH\"
"

assert_output --partial "$project_a/.luca/tools"
refute_output --partial "$project_b/.luca/tools"
}

@test "update_path: does not add HOME/.luca/tools when in a non-project directory under HOME" {
# $HOME/.luca/tools is always present (global Luca installation) and must be ignored
mkdir -p "$TEST_HOME/.luca/tools"
local plain_dir="$TEST_HOME/documents"
mkdir -p "$plain_dir"

run bash -c "
export HOME='$TEST_HOME'
export SHELL=/bin/bash
source '$REPO_ROOT/shell_hook.sh'
cd '$plain_dir'
update_path
echo \"\$PATH\"
"

refute_output --partial "$TEST_HOME/.luca/tools"
}

@test "update_path: project inside HOME is found but HOME/.luca/tools is not added" {
mkdir -p "$TEST_HOME/.luca/tools"
local project="$TEST_HOME/projects/myapp"
mkdir -p "$project/.luca/tools" "$project/src"

run bash -c "
export HOME='$TEST_HOME'
export SHELL=/bin/bash
source '$REPO_ROOT/shell_hook.sh'
cd '$project/src'
update_path
echo \"\$PATH\"
"

assert_output --partial "$project/.luca/tools"
refute_output --partial "$TEST_HOME/.luca/tools"
}

@test "update_path: non-luca PATH entries are never removed" {
local empty_dir="$BATS_TEST_TMPDIR/empty"
mkdir -p "$empty_dir"
Expand Down
Loading