chore: snapshot current project updates
This commit is contained in:
+72
-5
@@ -18,7 +18,9 @@ from .config import Config
|
||||
from .logging_config import get_logger, log_operation
|
||||
from .models import ExecutionPlan, FileOperation, OperationResult, RollbackLog
|
||||
from .quarantine import QuarantineManager
|
||||
from .utils import ensure_utc, utc_now
|
||||
from .state import StateManager
|
||||
from .transaction import TransactionLog
|
||||
from .utils import ensure_utc, is_within_root, utc_now
|
||||
|
||||
|
||||
class ExecutionEngine:
|
||||
@@ -28,15 +30,21 @@ class ExecutionEngine:
|
||||
self,
|
||||
logger: Optional[logging.Logger] = None,
|
||||
config: Optional[Config] = None,
|
||||
verbose_operations: bool = False,
|
||||
state_manager: Optional[StateManager] = None,
|
||||
):
|
||||
"""Initialize the execution engine.
|
||||
|
||||
Args:
|
||||
logger: Optional logger instance (uses default if not provided)
|
||||
config: Optional config (required for quarantine operations)
|
||||
verbose_operations: Emit per-operation dry-run logs at INFO when True
|
||||
state_manager: Optional state manager for updating file statuses
|
||||
"""
|
||||
self.logger = logger or get_logger()
|
||||
self.config = config
|
||||
self.verbose_operations = verbose_operations
|
||||
self.state_manager = state_manager
|
||||
self._quarantine_manager: Optional[QuarantineManager] = (
|
||||
QuarantineManager(config, self.logger) if config else None
|
||||
)
|
||||
@@ -80,12 +88,47 @@ class ExecutionEngine:
|
||||
f"Starting execution in {mode} mode with {len(plan.operations)} operations",
|
||||
operation_type="execute"
|
||||
)
|
||||
|
||||
# Initialize transaction log for execute mode
|
||||
transaction_log = None
|
||||
if mode == "execute":
|
||||
log_path = Path.home() / ".vlm" / "transaction.json"
|
||||
transaction_log = TransactionLog(log_path)
|
||||
transaction_log.start_transaction(plan)
|
||||
|
||||
# Execute all operations
|
||||
results = []
|
||||
for operation in plan.operations:
|
||||
for i, operation in enumerate(plan.operations):
|
||||
result = self.execute_operation(operation, mode)
|
||||
results.append(result)
|
||||
|
||||
# Update transaction and state logs in execute mode
|
||||
if mode == "execute":
|
||||
if transaction_log:
|
||||
transaction_log.mark_operation_complete(
|
||||
i, result.success, result.error_message
|
||||
)
|
||||
|
||||
# Update file state if successful and not a no-op
|
||||
if result.success and operation.operation_type != "no-op" and self.state_manager:
|
||||
new_status = "quarantined" if operation.operation_type == "quarantine" else "executed"
|
||||
self.state_manager.set_file_state(
|
||||
operation.source_path,
|
||||
status=new_status,
|
||||
reason=operation.reason
|
||||
)
|
||||
# We could save state incrementally, but saving at the end is more efficient.
|
||||
# For extra safety, we'll save every 10 operations.
|
||||
if (i + 1) % 10 == 0:
|
||||
self.state_manager.save()
|
||||
|
||||
# Finalize transaction and state
|
||||
if mode == "execute":
|
||||
if transaction_log:
|
||||
status = "completed" if all(r.success for r in results) else "failed"
|
||||
transaction_log.complete_transaction(status=status)
|
||||
if self.state_manager:
|
||||
self.state_manager.save()
|
||||
|
||||
# Generate execution summary
|
||||
summary = self._generate_execution_summary(results)
|
||||
@@ -156,9 +199,10 @@ class ExecutionEngine:
|
||||
executed_at=executed_at
|
||||
)
|
||||
if mode == "dry-run":
|
||||
level = logging.INFO if self.verbose_operations else logging.DEBUG
|
||||
log_operation(
|
||||
self.logger,
|
||||
logging.INFO,
|
||||
level,
|
||||
f"[DRY-RUN] Would quarantine: {operation.source_path} ({operation.reason})",
|
||||
operation_type="execute",
|
||||
file_path=operation.source_path
|
||||
@@ -219,7 +263,7 @@ class ExecutionEngine:
|
||||
)
|
||||
log_operation(
|
||||
self.logger,
|
||||
logging.INFO,
|
||||
logging.INFO if self.verbose_operations else logging.DEBUG,
|
||||
msg,
|
||||
operation_type="execute",
|
||||
file_path=operation.source_path
|
||||
@@ -266,6 +310,25 @@ class ExecutionEngine:
|
||||
|
||||
# Create destination directory if needed
|
||||
if operation.destination_path:
|
||||
if self.config and not is_within_root(
|
||||
operation.destination_path, self.config.library_root
|
||||
):
|
||||
error_msg = (
|
||||
f"Unsafe destination outside library root: {operation.destination_path}"
|
||||
)
|
||||
log_operation(
|
||||
self.logger,
|
||||
logging.ERROR,
|
||||
error_msg,
|
||||
operation_type="execute",
|
||||
file_path=operation.source_path
|
||||
)
|
||||
return OperationResult(
|
||||
operation=operation,
|
||||
success=False,
|
||||
error_message=error_msg,
|
||||
executed_at=executed_at
|
||||
)
|
||||
operation.destination_path.parent.mkdir(parents=True, exist_ok=True)
|
||||
|
||||
# Perform the move/rename operation
|
||||
@@ -316,7 +379,11 @@ class ExecutionEngine:
|
||||
Dictionary with counts of successful, failed, and skipped operations
|
||||
"""
|
||||
successful = sum(1 for r in results if r.success)
|
||||
failed = sum(1 for r in results if not r.success)
|
||||
failed = sum(
|
||||
1
|
||||
for r in results
|
||||
if (not r.success) and (not r.operation.has_conflict)
|
||||
)
|
||||
skipped = sum(
|
||||
1 for r in results
|
||||
if r.operation.operation_type == "no-op" or r.operation.has_conflict
|
||||
|
||||
Reference in New Issue
Block a user