#!/usr/bin/env python3 """Generate the auditable frontend deletion inventory from the working tree.""" from __future__ import annotations import subprocess from collections import Counter, defaultdict from pathlib import Path ROOT = Path(__file__).resolve().parents[1] OUTPUT = ROOT / "docs" / "architecture" / "FRONTEND_REMOVAL_INVENTORY.md" def deleted_files() -> list[str]: result = subprocess.run( ["git", "diff", "--name-only", "--diff-filter=D", "--", "frontend"], cwd=ROOT, check=True, capture_output=True, text=True, ) return sorted(line for line in result.stdout.splitlines() if line) def group_name(path: str) -> str: parts = Path(path).parts if len(parts) >= 5 and parts[:3] == ("frontend", "src", "views"): return f"views/{parts[3]}" if len(parts) >= 4 and parts[:3] == ("frontend", "src", "api"): return "api" if len(parts) >= 4 and parts[:3] == ("frontend", "src", "components"): return "components" if len(parts) >= 4 and parts[:3] == ("frontend", "src", "layout"): return "layout" return "/".join(parts[1:3]) def render(files: list[str]) -> str: groups: dict[str, list[str]] = defaultdict(list) for path in files: groups[group_name(path)].append(path) counts = Counter({name: len(paths) for name, paths in groups.items()}) lines = [ "# 前端历史代码删除清单", "", "> 生成方式:`python scripts/generate_frontend_removal_inventory.py`。本文件基于当前未提交删除集生成;合并前应再次生成并复核。", "", "## 1. 基线与恢复方式", "", "- 原解析报告识别出 348 个前端未匹配调用,产品确认其主体为已下线后端服务遗留;本轮不重建这些旧接口。", f"- 当前 Git 删除集包含 {len(files)} 个前端文件;一个文件可包含多个调用,因此文件数不等于 348 个调用数。", "- 原始代码备份:`/Users/maxiaolong/Documents/Codex/2026-07-06/g-i/DataOps-platform-backup-before-cleanup-20260716-170321.tar.gz`。", "- SHA-256:`866c6c4aff127f88497a5ddf39ac8d8da93008cc784ae6f276f80a7cd17d4ef0`。", "- 恢复优先级:先从 Git 历史恢复单文件;若原始工作区未入库内容缺失,再从上述只读备份提取。禁止将整份备份直接覆盖当前工作区。", "", "## 2. 删除批次", "", "| 批次 | 范围 | 处置依据 |", "|---|---|---|", "| C1 | Vanna、Airflow、模型训练、问卷薪酬、图表定制 | 产品确认整体下线 |", "| C2 | 数据资源、数据模型、数据指标和旧数据资产原型 | 静态引用与路由复核后确认没有保留链路 |", "| C3 | 旧数据工厂非 n8n 页面、执行/调度/节点管理 | 执行能力统一由 n8n 提供 |", "| C4 | 旧系统管理、自助注册、租户、升级管理 | 后续以管理员/编辑者/查看者重建 |", "| C5 | 不可达公共组件、API 封装和演示页 | 无活跃路由或 import 消费方 |", "", "## 3. 明确保留例外", "", "- n8n 适配页面和 `/api/datafactory/*` 后端代理接口;Workflow 保留为 n8n 原生概念。", "- DataFlow 治理定义页面;后续新增一个 DataFlow 对多个 n8n Workflow 版本的映射。", "- 数据治理知识库入口和工作台;当前为验收占位,下一阶段补齐真实能力。", "- 数据安全入口;当前不宣称已实现安全治理能力。", "- HOPMS 数据导入工具及其验收样例。", "", "## 4. 按目录统计", "", "| 分组 | 删除文件数 |", "|---|---:|", ] for name, count in sorted(counts.items(), key=lambda item: (-item[1], item[0])): lines.append(f"| `{name}` | {count} |") lines.extend( [ "", "## 5. 合并门禁", "", "1. `npm --prefix frontend run build` 成功。", "2. 路由表中不再出现已下线模块,活跃页面无失效 import。", "3. `tests/test_cleanup_contract.py`、Docker 静态隔离测试与本地冒烟测试全部通过。", "4. 数据安全、治理知识库、工作台、DataFlow 和 n8n 保留入口可访问。", "5. 生产数据库不执行删表;仅提交代码和建库脚本清理。", "", "## 6. 完整删除文件清单", "", ] ) for name in sorted(groups): lines.append(f"### {name}") lines.append("") lines.extend(f"- `{path}`" for path in groups[name]) lines.append("") return "\n".join(lines).rstrip() + "\n" def main() -> None: files = deleted_files() if not files: raise SystemExit("No deleted frontend files found in the working tree") OUTPUT.parent.mkdir(parents=True, exist_ok=True) OUTPUT.write_text(render(files), encoding="utf-8") print(f"Generated {OUTPUT.relative_to(ROOT)} with {len(files)} deleted files") if __name__ == "__main__": main()