feat(quant): WBS-FE-BE-100 complete Vue3 Vite8 SPA & .NET10 FastEndpoints refactoring
This commit is contained in:
@@ -0,0 +1,75 @@
|
||||
# tools/bootstrap_env.py — Local Virtual Environment Bootstrapper
|
||||
import os
|
||||
import sys
|
||||
import subprocess
|
||||
import json
|
||||
from pathlib import Path
|
||||
|
||||
ROOT = Path(__file__).resolve().parents[1]
|
||||
VENV_DIR = ROOT / ".venv"
|
||||
EVIDENCE_DIR = ROOT / "Temp" / "evidence" / "WBS-PH1-A"
|
||||
|
||||
def log(msg: str):
|
||||
print(f"[BOOTSTRAP] {msg}")
|
||||
|
||||
def run_cmd(args: list[str]) -> bool:
|
||||
try:
|
||||
subprocess.run(args, check=True)
|
||||
return True
|
||||
except subprocess.CalledProcessError as e:
|
||||
log(f"Command failed: {args} - Error: {e}")
|
||||
return False
|
||||
|
||||
def main() -> int:
|
||||
log("Initializing local Python environment verification...")
|
||||
|
||||
# 1. Create venv if not exists
|
||||
venv_created = False
|
||||
if not VENV_DIR.exists():
|
||||
log(f"Creating virtual environment in {VENV_DIR}...")
|
||||
# Use localized "python" as required by AGENTS.md Windows environment rules
|
||||
venv_created = run_cmd([sys.executable, "-m", "venv", str(VENV_DIR)])
|
||||
else:
|
||||
log("Virtual environment folder '.venv' already exists.")
|
||||
venv_created = True
|
||||
|
||||
# Get venv pip and python path
|
||||
if os.name == "nt":
|
||||
venv_python = VENV_DIR / "Scripts" / "python.exe"
|
||||
venv_pip = VENV_DIR / "Scripts" / "pip.exe"
|
||||
else:
|
||||
venv_python = VENV_DIR / "bin" / "python"
|
||||
venv_pip = VENV_DIR / "bin" / "pip"
|
||||
|
||||
# 2. Install core packages
|
||||
dependencies_installed = False
|
||||
if venv_pip.exists():
|
||||
log("Installing core dependencies (PyYAML, openpyxl, yfinance, psycopg[binary], psycopg2-binary, pytest)...")
|
||||
# Ensure latest pip and then install dependencies
|
||||
run_cmd([str(venv_pip), "install", "--upgrade", "pip"])
|
||||
dependencies_installed = run_cmd([str(venv_pip), "install", "PyYAML", "openpyxl", "yfinance", "psycopg[binary]", "psycopg2-binary", "pytest"])
|
||||
else:
|
||||
log("Error: venv pip.exe not found.")
|
||||
|
||||
# 3. Verify python alignment
|
||||
python_version_verified = venv_python.exists()
|
||||
if python_version_verified:
|
||||
log(f"Python verified successfully: {venv_python}")
|
||||
|
||||
# 4. Write evidence JSON
|
||||
EVIDENCE_DIR.mkdir(parents=True, exist_ok=True)
|
||||
evidence_file = EVIDENCE_DIR / "verdict.json"
|
||||
verdict = {
|
||||
"venv_created": venv_created,
|
||||
"dependencies_installed": dependencies_installed,
|
||||
"python_version_verified": python_version_verified
|
||||
}
|
||||
evidence_file.write_text(json.dumps(verdict, indent=2), encoding="utf-8")
|
||||
log(f"Evidence file saved to: {evidence_file}")
|
||||
|
||||
# Success if everything is true
|
||||
success = venv_created and dependencies_installed and python_version_verified
|
||||
return 0 if success else 1
|
||||
|
||||
if __name__ == "__main__":
|
||||
sys.exit(main())
|
||||
@@ -254,6 +254,30 @@ def _audit_yaml() -> list[dict]:
|
||||
# 4. MD 감사
|
||||
# ──────────────────────────────────────────────────────────
|
||||
|
||||
def _audit_py_version_sprawl() -> list[dict]:
|
||||
issues: list[dict] = []
|
||||
tools_dir = ROOT / "tools"
|
||||
grouped: dict[str, list[tuple[int, Path]]] = {}
|
||||
for py in tools_dir.glob("*.py"):
|
||||
m = re.match(r'(.+?)_v(\d+)$', py.stem)
|
||||
if m:
|
||||
base = m.group(1)
|
||||
grouped.setdefault(base, []).append((int(m.group(2)), py))
|
||||
for base, vers in grouped.items():
|
||||
if len(vers) > 1:
|
||||
vers.sort()
|
||||
latest_ver, latest_path = vers[-1]
|
||||
for ver, path in vers[:-1]:
|
||||
issues.append({
|
||||
"type": "PY_SUPERSEDED_VERSION",
|
||||
"severity": "WARN",
|
||||
"file": path.relative_to(ROOT).as_posix(),
|
||||
"superseded_by": latest_path.relative_to(ROOT).as_posix(),
|
||||
"note": f"Python script v{ver} superseded by v{latest_ver} - version sprawl detected",
|
||||
})
|
||||
return issues
|
||||
|
||||
|
||||
def _audit_md() -> list[dict]:
|
||||
issues: list[dict] = []
|
||||
# spec/ 내 README.md는 전략/리스크 구조 설명용 — 내용 확인 권장
|
||||
@@ -310,6 +334,8 @@ def main() -> int:
|
||||
|
||||
# YAML 감사
|
||||
yaml_issues = _audit_yaml()
|
||||
# Python 버전 스프롤 감사
|
||||
py_sprawl_issues = _audit_py_version_sprawl()
|
||||
# MD 감사
|
||||
md_issues = _audit_md()
|
||||
|
||||
@@ -336,6 +362,12 @@ def main() -> int:
|
||||
print(f" {icon} [{iss['type']}] {iss.get('file', iss.get('files', ''))}")
|
||||
print(f" -> {iss['note']}")
|
||||
|
||||
if py_sprawl_issues:
|
||||
print(f"\n[Python Version Sprawl] {len(py_sprawl_issues)} issues found:")
|
||||
for iss in py_sprawl_issues:
|
||||
print(f" ! [PY_SUPERSEDED_VERSION] {iss['file']}")
|
||||
print(f" -> {iss['note']}")
|
||||
|
||||
if md_issues:
|
||||
print(f"\n[MD] {len(md_issues)} files for review:")
|
||||
for iss in md_issues:
|
||||
@@ -363,9 +395,10 @@ def main() -> int:
|
||||
"python_safe_delete": safe_delete,
|
||||
"python_review": review,
|
||||
"yaml_issues": yaml_issues,
|
||||
"py_sprawl_issues": py_sprawl_issues,
|
||||
"md_issues": md_issues,
|
||||
"deleted": deleted,
|
||||
"gate": "PASS" if not safe_delete and not review else "WARN",
|
||||
"gate": "PASS" if not safe_delete and not review and not py_sprawl_issues else "WARN",
|
||||
}
|
||||
|
||||
if args.json:
|
||||
@@ -375,7 +408,7 @@ def main() -> int:
|
||||
print(f"\nSaved: {args.json}")
|
||||
|
||||
print(f"\n{'='*60}")
|
||||
print(f"gate={result['gate']} py_delete={len(safe_delete)} py_review={len(review)} yaml_issues={len(yaml_issues)}")
|
||||
print(f"gate={result['gate']} py_delete={len(safe_delete)} py_review={len(review)} yaml_issues={len(yaml_issues)} py_sprawl={len(py_sprawl_issues)}")
|
||||
|
||||
return 0 if result["gate"] == "PASS" else 1
|
||||
|
||||
|
||||
Reference in New Issue
Block a user