项目理解skill-v1
This commit is contained in:
266
codebase_architecture_analyzer_v1/scripts/detect_tech_stack.py
Normal file
266
codebase_architecture_analyzer_v1/scripts/detect_tech_stack.py
Normal file
@ -0,0 +1,266 @@
|
||||
#!/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()
|
||||
@ -0,0 +1,167 @@
|
||||
#!/usr/bin/env python3
|
||||
"""
|
||||
依赖提取脚本
|
||||
|
||||
用法: python extract_dependencies.py [项目路径]
|
||||
|
||||
输出: JSON 格式的依赖信息
|
||||
"""
|
||||
|
||||
import ast
|
||||
import json
|
||||
import sys
|
||||
from pathlib import Path
|
||||
from typing import Dict, List, Set
|
||||
|
||||
|
||||
def extract_python_imports(file_path: Path) -> Set[str]:
|
||||
"""从 Python 文件提取 import"""
|
||||
imports = set()
|
||||
|
||||
try:
|
||||
with open(file_path, 'r', encoding='utf-8') as f:
|
||||
tree = ast.parse(f.read(), filename=str(file_path))
|
||||
|
||||
for node in ast.walk(tree):
|
||||
if isinstance(node, ast.Import):
|
||||
for alias in node.names:
|
||||
# 只保留顶层包名
|
||||
module = alias.name.split('.')[0]
|
||||
imports.add(module)
|
||||
|
||||
elif isinstance(node, ast.ImportFrom):
|
||||
if node.module:
|
||||
module = node.module.split('.')[0]
|
||||
imports.add(module)
|
||||
|
||||
except Exception as e:
|
||||
print(f"Warning: Failed to parse {file_path}: {e}", file=sys.stderr)
|
||||
|
||||
return imports
|
||||
|
||||
|
||||
def analyze_python_project(project_path: Path) -> Dict:
|
||||
"""分析 Python 项目的依赖"""
|
||||
all_imports = set()
|
||||
file_count = 0
|
||||
|
||||
# 遍历所有 Python 文件
|
||||
for py_file in project_path.rglob("*.py"):
|
||||
# 跳过虚拟环境和 node_modules
|
||||
if any(part in py_file.parts for part in ['venv', 'env', '.venv', 'node_modules', '__pycache__']):
|
||||
continue
|
||||
|
||||
file_count += 1
|
||||
imports = extract_python_imports(py_file)
|
||||
all_imports.update(imports)
|
||||
|
||||
# 读取声明的依赖
|
||||
declared_deps = set()
|
||||
|
||||
requirements_txt = project_path / "requirements.txt"
|
||||
if requirements_txt.exists():
|
||||
for line in requirements_txt.read_text(encoding='utf-8').split('\n'):
|
||||
line = line.strip()
|
||||
if line and not line.startswith('#'):
|
||||
# 去掉版本号
|
||||
dep = line.split('==')[0].split('>=')[0].split('~=')[0].strip()
|
||||
declared_deps.add(dep)
|
||||
|
||||
pyproject_toml = project_path / "pyproject.toml"
|
||||
if pyproject_toml.exists():
|
||||
content = pyproject_toml.read_text(encoding='utf-8')
|
||||
# 简单提取(不使用 toml 库)
|
||||
in_deps = False
|
||||
for line in content.split('\n'):
|
||||
if '[tool.poetry.dependencies]' in line or '[project.dependencies]' in line:
|
||||
in_deps = True
|
||||
continue
|
||||
if in_deps and line.strip().startswith('['):
|
||||
break
|
||||
if in_deps and '=' in line:
|
||||
dep = line.split('=')[0].strip().strip('"')
|
||||
if dep != 'python':
|
||||
declared_deps.add(dep)
|
||||
|
||||
# Python 标准库(部分常见的)
|
||||
stdlib = {
|
||||
'os', 'sys', 'json', 'time', 'datetime', 'collections', 'itertools',
|
||||
'functools', 'pathlib', 're', 'math', 'random', 'typing', 'abc',
|
||||
'asyncio', 'logging', 'unittest', 'argparse', 'subprocess', 'io',
|
||||
'copy', 'pickle', 'sqlite3', 'http', 'urllib', 'email', 'uuid'
|
||||
}
|
||||
|
||||
# 区分第三方包和标准库
|
||||
third_party = all_imports - stdlib
|
||||
|
||||
return {
|
||||
"language": "Python",
|
||||
"files_analyzed": file_count,
|
||||
"total_imports": len(all_imports),
|
||||
"third_party_imports": list(sorted(third_party)),
|
||||
"declared_dependencies": list(sorted(declared_deps)),
|
||||
"undeclared_usage": list(sorted(third_party - declared_deps)),
|
||||
"unused_dependencies": list(sorted(declared_deps - third_party))
|
||||
}
|
||||
|
||||
|
||||
def analyze_javascript_project(project_path: Path) -> Dict:
|
||||
"""分析 JavaScript 项目的依赖"""
|
||||
package_json = project_path / "package.json"
|
||||
|
||||
if not package_json.exists():
|
||||
return None
|
||||
|
||||
try:
|
||||
import json as json_module
|
||||
content = json_module.loads(package_json.read_text(encoding='utf-8'))
|
||||
|
||||
dependencies = content.get("dependencies", {})
|
||||
dev_dependencies = content.get("devDependencies", {})
|
||||
|
||||
return {
|
||||
"language": "JavaScript/TypeScript",
|
||||
"dependencies": list(dependencies.keys()),
|
||||
"dev_dependencies": list(dev_dependencies.keys()),
|
||||
"total_dependencies": len(dependencies) + len(dev_dependencies)
|
||||
}
|
||||
|
||||
except Exception as e:
|
||||
print(f"Error parsing package.json: {e}", file=sys.stderr)
|
||||
return None
|
||||
|
||||
|
||||
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)
|
||||
|
||||
results = []
|
||||
|
||||
# 分析 Python
|
||||
python_result = analyze_python_project(project_path)
|
||||
if python_result["files_analyzed"] > 0:
|
||||
results.append(python_result)
|
||||
|
||||
# 分析 JavaScript
|
||||
js_result = analyze_javascript_project(project_path)
|
||||
if js_result:
|
||||
results.append(js_result)
|
||||
|
||||
# 输出结果
|
||||
output = {
|
||||
"project_path": str(project_path),
|
||||
"analyses": results
|
||||
}
|
||||
|
||||
print(json.dumps(output, indent=2, ensure_ascii=False))
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
Reference in New Issue
Block a user