89b4c118d1
src/gas/core/, src/gas_adapter_parts/의 모듈 소스를 clasp push 대상인 루트 .gs 번들(gas_lib.gs, gas_data_collect.gs, gas_data_feed.gs)로 해시 검증과 함께 생성한다. 번들 파일에는 "GENERATED — DO NOT EDIT MANUALLY" 헤더와 소스 해시를 새겨 수동 편집 드리프트를 방지한다. - build_gas_bundle_v1.py: 소스→번들 생성, 해시 헤더 삽입 - validate_gas_bundle_sync_v1.py: 번들이 현재 소스 해시와 일치하는지 검증 - audit_tools_thin_wrapper_v1.py: tools/ CLI가 핵심 로직 없이 thin wrapper로만 동작하는지 감사 - deploy_gas.py: 번들 빌드 파이프라인과 연동
138 lines
4.6 KiB
Python
138 lines
4.6 KiB
Python
#!/usr/bin/env python3
|
|
from __future__ import annotations
|
|
|
|
import hashlib
|
|
import json
|
|
import re
|
|
import sys
|
|
from pathlib import Path
|
|
|
|
ROOT = Path(__file__).resolve().parent.parent
|
|
|
|
# Mappings of bundles to sources
|
|
BUNDLES = {
|
|
"gas_lib.gs": [
|
|
"src/gas/core/gas_lib.gs"
|
|
],
|
|
"gas_data_collect.gs": [
|
|
"src/gas_adapter_parts/gdc_01_fetch_fundamentals.gs",
|
|
"src/gas_adapter_parts/gdc_02_account_satellite.gs"
|
|
],
|
|
"gas_data_feed.gs": [
|
|
"src/gas_adapter_parts/gdf_01_price_metrics.gs",
|
|
"src/gas_adapter_parts/gdf_02_harness_assembly.gs",
|
|
"src/gas_adapter_parts/gdf_03_portfolio_gates.gs",
|
|
"src/gas_adapter_parts/gdf_04_execution_quality.gs",
|
|
"src/gas_adapter_parts/gdf_05_alpha_engines.gs",
|
|
"src/gas_adapter_parts/gdf_06_rebalance.gs"
|
|
]
|
|
}
|
|
|
|
def main() -> int:
|
|
bundle_sync_hash_match = True
|
|
manual_edit_generated_bundle_count = 0
|
|
findings = []
|
|
|
|
for bundle_name, src_relative_paths in BUNDLES.items():
|
|
dst_path = ROOT / bundle_name
|
|
if not dst_path.exists():
|
|
bundle_sync_hash_match = False
|
|
findings.append({
|
|
"bundle": bundle_name,
|
|
"status": "MISSING",
|
|
"error": "Generated bundle file does not exist."
|
|
})
|
|
continue
|
|
|
|
content = dst_path.read_text(encoding="utf-8")
|
|
|
|
m_time = re.search(r"Generated At:\s*(.*?)\n", content)
|
|
m_hash = re.search(r"Source Hash:\s*([a-f0-9]+)\n", content)
|
|
|
|
if not m_time or not m_hash:
|
|
bundle_sync_hash_match = False
|
|
findings.append({
|
|
"bundle": bundle_name,
|
|
"status": "INVALID_HEADER",
|
|
"error": "Generated bundle header is invalid."
|
|
})
|
|
continue
|
|
|
|
gen_time = m_time.group(1).strip()
|
|
header_hash = m_hash.group(1).strip()
|
|
|
|
# Concatenate current source file contents
|
|
concatenated_lines = []
|
|
source_missing = False
|
|
for src_rel in src_relative_paths:
|
|
src_path = ROOT / src_rel
|
|
if not src_path.exists():
|
|
source_missing = True
|
|
print(f"ERROR: Source file missing: {src_rel}")
|
|
break
|
|
src_content = src_path.read_text(encoding="utf-8")
|
|
concatenated_lines.append(f"// --- Source: {src_rel} ---")
|
|
concatenated_lines.append(src_content)
|
|
concatenated_lines.append("")
|
|
|
|
if source_missing:
|
|
bundle_sync_hash_match = False
|
|
findings.append({
|
|
"bundle": bundle_name,
|
|
"status": "SOURCE_MISSING",
|
|
"error": "One or more source files are missing."
|
|
})
|
|
continue
|
|
|
|
full_source_content = "\n".join(concatenated_lines)
|
|
actual_hash = hashlib.sha256(full_source_content.encode("utf-8")).hexdigest()
|
|
|
|
# Construct expected content
|
|
expected_content = f"""// =========================================================================
|
|
// GENERATED BUNDLE - DO NOT EDIT THIS FILE MANUALLY
|
|
// Generated At: {gen_time}
|
|
// Source Files: {", ".join(src_relative_paths)}
|
|
// Source Hash: {actual_hash}
|
|
// =========================================================================
|
|
|
|
{full_source_content}"""
|
|
|
|
# Verify hash match
|
|
hash_ok = (header_hash == actual_hash)
|
|
if not hash_ok:
|
|
bundle_sync_hash_match = False
|
|
|
|
# Verify manual edits
|
|
edits = 0
|
|
if content != expected_content:
|
|
edits = 1
|
|
manual_edit_generated_bundle_count += 1
|
|
|
|
findings.append({
|
|
"bundle": bundle_name,
|
|
"status": "SYNCED" if (hash_ok and edits == 0) else "DRIFT",
|
|
"hash_match": hash_ok,
|
|
"manual_edits": edits,
|
|
"header_hash": header_hash,
|
|
"actual_hash": actual_hash
|
|
})
|
|
|
|
gate_passed = bundle_sync_hash_match and (manual_edit_generated_bundle_count == 0)
|
|
|
|
result = {
|
|
"formula_id": "GAS_BUNDLE_SYNC_VALIDATOR_V1",
|
|
"bundle_sync_hash_match": bundle_sync_hash_match,
|
|
"manual_edit_generated_bundle_count": manual_edit_generated_bundle_count,
|
|
"findings": findings,
|
|
"gate": "PASS" if gate_passed else "FAIL"
|
|
}
|
|
|
|
out_path = ROOT / "Temp" / "gas_bundle_validation_v1.json"
|
|
out_path.write_text(json.dumps(result, ensure_ascii=False, indent=2), encoding="utf-8")
|
|
|
|
print(json.dumps(result, ensure_ascii=True, indent=2))
|
|
return 0 if gate_passed else 1
|
|
|
|
if __name__ == "__main__":
|
|
sys.exit(main())
|