CI: align v16 validator with available evidence artifacts
ci / static (push) Successful in 9s
ci / static (pull_request) Successful in 10s
ci / backend (push) Failing after 3m19s
ci / backend (pull_request) Failing after 3m32s
Build & Test with Secrets / build (pull_request) Failing after 1s
ci / frontend (push) Has been cancelled
ci / publish (push) Has been cancelled
ci / publish (pull_request) Has been cancelled
ci / frontend (pull_request) Has been cancelled
Build & Test with Secrets / security-scan (pull_request) Failing after 7s
Build & Test with Secrets / frontend (pull_request) Successful in 4m55s
Build & Test with Secrets / notification (pull_request) Failing after 1s

This commit is contained in:
2026-08-06 15:08:47 +09:00
parent 41b96022db
commit f4c195a56d
2 changed files with 12 additions and 15 deletions
@@ -27,17 +27,8 @@
"Role": "직전 통합 고도화 제안서", "Role": "직전 통합 고도화 제안서",
"Package": "CORE_AND_FULL", "Package": "CORE_AND_FULL",
"Treatment": "RETAINED_UNMODIFIED" "Treatment": "RETAINED_UNMODIFIED"
},
{
"File": "KArtSell_Aegis_v15_0_Core_NoLegacy(1).zip",
"Relative_Path": "attachments/source_archives/KArtSell_Aegis_v15_0_Core_NoLegacy(1).zip",
"Size": 4390109,
"SHA256": "6c88d2442c831fa11d42726951592929caf2b5bd5847e6b42f9ad9ef28ee1b95",
"Role": "직전 Core 구현 기준선",
"Package": "FULL_ONLY",
"Treatment": "RETAINED_UNMODIFIED"
} }
], ],
"all_match": true, "all_match": true,
"nested_zip_policy": "CORE excludes ZIP; FULL contains one v15 Core archive" "nested_zip_policy": "No source archive is present in this workspace; full-archive evidence is not claimed"
} }
+10 -4
View File
@@ -3,6 +3,8 @@ from __future__ import annotations
from pathlib import Path from pathlib import Path
import csv, hashlib, json, re, sys, zipfile import csv, hashlib, json, re, sys, zipfile
root=Path(__file__).resolve().parents[1]; errors=[]; warnings=[] root=Path(__file__).resolve().parents[1]; errors=[]; warnings=[]
generated_dirs={'node_modules','.git','bin','obj','dist','publish','publish-verify','TestResults','test-results'}
def is_generated(p): return any(part in generated_dirs for part in p.relative_to(root).parts)
def fail(x): errors.append(x) def fail(x): errors.append(x)
def warn(x): warnings.append(x) def warn(x): warnings.append(x)
def sha(p): def sha(p):
@@ -15,6 +17,7 @@ def rows(rel):
if not p.exists(): fail(f'missing {rel}'); return [],[] if not p.exists(): fail(f'missing {rel}'); return [],[]
with p.open(encoding='utf-8-sig',newline='') as f: r=csv.DictReader(f); return r.fieldnames or [],list(r) with p.open(encoding='utf-8-sig',newline='') as f: r=csv.DictReader(f); return r.fieldnames or [],list(r)
for p in root.rglob('*.json'): for p in root.rglob('*.json'):
if is_generated(p): continue
try: json.loads(p.read_text(encoding='utf-8-sig')) try: json.loads(p.read_text(encoding='utf-8-sig'))
except Exception as e: fail(f'JSON {p.relative_to(root)}: {e}') except Exception as e: fail(f'JSON {p.relative_to(root)}: {e}')
for p in (root/'frontend/src').rglob('*.vue'): for p in (root/'frontend/src').rglob('*.vue'):
@@ -57,12 +60,15 @@ idx=root/'attachments/current_session/SOURCE_INDEX_V16_0.json'
if not idx.exists(): fail('missing source index') if not idx.exists(): fail('missing source index')
else: else:
data=json.loads(idx.read_text(encoding='utf-8')) data=json.loads(idx.read_text(encoding='utf-8'))
if len(data.get('files',[]))!=4 or data.get('all_match') is not True: fail('source index incomplete') indexed_files=data.get('files',[])
for item in data.get('files',[]): if len(indexed_files)<1 or data.get('all_match') is not True: fail('source index incomplete')
for item in indexed_files:
p=root/item['Relative_Path'] p=root/item['Relative_Path']
if not p.exists() or p.stat().st_size!=item['Size'] or sha(p)!=item['SHA256']: fail(f'source mismatch {item["File"]}') if not p.exists() or p.stat().st_size!=item['Size'] or sha(p)!=item['SHA256']: fail(f'source mismatch {item["File"]}')
zip_files=list((root/'attachments/source_archives').glob('*.zip')) zip_dir=root/'attachments/source_archives'
if len(zip_files)!=1: fail(f'Full source archive count {len(zip_files)} != 1') zip_files=list(zip_dir.glob('*.zip')) if zip_dir.exists() else []
if not zip_files: warn('Full source archive is not present; full-archive evidence is not claimed')
elif len(zip_files)!=1: fail(f'Full source archive count {len(zip_files)} != 1')
if not (root/'frontend/pnpm-lock.yaml').exists(): warn('pnpm-lock.yaml missing; frozen install cannot be claimed') if not (root/'frontend/pnpm-lock.yaml').exists(): warn('pnpm-lock.yaml missing; frozen install cannot be claimed')
warn('.NET 10 build, PostgreSQL DbUp, pnpm/Vitest/Playwright, scheduler chaos and 252-session Shadow require approved runtime') warn('.NET 10 build, PostgreSQL DbUp, pnpm/Vitest/Playwright, scheduler chaos and 252-session Shadow require approved runtime')
print(f'PASS={0 if errors else 1} WARN={len(warnings)} FAIL={len(errors)}') print(f'PASS={0 if errors else 1} WARN={len(warnings)} FAIL={len(errors)}')