From 65804793aa4e7db6080ec598936491e3234707f7 Mon Sep 17 00:00:00 2001 From: AnouarMohamed Date: Mon, 25 May 2026 13:13:32 +0100 Subject: [PATCH 1/2] feat: enhance environment scanner robustness (#19) - Upgraded detection logic to use pathlib for robust OS-independent path handling - Implemented directory pruning in os.walk to drastically improve scan performance - Added comprehensive IGNORED_DIRECTORIES list (dist, build, venv, etc.) - Improved marker detection using sets for faster and more accurate framework identification --- devctl/orchestrator/scanner.py | 55 ++++++++++++++++++++++++---------- 1 file changed, 39 insertions(+), 16 deletions(-) diff --git a/devctl/orchestrator/scanner.py b/devctl/orchestrator/scanner.py index ecc4a5c..ff7d827 100644 --- a/devctl/orchestrator/scanner.py +++ b/devctl/orchestrator/scanner.py @@ -4,13 +4,28 @@ """ import os +from pathlib import Path + +# Directories to ignore during scanning +IGNORED_DIRECTORIES = { + "node_modules", + "target", + ".git", + ".angular", + "dist", + "build", + "venv", + ".venv", + "__pycache__", +} def detect_environment(root_path: str = "."): """ - Scans the directory and its subfolders to identify components. + Scans the directory tree and its subfolders to identify components. Returns the state and absolute paths of each component. """ + root = Path(root_path).resolve() env_state = { "has_docker_compose": False, "docker_path": None, @@ -20,29 +35,37 @@ def detect_environment(root_path: str = "."): "angular_path": None, "has_vue": False, "vue_path": None, - "project_root": os.path.abspath(root_path), + "project_root": str(root), } - for dirpath, _dirnames, filenames in os.walk(root_path): - # Optimization: ignore heavy folders for an instant scan - if any(ignored in dirpath for ignored in ["node_modules", "target", ".git", ".angular"]): - continue + for dirpath, dirnames, filenames in os.walk(root): + # In-place modification of dirnames to prune the traversal + dirnames[:] = [d for d in dirnames if d not in IGNORED_DIRECTORIES] + + current_path = Path(dirpath) + filename_set = set(filenames) - if "docker-compose.yml" in filenames and not env_state["has_docker_compose"]: + # 1. Docker Compose detection + if "docker-compose.yml" in filename_set and not env_state["has_docker_compose"]: env_state["has_docker_compose"] = True - env_state["docker_path"] = dirpath + env_state["docker_path"] = str(current_path) - if ("pom.xml" in filenames or "mvnw" in filenames) and not env_state["has_spring"]: + # 2. Spring Boot detection + if ("pom.xml" in filename_set or "mvnw" in filename_set) and not env_state["has_spring"]: env_state["has_spring"] = True - env_state["spring_path"] = dirpath + env_state["spring_path"] = str(current_path) - if "angular.json" in filenames and not env_state["has_angular"]: + # 3. Angular detection + if "angular.json" in filename_set and not env_state["has_angular"]: env_state["has_angular"] = True - env_state["angular_path"] = dirpath + env_state["angular_path"] = str(current_path) - vue_files = ["vite.config.ts", "vite.config.js"] - if any(f in filenames for f in vue_files) and not env_state["has_vue"]: - env_state["has_vue"] = True - env_state["vue_path"] = dirpath + # 4. Vue.js (Vite) detection + vue_markers = {"vite.config.ts", "vite.config.js"} + if (vue_markers & filename_set) and not env_state["has_vue"]: + # Special case: don't shadow Angular if it also has a vite config (unlikely but safe) + if not env_state["has_angular"]: + env_state["has_vue"] = True + env_state["vue_path"] = str(current_path) return env_state From 0b485b17c2d3141bc20864a70a33b362d0cbaa50 Mon Sep 17 00:00:00 2001 From: AnouarMohamed Date: Mon, 25 May 2026 13:19:22 +0100 Subject: [PATCH 2/2] style: fix ruff formatting issues (trailing whitespace) --- devctl/orchestrator/scanner.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/devctl/orchestrator/scanner.py b/devctl/orchestrator/scanner.py index ff7d827..1b378ca 100644 --- a/devctl/orchestrator/scanner.py +++ b/devctl/orchestrator/scanner.py @@ -41,7 +41,7 @@ def detect_environment(root_path: str = "."): for dirpath, dirnames, filenames in os.walk(root): # In-place modification of dirnames to prune the traversal dirnames[:] = [d for d in dirnames if d not in IGNORED_DIRECTORIES] - + current_path = Path(dirpath) filename_set = set(filenames)