From 619feff5f47a3d5837932da8d58ec3dbc82dd408 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Sat, 12 Sep 2026 13:38:48 +0000 Subject: [PATCH] fix(ops): address provenance gate review findings Co-authored-by: genichin <4282227+genichin@users.noreply.github.com> --- docs/operations.md | 1 + scripts/release_provenance_gate.py | 17 ++++++++++++++--- trex_core/adapters/persistence/ops_checks.py | 5 +++-- 3 files changed, 18 insertions(+), 5 deletions(-) diff --git a/docs/operations.md b/docs/operations.md index 6c71e59..92633c5 100644 --- a/docs/operations.md +++ b/docs/operations.md @@ -65,6 +65,7 @@ python scripts/release_provenance_gate.py \ - `--image-digest`와 `--runtime-file` 검증 결과(sha256 + revision 일치 여부)가 JSON record에 저장된다. - actor/timestamp/instance/command-class/reason이 JSON record에 함께 저장되어 stop/start/recreate 감사 추적이 가능하다. - PRE/POST 모두 실행해 재기동 전후의 `pending_slots`, `reconciliation_slots`, `queue_consumers`, broker-side-effect 로그(`order_submit/fill/reject/cancel`)를 자동 캡처한다. +- broker-side-effect 로그 입력은 기본 `/tmp/trex-core.log`를 사용하며, 로그 파일 위치가 다르면 `--broker-log-path `로 명시한다. - 이 스크립트는 read-only capture 전용이다. 주문 제출·취소, DB 복구, 자동 sync/verify live 실행을 암묵적으로 수행하지 않는다. 재기동 후에는 동일 명령(예: `--command-class "manual-start"`)으로 POST record를 추가 저장한다. diff --git a/scripts/release_provenance_gate.py b/scripts/release_provenance_gate.py index e11a68b..b4b7a6b 100644 --- a/scripts/release_provenance_gate.py +++ b/scripts/release_provenance_gate.py @@ -72,7 +72,16 @@ def main(argv: list[str] | None = None) -> int: output_dir.mkdir(parents=True, exist_ok=True) output_path = _record_path(output_dir, instance=args.instance, command_class=args.command_class) - if report["checkout"]["dirty_paths"] and not args.allow_dirty: + dirty_checkout = bool(report["checkout"]["dirty_paths"]) + runtime_ok = all( + item.get("matches_expected_revision", False) for item in report["runtime_file_verification"] + ) + oci_ok = bool(report["artifact_identity"]["oci_revision_matches_head"]) + allow_dirty_passed = bool(args.allow_dirty and dirty_checkout and runtime_ok and oci_ok) + report["gate"]["allow_dirty"] = bool(args.allow_dirty) + report["gate"]["allow_dirty_passed"] = allow_dirty_passed + + if dirty_checkout and not args.allow_dirty: report["dirty_checkout_isolation"] = isolate_dirty_checkout_files( checkout_dir=args.checkout_dir, isolate_dir=args.isolate_dir, @@ -80,9 +89,11 @@ def main(argv: list[str] | None = None) -> int: output_path.write_text(json.dumps(report, ensure_ascii=False, indent=2) + "\n", encoding="utf-8") print(str(output_path)) - if report["checkout"]["dirty_paths"] and not args.allow_dirty: + if dirty_checkout and not args.allow_dirty: return 2 - return 0 if report["gate"]["passed"] else 1 + if report["gate"]["passed"] or allow_dirty_passed: + return 0 + return 1 if __name__ == "__main__": diff --git a/trex_core/adapters/persistence/ops_checks.py b/trex_core/adapters/persistence/ops_checks.py index 156486d..de5cdff 100644 --- a/trex_core/adapters/persistence/ops_checks.py +++ b/trex_core/adapters/persistence/ops_checks.py @@ -98,9 +98,10 @@ def _sha256_bytes(payload: bytes) -> str: def _resolve_under(root: Path, relpath: str) -> Path | None: - candidate = (root / relpath).resolve() + resolved_root = root.resolve() + candidate = (resolved_root / relpath).resolve() try: - candidate.relative_to(root) + candidate.relative_to(resolved_root) return candidate except ValueError: return None