Files

267 lines
7.6 KiB
Python
Raw Permalink Normal View History

2025-11-12 10:27:56 +08:00
#!/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()