202 lines
6.4 KiB
Bash
Executable File
202 lines
6.4 KiB
Bash
Executable File
#!/bin/bash
|
|
# update-graph.sh — 增量更新知识图谱
|
|
# 用法:
|
|
# ./scripts/update-graph.sh # 更新前后端
|
|
# ./scripts/update-graph.sh backend # 只更新后端
|
|
# ./scripts/update-graph.sh frontend # 只更新前端
|
|
#
|
|
# 由 AI Agent 在完成代码修改后自动调用,或手动执行。
|
|
|
|
set -euo pipefail
|
|
ROOT_DIR="$(cd "$(dirname "$0")/.." && pwd)"
|
|
PYTHON=""
|
|
|
|
# 检测 graphify 使用的 Python 解释器
|
|
detect_python() {
|
|
local graphify_out="$1/graphify-out/.graphify_python"
|
|
if [ -f "$graphify_out" ]; then
|
|
PYTHON="$(cat "$graphify_out")"
|
|
else
|
|
PYTHON="python3"
|
|
fi
|
|
}
|
|
|
|
# 检查 graphify 是否安装
|
|
check_graphify() {
|
|
if ! "$PYTHON" -c "import graphify" 2>/dev/null; then
|
|
echo "[graphify] 未安装,正在安装..."
|
|
"$PYTHON" -m pip install graphifyy -q 2>/dev/null || "$PYTHON" -m pip install graphifyy -q --break-system-packages 2>&1 | tail -3
|
|
fi
|
|
}
|
|
|
|
# 增量更新单个项目的图谱
|
|
update_graph() {
|
|
local name="$1"
|
|
local src_dir="$2"
|
|
local label="$3"
|
|
|
|
echo ""
|
|
echo "=== 更新 ${label} 知识图谱 (${src_dir}) ==="
|
|
|
|
detect_python "$ROOT_DIR/$name"
|
|
check_graphify
|
|
|
|
cd "$ROOT_DIR/$name"
|
|
|
|
local detect_json="graphify-out/.graphify_detect.json"
|
|
local manifest_json="graphify-out/manifest.json"
|
|
|
|
if [ ! -f "$manifest_json" ]; then
|
|
echo "[graphify] 未找到 manifest.json,跳过增量更新(首次需运行完整构建)"
|
|
return 0
|
|
fi
|
|
|
|
# 运行增量检测
|
|
"$PYTHON" -c "
|
|
import sys, json
|
|
from graphify.detect import detect_incremental, save_manifest
|
|
from pathlib import Path
|
|
|
|
result = detect_incremental(Path('${src_dir}'))
|
|
new_total = result.get('new_total', 0)
|
|
Path('graphify-out/.graphify_incremental.json').write_text(json.dumps(result))
|
|
print(f'{new_total} file(s) changed since last run.')
|
|
if new_total == 0:
|
|
print('SKIP: Nothing to update.')
|
|
sys.exit(0)
|
|
"
|
|
|
|
# 如果没有变化,跳过
|
|
local changed=$?
|
|
if [ $changed -ne 0 ]; then
|
|
return 0
|
|
fi
|
|
|
|
# 检查是否只是代码变更
|
|
"$PYTHON" -c "
|
|
import json
|
|
from pathlib import Path
|
|
|
|
result = json.loads(Path('graphify-out/.graphify_incremental.json').read_text())
|
|
code_exts = {'.py','.ts','.js','.vue','.go','.rs','.java','.cpp','.c','.rb','.swift','.kt','.cs','.scala','.php','.cc','.cxx','.hpp','.h','.kts'}
|
|
new_files = result.get('new_files', {})
|
|
all_changed = [f for files in new_files.values() for f in files]
|
|
code_only = all(Path(f).suffix.lower() in code_exts for f in all_changed) if all_changed else True
|
|
print('code_only:', code_only)
|
|
" 2>/dev/null
|
|
|
|
# AST 提取
|
|
"$PYTHON" -c "
|
|
import sys, json
|
|
from graphify.extract import collect_files, extract
|
|
from graphify.build import build_from_json
|
|
from graphify.cluster import cluster, score_all
|
|
from graphify.analyze import god_nodes, surprising_connections, suggest_questions
|
|
from graphify.report import generate
|
|
from graphify.export import to_json, to_html
|
|
from pathlib import Path
|
|
|
|
extraction = json.loads(Path('graphify-out/.graphify_extract.json').read_text()) if Path('graphify-out/.graphify_extract.json').exists() else None
|
|
if extraction is None:
|
|
print('ERROR: No existing extraction found. Run full build first.')
|
|
sys.exit(1)
|
|
|
|
# Re-extract AST for all code files (fast, deterministic)
|
|
from graphify.detect import detect
|
|
detection = detect(Path('${src_dir}'))
|
|
Path('graphify-out/.graphify_detect.json').write_text(json.dumps(detection))
|
|
|
|
code_files = []
|
|
for f in detection.get('files', {}).get('code', []):
|
|
code_files.extend(collect_files(Path(f)) if Path(f).is_dir() else [Path(f)])
|
|
|
|
if code_files:
|
|
result = extract(code_files)
|
|
Path('graphify-out/.graphify_extract.json').write_text(json.dumps(result, indent=2))
|
|
print(f'AST: {len(result[\"nodes\"])} nodes, {len(result[\"edges\"])} edges')
|
|
else:
|
|
print('No code files found.')
|
|
sys.exit(1)
|
|
|
|
# Build graph
|
|
G = build_from_json(result)
|
|
communities = cluster(G)
|
|
cohesion = score_all(G, communities)
|
|
tokens = {'input': 0, 'output': 0}
|
|
gods = god_nodes(G)
|
|
surprises = surprising_connections(G, communities)
|
|
|
|
# Try to load existing labels
|
|
labels = {}
|
|
labels_path = Path('graphify-out/.graphify_labels.json')
|
|
if labels_path.exists():
|
|
labels_raw = json.loads(labels_path.read_text())
|
|
labels = {int(k): v for k, v in labels_raw.items()}
|
|
else:
|
|
labels = {cid: f'Community {cid}' for cid in communities}
|
|
|
|
questions = suggest_questions(G, communities, labels)
|
|
|
|
# Generate outputs
|
|
report = generate(G, communities, cohesion, labels, gods, surprises, detection, tokens, '${src_dir}', suggested_questions=questions)
|
|
Path('graphify-out/GRAPH_REPORT.md').write_text(report)
|
|
to_json(G, communities, 'graphify-out/graph.json')
|
|
|
|
# Save analysis for labels
|
|
analysis = {
|
|
'communities': {str(k): v for k, v in communities.items()},
|
|
'cohesion': {str(k): v for k, v in cohesion.items()},
|
|
'gods': gods,
|
|
'surprises': surprises,
|
|
}
|
|
Path('graphify-out/.graphify_analysis.json').write_text(json.dumps(analysis, indent=2))
|
|
Path('graphify-out/.graphify_labels.json').write_text(json.dumps({str(k): v for k, v in labels.items()}))
|
|
|
|
# Generate HTML
|
|
to_html(G, communities, 'graphify-out/graph.html', community_labels=labels or None)
|
|
|
|
# Save manifest
|
|
from graphify.detect import save_manifest
|
|
save_manifest(detection['files'])
|
|
|
|
print(f'Graph updated: {G.number_of_nodes()} nodes, {G.number_of_edges()} edges, {len(communities)} communities')
|
|
print(f'Report: graphify-out/GRAPH_REPORT.md')
|
|
" || {
|
|
echo "[graphify] 增量更新失败"
|
|
return 1
|
|
}
|
|
|
|
# Cleanup temp files
|
|
rm -f graphify-out/.graphify_detect.json graphify-out/.graphify_extract.json graphify-out/.graphify_ast.json graphify-out/.graphify_incremental.json graphify-out/.graphify_analysis.json graphify-out/.graphify_labels.json
|
|
|
|
cd "$ROOT_DIR"
|
|
echo "[graphify] ${label} 图谱更新完成 ✓"
|
|
}
|
|
|
|
# ---- Main ----
|
|
TARGET="${1:-all}"
|
|
|
|
echo "╔══════════════════════════════════════╗"
|
|
echo "║ zkxw 知识图谱增量更新工具 ║"
|
|
echo "╚══════════════════════════════════════╝"
|
|
|
|
case "$TARGET" in
|
|
backend)
|
|
update_graph "backend" "app" "后端"
|
|
;;
|
|
frontend)
|
|
update_graph "web-admin" "src" "前端"
|
|
;;
|
|
all|"")
|
|
update_graph "backend" "app" "后端"
|
|
update_graph "web-admin" "src" "前端"
|
|
;;
|
|
*)
|
|
echo "用法: $0 [backend|frontend|all]"
|
|
exit 1
|
|
;;
|
|
esac
|
|
|
|
echo ""
|
|
echo "✅ 知识图谱更新完毕。HTML 可视化: */graphify-out/graph.html"
|