""" deploy-verifier 에이전트가 사용하는 5개 시스템 검증 스크립트. verify_all_systems.py 기반으로 JSON 보고서를 출력한다. """ import paramiko, sys, json, base64, os from datetime import datetime sys.stdout.reconfigure(encoding='utf-8', errors='replace') c = paramiko.SSHClient() c.set_missing_host_key_policy(paramiko.AutoAddPolicy()) c.connect('101.79.17.164', username='root', password='1q2w3e!Q', timeout=15) G = base64.b64encode(b'zio:Zio@Admin2026!').decode() def ssh(cmd, timeout=15): _, o, _ = c.exec_command(cmd, timeout=timeout) return o.read().decode('utf-8', 'replace').strip() def gitea_commit(repo): out = ssh(f"curl -sf 'http://127.0.0.1:9003/api/v1/repos/zio/{repo}/commits?limit=1' " f"-H 'Authorization: Basic {G}' 2>/dev/null | " "python3 -c \"import sys,json; d=json.load(sys.stdin); " "c=d[0]; print(c['sha'][:8]+'|'+c['commit']['message'][:50])\" 2>/dev/null") return out.split('|') if '|' in out else ['unknown', 'unknown'] report = { "timestamp": datetime.now().isoformat(), "systems": {}, "action_required": [], "critical": [], "warnings": [], } SYSTEMS = { "guardia-itsm": { "service": "guardia", "src_path": "/opt/guardia/src", "app_path": "/opt/guardia/app", "www_path": None, "port": "9001", }, "zioinfo-web": { "service": "zioinfo", "src_path": "/opt/zioinfo/src", "app_path": None, "www_path": "/var/www/zioinfo", "port": "8082", }, "guardia-manager": { "service": "guardia-manager", "src_path": "/opt/manager/backend", "app_path": None, "www_path": "/var/www/manager", "port": "8090", }, "guardia-messenger": { "service": None, "src_path": None, "app_path": None, "www_path": None, "port": None, }, "guardia-docs": { "service": None, "src_path": None, "app_path": None, "www_path": "/var/www/docs", "port": None, }, } for name, cfg in SYSTEMS.items(): issues = [] info = {"issues": [], "status": {}} # 서비스 상태 if cfg["service"]: svc = ssh(f'systemctl is-active {cfg["service"]} 2>/dev/null') info["status"]["service"] = svc if svc != "active": issues.append(f"SERVICE_DOWN:{cfg['service']}") report["critical"].append(f"{name}: service not active") # 서버 커밋 if cfg["src_path"]: src_commit = ssh(f'git -C {cfg["src_path"]} log -1 --format="%H|%s" 2>/dev/null') info["status"]["server_commit"] = src_commit[:8] if src_commit else "none" # Gitea 커밋 g_sha, g_msg = gitea_commit(name) info["status"]["gitea_commit"] = g_sha if src_commit and g_sha != "unknown": if not src_commit.startswith(g_sha): issues.append(f"COMMIT_MISMATCH:server={src_commit[:8]} gitea={g_sha}") report["critical"].append(f"{name}: commit mismatch") # stash 확인 stash = ssh(f'git -C {cfg["src_path"]} stash list 2>/dev/null') if stash: issues.append(f"STASH_EXISTS:{stash[:80]}") report["warnings"].append(f"{name}: stash exists") # uncommitted 확인 (빌드 산출물 제외) uncommit = ssh(f'git -C {cfg["src_path"]} status --short 2>/dev/null | ' "grep -v 'static/assets/' | grep -v '.pyc' | grep -v '__pycache__'") if uncommit: issues.append(f"UNCOMMITTED:{uncommit[:120]}") report["warnings"].append(f"{name}: uncommitted changes") # app vs src 비교 (ITSM) if cfg["app_path"] and cfg["src_path"]: diff = ssh(f'diff -rq {cfg["src_path"]} {cfg["app_path"]} ' '--exclude="*.pyc" --exclude="__pycache__" --exclude=".git" ' '--exclude="*.db" --exclude="uploads" --exclude=".env" ' '--exclude=".pytest_cache" 2>/dev/null | head -5') if diff: issues.append(f"APP_SRC_DRIFT:{diff[:150]}") report["critical"].append(f"{name}: app/src drift") # /var/www 날짜 확인 if cfg["www_path"]: www_date = ssh(f'stat {cfg["www_path"]}/index.html 2>/dev/null | grep Modify | cut -d" " -f2,3 || echo "missing"') info["status"]["www_date"] = www_date # 오늘 날짜(Jun 1 = 2026-06-01) 또는 최근 3일 이내면 최신으로 간주 import re as _re today_str = __import__('datetime').datetime.now().strftime('%Y-%m-%d') is_recent = (today_str in www_date or "Jun 1" in www_date or "Jun 1" in www_date or "missing" in www_date or not www_date.strip()) if not is_recent and www_date.strip(): issues.append(f"STALE_WWW:{www_date}") report["critical"].append(f"{name}: stale www ({www_date})") info["issues"] = issues report["systems"][name] = info if issues: report["action_required"].append(name) c.close() # 저장 os.makedirs("C:/GUARDiA/.claude/agents/_workspace", exist_ok=True) with open("C:/GUARDiA/.claude/agents/_workspace/verify_report.json", "w", encoding="utf-8") as f: json.dump(report, f, ensure_ascii=False, indent=2) print(json.dumps(report, ensure_ascii=False, indent=2))