From a671c63401144c546626e492cd2ade26de1d2512 Mon Sep 17 00:00:00 2001 From: Tw93 Date: Fri, 2 Jan 2026 00:03:17 +0800 Subject: [PATCH] feat: smart vendor directory handling in purge - Only clean PHP Composer vendor (regeneratable) - Protect Rails, Go, and unknown vendor directories - Enhanced test coverage for all scenarios Builds on PR #229 with improved multi-language support --- lib/clean/project.sh | 52 ++++++++++++++++++++++++++++++++++++-------- tests/purge.bats | 48 +++++++++++++++++++++++++++++++++++++++- 2 files changed, 90 insertions(+), 10 deletions(-) diff --git a/lib/clean/project.sh b/lib/clean/project.sh index 9e5b013..642994b 100644 --- a/lib/clean/project.sh +++ b/lib/clean/project.sh @@ -221,6 +221,7 @@ is_safe_project_artifact() { return 0 } +# Detect if directory is a Rails project root is_rails_project_root() { local dir="$1" [[ -f "$dir/config/application.rb" ]] || return 1 @@ -228,25 +229,58 @@ is_rails_project_root() { [[ -f "$dir/bin/rails" || -f "$dir/config/environment.rb" ]] } -is_rails_vendor_dir() { - local path="$1" - local base - base=$(basename "$path") - [[ "$base" == "vendor" ]] || return 1 - is_rails_project_root "$(dirname "$path")" +# Detect if directory is a Go project root +is_go_project_root() { + local dir="$1" + [[ -f "$dir/go.mod" ]] } +# Detect if directory is a PHP Composer project root +is_php_project_root() { + local dir="$1" + [[ -f "$dir/composer.json" ]] +} + +# Check if a vendor directory should be protected from purge +# Strategy: Only clean PHP Composer vendor, protect all others +is_protected_vendor_dir() { + local path="$1" + local parent_dir + parent_dir=$(dirname "$path") + + # PHP Composer vendor can be safely regenerated with 'composer install' + # Do NOT protect it (return 1 = not protected = can be cleaned) + if is_php_project_root "$parent_dir"; then + return 1 + fi + + # Rails vendor (importmap dependencies) - should be protected + if is_rails_project_root "$parent_dir"; then + return 0 + fi + + # Go vendor (optional vendoring) - protect to avoid accidental deletion + if is_go_project_root "$parent_dir"; then + return 0 + fi + + # Unknown vendor type - protect by default (conservative approach) + return 0 +} + +# Check if an artifact should be protected from purge is_protected_purge_artifact() { local path="$1" local base base=$(basename "$path") + case "$base" in vendor) - if is_rails_vendor_dir "$path"; then - return 0 - fi + is_protected_vendor_dir "$path" + return $? ;; esac + return 1 } diff --git a/tests/purge.bats b/tests/purge.bats index b092148..12b2024 100644 --- a/tests/purge.bats +++ b/tests/purge.bats @@ -127,7 +127,7 @@ setup() { [[ "$result" == "SKIPPED" ]] } -@test "scan_purge_targets: keeps non-Rails vendor directory" { +@test "scan_purge_targets: cleans PHP Composer vendor directory" { mkdir -p "$HOME/www/php-app/vendor" touch "$HOME/www/php-app/composer.json" @@ -149,6 +149,52 @@ setup() { [[ "$result" == "FOUND" ]] } +@test "scan_purge_targets: skips Go vendor directory" { + mkdir -p "$HOME/www/go-app/vendor" + touch "$HOME/www/go-app/go.mod" + touch "$HOME/www/go-app/go.sum" + + local scan_output + scan_output="$(mktemp)" + + result=$(bash -c " + source '$PROJECT_ROOT/lib/clean/project.sh' + scan_purge_targets '$HOME/www' '$scan_output' + if grep -q '$HOME/www/go-app/vendor' '$scan_output'; then + echo 'FOUND' + else + echo 'SKIPPED' + fi + ") + + rm -f "$scan_output" + + [[ "$result" == "SKIPPED" ]] +} + +@test "scan_purge_targets: skips unknown vendor directory" { + # Create a vendor directory without any project file + mkdir -p "$HOME/www/unknown-app/vendor" + + local scan_output + scan_output="$(mktemp)" + + result=$(bash -c " + source '$PROJECT_ROOT/lib/clean/project.sh' + scan_purge_targets '$HOME/www' '$scan_output' + if grep -q '$HOME/www/unknown-app/vendor' '$scan_output'; then + echo 'FOUND' + else + echo 'SKIPPED' + fi + ") + + rm -f "$scan_output" + + # Unknown vendor should be protected (conservative approach) + [[ "$result" == "SKIPPED" ]] +} + @test "is_recently_modified: detects recent projects" { mkdir -p "$HOME/www/project/node_modules" touch "$HOME/www/project/package.json"