152 lines
4.5 KiB
Python
152 lines
4.5 KiB
Python
"""Data models for the migration pipeline."""
|
|
|
|
from __future__ import annotations
|
|
|
|
from dataclasses import dataclass, field
|
|
from enum import Enum
|
|
from typing import Optional
|
|
|
|
|
|
# ---------------------------------------------------------------------------
|
|
# Enumerations
|
|
# ---------------------------------------------------------------------------
|
|
|
|
class Track(str, Enum):
|
|
"""Migration track."""
|
|
A = "track_a_conservative" # Qt5→Qt6 compatibility only
|
|
B = "track_b_modern" # Qt6 + C++20 modernisation
|
|
|
|
|
|
class FileStatus(str, Enum):
|
|
PENDING = "pending"
|
|
IN_PROGRESS = "in_progress"
|
|
SUCCESS = "success"
|
|
QUARANTINED = "quarantined"
|
|
|
|
|
|
class FailureClass(str, Enum):
|
|
COMPILE_ERROR = "compile_error"
|
|
LINK_ERROR = "link_error"
|
|
TEST_FAILURE = "test_failure"
|
|
TEST_BUILD_FAILURE = "test_build_failure"
|
|
BUDGET_EXCEEDED = "budget_exceeded"
|
|
API_ERROR = "api_error"
|
|
CHUNK_MERGE_ERROR = "chunk_merge_error"
|
|
UNKNOWN = "unknown"
|
|
|
|
|
|
# ---------------------------------------------------------------------------
|
|
# Chunk
|
|
# ---------------------------------------------------------------------------
|
|
|
|
@dataclass
|
|
class CodeChunk:
|
|
"""A contiguous slice of a C++ source file."""
|
|
index: int
|
|
start_line: int # 0-based, inclusive
|
|
end_line: int # 0-based, exclusive
|
|
content: str
|
|
boundary_type: str # "complete" | "semantic" | "fallback"
|
|
|
|
@property
|
|
def line_count(self) -> int:
|
|
return self.end_line - self.start_line
|
|
|
|
|
|
# ---------------------------------------------------------------------------
|
|
# Quality gates
|
|
# ---------------------------------------------------------------------------
|
|
|
|
@dataclass
|
|
class QualityGateResult:
|
|
"""Result of all quality gates for one migration attempt."""
|
|
build_ok: bool = False
|
|
build_errors: str = ""
|
|
build_warnings: list[str] = field(default_factory=list)
|
|
|
|
existing_tests_ok: bool = False
|
|
new_tests_ok: bool = False
|
|
test_output: str = ""
|
|
test_warnings: list[str] = field(default_factory=list)
|
|
|
|
@property
|
|
def all_gates_passed(self) -> bool:
|
|
"""Hard gates: build green, all tests green.
|
|
Warnings are advisory and do NOT block (config.WARNINGS_ARE_BLOCKING=False)."""
|
|
from migration_pipeline.config import WARNINGS_ARE_BLOCKING
|
|
if WARNINGS_ARE_BLOCKING:
|
|
return (
|
|
self.build_ok
|
|
and self.existing_tests_ok
|
|
and self.new_tests_ok
|
|
and not self.build_warnings
|
|
and not self.test_warnings
|
|
)
|
|
return self.build_ok and self.existing_tests_ok and self.new_tests_ok
|
|
|
|
@property
|
|
def total_warnings(self) -> int:
|
|
return len(self.build_warnings) + len(self.test_warnings)
|
|
|
|
|
|
# ---------------------------------------------------------------------------
|
|
# Quarantine
|
|
# ---------------------------------------------------------------------------
|
|
|
|
@dataclass
|
|
class QuarantineEntry:
|
|
"""Rich metadata for a file that could not be migrated automatically."""
|
|
file_path: str
|
|
track: str
|
|
failure_class: str
|
|
last_error: str # truncated at 2000 chars
|
|
affected_apis: list[str]
|
|
chunk_info: str
|
|
manual_recommendation: str
|
|
timestamp: str # ISO-8601 UTC
|
|
retry_count: int
|
|
elapsed_seconds: float
|
|
|
|
|
|
# ---------------------------------------------------------------------------
|
|
# Per-file result
|
|
# ---------------------------------------------------------------------------
|
|
|
|
@dataclass
|
|
class FileResult:
|
|
file_path: str
|
|
track: str
|
|
status: str # FileStatus value
|
|
iterations: int
|
|
elapsed_seconds: float
|
|
gate: QualityGateResult
|
|
quarantine_entry: Optional[QuarantineEntry] = None
|
|
migrated_path: str = ""
|
|
test_name: str = ""
|
|
|
|
|
|
# ---------------------------------------------------------------------------
|
|
# Batch result
|
|
# ---------------------------------------------------------------------------
|
|
|
|
@dataclass
|
|
class BatchResult:
|
|
"""Aggregated result for one batch + one track."""
|
|
batch_id: str
|
|
track: str
|
|
branch: str
|
|
total_files: int
|
|
success_count: int = 0
|
|
quarantine_count: int = 0
|
|
total_warnings: int = 0
|
|
mean_elapsed_seconds: float = 0.0
|
|
results: list[FileResult] = field(default_factory=list)
|
|
|
|
@property
|
|
def success_rate(self) -> float:
|
|
return self.success_count / self.total_files if self.total_files else 0.0
|
|
|
|
@property
|
|
def quarantine_rate(self) -> float:
|
|
return self.quarantine_count / self.total_files if self.total_files else 0.0
|