#!/usr/bin/env python3 """ 技术栈检测脚本 用法: python detect_tech_stack.py [项目路径] 输出: JSON 格式的技术栈信息 """ import json import sys from pathlib import Path from typing import Dict, List, Optional def detect_python_stack(project_path: Path) -> Optional[Dict]: """检测 Python 技术栈""" result = {"language": "Python", "frameworks": [], "dependencies": []} # 查找配置文件 pyproject = project_path / "pyproject.toml" requirements = project_path / "requirements.txt" setup_py = project_path / "setup.py" if pyproject.exists(): content = pyproject.read_text(encoding='utf-8') result["config_file"] = "pyproject.toml" # 检测 Python 版本 if 'python = "^' in content: version = content.split('python = "^')[1].split('"')[0] result["version"] = version # 检测框架 if "fastapi" in content.lower(): result["frameworks"].append("FastAPI") if "django" in content.lower(): result["frameworks"].append("Django") if "flask" in content.lower(): result["frameworks"].append("Flask") if "langgraph" in content.lower(): result["frameworks"].append("LangGraph") if "crewai" in content.lower(): result["frameworks"].append("CrewAI") elif requirements.exists(): content = requirements.read_text(encoding='utf-8') result["config_file"] = "requirements.txt" # 检测框架 for line in content.split('\n'): line = line.strip().lower() if line.startswith('fastapi'): result["frameworks"].append("FastAPI") elif line.startswith('django'): result["frameworks"].append("Django") elif line.startswith('flask'): result["frameworks"].append("Flask") elif line.startswith('langgraph'): result["frameworks"].append("LangGraph") else: return None return result def detect_javascript_stack(project_path: Path) -> Optional[Dict]: """检测 JavaScript/TypeScript 技术栈""" package_json = project_path / "package.json" if not package_json.exists(): return None result = {"language": "JavaScript/TypeScript", "frameworks": [], "dependencies": []} try: import json as json_module content = json_module.loads(package_json.read_text(encoding='utf-8')) # 检测框架 deps = {**content.get("dependencies", {}), **content.get("devDependencies", {})} if "react" in deps: result["frameworks"].append("React") if "@types/react" in deps: result["language"] = "TypeScript" if "vue" in deps: result["frameworks"].append("Vue.js") if "next" in deps: result["frameworks"].append("Next.js") if "express" in deps: result["frameworks"].append("Express.js") if "@nestjs/core" in deps: result["frameworks"].append("NestJS") # 检测 Node 版本 if "engines" in content and "node" in content["engines"]: result["version"] = content["engines"]["node"] except Exception as e: print(f"Error parsing package.json: {e}", file=sys.stderr) return None return result def detect_go_stack(project_path: Path) -> Optional[Dict]: """检测 Go 技术栈""" go_mod = project_path / "go.mod" if not go_mod.exists(): return None result = {"language": "Go", "frameworks": [], "dependencies": []} content = go_mod.read_text(encoding='utf-8') # 检测 Go 版本 for line in content.split('\n'): if line.strip().startswith('go '): result["version"] = line.strip().split()[1] break # 检测框架 if "gin-gonic/gin" in content: result["frameworks"].append("Gin") if "gorilla/mux" in content: result["frameworks"].append("Gorilla Mux") if "fiber" in content: result["frameworks"].append("Fiber") return result def detect_rust_stack(project_path: Path) -> Optional[Dict]: """检测 Rust 技术栈""" cargo_toml = project_path / "Cargo.toml" if not cargo_toml.exists(): return None result = {"language": "Rust", "frameworks": [], "dependencies": []} content = cargo_toml.read_text(encoding='utf-8') # 检测框架 if "actix-web" in content: result["frameworks"].append("Actix Web") if "rocket" in content: result["frameworks"].append("Rocket") if "axum" in content: result["frameworks"].append("Axum") return result def detect_java_stack(project_path: Path) -> Optional[Dict]: """检测 Java 技术栈""" pom_xml = project_path / "pom.xml" build_gradle = project_path / "build.gradle" if pom_xml.exists(): result = {"language": "Java", "frameworks": [], "build_tool": "Maven"} content = pom_xml.read_text(encoding='utf-8') # 检测框架 if "spring-boot" in content: result["frameworks"].append("Spring Boot") if "quarkus" in content: result["frameworks"].append("Quarkus") return result elif build_gradle.exists(): result = {"language": "Java", "frameworks": [], "build_tool": "Gradle"} content = build_gradle.read_text(encoding='utf-8') # 检测框架 if "spring-boot" in content: result["frameworks"].append("Spring Boot") return result return None def detect_docker_usage(project_path: Path) -> Dict: """检测 Docker 使用情况""" docker_compose = project_path / "docker-compose.yml" dockerfile = project_path / "Dockerfile" result = {"containerized": False, "services": []} if docker_compose.exists(): result["containerized"] = True result["compose"] = True # 简单解析 services content = docker_compose.read_text(encoding='utf-8') in_services = False for line in content.split('\n'): if line.strip() == "services:": in_services = True continue if in_services and line.startswith(' ') and ':' in line and not line.strip().startswith('#'): service_name = line.strip().rstrip(':') if service_name and not service_name.startswith('-'): result["services"].append(service_name) elif dockerfile.exists(): result["containerized"] = True result["compose"] = False return result def main(): # 获取项目路径 if len(sys.argv) > 1: project_path = Path(sys.argv[1]) else: project_path = Path.cwd() if not project_path.exists(): print(f"Error: Path {project_path} does not exist", file=sys.stderr) sys.exit(1) # 检测各种技术栈 stacks = [] python_stack = detect_python_stack(project_path) if python_stack: stacks.append(python_stack) js_stack = detect_javascript_stack(project_path) if js_stack: stacks.append(js_stack) go_stack = detect_go_stack(project_path) if go_stack: stacks.append(go_stack) rust_stack = detect_rust_stack(project_path) if rust_stack: stacks.append(rust_stack) java_stack = detect_java_stack(project_path) if java_stack: stacks.append(java_stack) # 检测 Docker docker_info = detect_docker_usage(project_path) # 输出结果 result = { "project_path": str(project_path), "stacks": stacks, "docker": docker_info } print(json.dumps(result, indent=2, ensure_ascii=False)) if __name__ == "__main__": main()