Files
zkxw/scripts/update-graph.sh
T
2026-08-06 16:54:32 +08:00

201 lines
6.3 KiB
Bash
Executable File
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
#!/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
# Re-extract AST for all code files (fast, deterministic)
# 注:不依赖旧的 .graphify_extract.json;缺失时从头提取即可
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(节点过多时跳过可视化,仍保存 json/report)
try:
to_html(G, communities, 'graphify-out/graph.html', community_labels=labels or None)
except ValueError as e:
print(f'[graphify] skip HTML viz: {e}')
# 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(保留 .graphify_extract.json 供下次增量更新)
rm -f graphify-out/.graphify_detect.json graphify-out/.graphify_ast.json graphify-out/.graphify_incremental.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"