140 lines
3.6 KiB
Python
140 lines
3.6 KiB
Python
from __future__ import annotations
|
|
|
|
import hashlib
|
|
import json
|
|
from datetime import datetime
|
|
from pathlib import Path
|
|
|
|
|
|
ROOT = Path(__file__).resolve().parents[1]
|
|
TARGET = ROOT / "app"
|
|
OUTPUT = ROOT / "docs" / "migration" / "原版资产清单.json"
|
|
|
|
DIRECTORIES = (
|
|
"backend",
|
|
"config",
|
|
"static",
|
|
"tests",
|
|
"tools",
|
|
"vendor",
|
|
"游资skills",
|
|
)
|
|
|
|
ROOT_FILES = (
|
|
".dockerignore",
|
|
".env.example",
|
|
".gitignore",
|
|
"advanced_strategies.py",
|
|
"alert_service.py",
|
|
"api_access.py",
|
|
"app_config.py",
|
|
"ARCHITECTURE.md",
|
|
"assistant_agent.py",
|
|
"chart_data_provider.py",
|
|
"compose.yaml",
|
|
"database.py",
|
|
"demo_data.py",
|
|
"Dockerfile",
|
|
"DOCKER_DEPLOY.md",
|
|
"heaven_agent.py",
|
|
"heaven_engine.py",
|
|
"ifind_client.py",
|
|
"llm_strategy.py",
|
|
"llm_stream.py",
|
|
"market_insights.py",
|
|
"mentor_agent.py",
|
|
"package-lock.json",
|
|
"package.json",
|
|
"playwright.config.js",
|
|
"README.md",
|
|
"realtime_aggregator.py",
|
|
"requirements.txt",
|
|
"screener.py",
|
|
"security.py",
|
|
"sentiment_engine.py",
|
|
"server.py",
|
|
"strategy_tracking.py",
|
|
"sync_data.py",
|
|
"THIRD_PARTY_NOTICES.md",
|
|
"trade_journal.py",
|
|
"tushare_client.py",
|
|
)
|
|
|
|
|
|
def digest(path: Path) -> str:
|
|
checksum = hashlib.sha256()
|
|
with path.open("rb") as handle:
|
|
for block in iter(lambda: handle.read(1024 * 1024), b""):
|
|
checksum.update(block)
|
|
return checksum.hexdigest()
|
|
|
|
|
|
def source_files() -> list[Path]:
|
|
files = [ROOT / name for name in ROOT_FILES]
|
|
for directory in DIRECTORIES:
|
|
files.extend(
|
|
path
|
|
for path in (ROOT / directory).rglob("*")
|
|
if path.is_file() and "__pycache__" not in path.parts
|
|
)
|
|
return sorted(set(files), key=lambda path: path.relative_to(ROOT).as_posix())
|
|
|
|
|
|
def build_manifest() -> dict[str, object]:
|
|
assets = []
|
|
mismatches = []
|
|
for source in source_files():
|
|
relative = source.relative_to(ROOT)
|
|
target = TARGET / relative
|
|
source_hash = digest(source)
|
|
target_hash = digest(target) if target.is_file() else ""
|
|
status = "identical" if source_hash == target_hash else "mismatch"
|
|
if status != "identical":
|
|
mismatches.append(relative.as_posix())
|
|
assets.append(
|
|
{
|
|
"source": relative.as_posix(),
|
|
"target": f"app/{relative.as_posix()}",
|
|
"bytes": source.stat().st_size,
|
|
"sha256": source_hash,
|
|
"disposition": "original_copy_pending_move",
|
|
"status": status,
|
|
}
|
|
)
|
|
return {
|
|
"schema_version": 1,
|
|
"generated_at": datetime.now().astimezone().isoformat(timespec="seconds"),
|
|
"source_commit": "41329943c4878fc09ed82ec376eb93ab151e4092",
|
|
"source_root": ".",
|
|
"target_root": "app",
|
|
"excluded": [
|
|
"next/",
|
|
"data/review.db and SQLite sidecars",
|
|
"data/private-mentor-skills/",
|
|
".env",
|
|
"node_modules/",
|
|
"logs, caches and generated test results",
|
|
],
|
|
"asset_count": len(assets),
|
|
"mismatch_count": len(mismatches),
|
|
"mismatches": mismatches,
|
|
"assets": assets,
|
|
}
|
|
|
|
|
|
def main() -> int:
|
|
manifest = build_manifest()
|
|
OUTPUT.write_text(
|
|
json.dumps(manifest, ensure_ascii=False, indent=2) + "\n",
|
|
encoding="utf-8",
|
|
)
|
|
print(
|
|
f"assets={manifest['asset_count']} mismatches={manifest['mismatch_count']} "
|
|
f"output={OUTPUT}"
|
|
)
|
|
return 1 if manifest["mismatch_count"] else 0
|
|
|
|
|
|
if __name__ == "__main__":
|
|
raise SystemExit(main())
|