diff --git a/codeflow_engine/__init__.py b/codeflow_engine/__init__.py index 0de48ba..8d9fb51 100644 --- a/codeflow_engine/__init__.py +++ b/codeflow_engine/__init__.py @@ -34,9 +34,16 @@ ) from codeflow_engine.integrations.base import Integration from codeflow_engine.quality.metrics_collector import MetricsCollector -from codeflow_engine.security.authorization.enterprise_manager import ( - EnterpriseAuthorizationManager, -) + +# Security - guarded import +EnterpriseAuthorizationManager: type[Any] | None = None +try: + from codeflow_engine.security.authorization.enterprise_manager import ( + EnterpriseAuthorizationManager, + ) +except (ImportError, OSError): + pass + from codeflow_engine.workflows.base import Workflow from codeflow_engine.workflows.engine import WorkflowEngine diff --git a/codeflow_engine/actions/__init__.py b/codeflow_engine/actions/__init__.py index 5b38a41..5178031 100644 --- a/codeflow_engine/actions/__init__.py +++ b/codeflow_engine/actions/__init__.py @@ -17,19 +17,66 @@ from codeflow_engine.actions.registry import ActionRegistry -# Import category modules -from codeflow_engine.actions import ( - ai_actions, - analysis, - base, - generation, - git, - issues, - maintenance, - platform, - quality, - scripts, -) +# Import category modules with error handling for optional dependencies +ai_actions = None +try: + from codeflow_engine.actions import ai_actions +except (ImportError, OSError): + pass + +analysis = None +try: + from codeflow_engine.actions import analysis +except (ImportError, OSError): + pass + +base = None +try: + from codeflow_engine.actions import base +except (ImportError, OSError): + pass + +generation = None +try: + from codeflow_engine.actions import generation +except (ImportError, OSError): + pass + +git = None +try: + from codeflow_engine.actions import git +except (ImportError, OSError): + pass + +issues = None +try: + from codeflow_engine.actions import issues +except (ImportError, OSError): + pass + +maintenance = None +try: + from codeflow_engine.actions import maintenance +except (ImportError, OSError): + pass + +platform = None +try: + from codeflow_engine.actions import platform +except (ImportError, OSError): + pass + +quality = None +try: + from codeflow_engine.actions import quality +except (ImportError, OSError): + pass + +scripts = None +try: + from codeflow_engine.actions import scripts +except (ImportError, OSError): + pass # Re-export commonly used actions for backward compatibility # Analysis diff --git a/codeflow_engine/actions/ai_actions/__init__.py b/codeflow_engine/actions/ai_actions/__init__.py index ac2d9cb..db90918 100644 --- a/codeflow_engine/actions/ai_actions/__init__.py +++ b/codeflow_engine/actions/ai_actions/__init__.py @@ -37,8 +37,18 @@ except ImportError: pass -# Submodule exports -from codeflow_engine.actions.ai_actions import autogen, llm +# Submodule exports with guarded imports +autogen = None +try: + from codeflow_engine.actions.ai_actions import autogen +except (ImportError, OSError): + pass + +llm = None +try: + from codeflow_engine.actions.ai_actions import llm +except (ImportError, OSError): + pass __all__ = [ "AutoGenAgentSystem", diff --git a/codeflow_engine/actions/issues/__init__.py b/codeflow_engine/actions/issues/__init__.py index 6371e92..213ea44 100644 --- a/codeflow_engine/actions/issues/__init__.py +++ b/codeflow_engine/actions/issues/__init__.py @@ -43,8 +43,15 @@ except ImportError: pass +FindStaleIssuesOrPRs: type[Any] | None = None +try: + from codeflow_engine.actions.issues.find_stale_issues_or_prs import FindStaleIssuesOrPRs +except ImportError: + pass + __all__ = [ "CreateOrUpdateIssue", + "FindStaleIssuesOrPRs", "IssueCreator", "LabelPR", "LabelPRBySize", diff --git a/codeflow_engine/models/base.py b/codeflow_engine/models/base.py index ece7c6e..6a06f65 100644 --- a/codeflow_engine/models/base.py +++ b/codeflow_engine/models/base.py @@ -6,7 +6,7 @@ from abc import ABC from dataclasses import dataclass, field -from datetime import datetime +from datetime import datetime, timezone from typing import Any @@ -15,7 +15,7 @@ class BaseModel(ABC): """Base class for all CodeFlow models with common functionality.""" id: str | None = None - created_at: datetime = field(default_factory=datetime.utcnow) + created_at: datetime = field(default_factory=lambda: datetime.now(timezone.utc)) updated_at: datetime | None = None def to_dict(self) -> dict[str, Any]: @@ -33,14 +33,18 @@ def to_dict(self) -> dict[str, Any]: @dataclass class TimestampMixin: - """Mixin for models that need timestamp tracking.""" + """Mixin for models that need timestamp tracking. + + Note: Defines created_at and updated_at fields (same as BaseModel). + Be cautious when using with BaseModel in multiple inheritance to avoid conflicts. + """ - created_at: datetime = field(default_factory=datetime.utcnow) + created_at: datetime = field(default_factory=lambda: datetime.now(timezone.utc)) updated_at: datetime | None = None def touch(self) -> None: """Update the updated_at timestamp.""" - self.updated_at = datetime.utcnow() + self.updated_at = datetime.now(timezone.utc) @dataclass diff --git a/codeflow_engine/models/config.py b/codeflow_engine/models/config.py index bf13d37..757f593 100644 --- a/codeflow_engine/models/config.py +++ b/codeflow_engine/models/config.py @@ -4,10 +4,11 @@ This module contains data models for configuration objects used in the CodeFlow system. """ -from dataclasses import dataclass, field from enum import StrEnum from typing import Any +from pydantic import BaseModel, Field, SecretStr, field_validator + class LogLevel(StrEnum): """Logging levels for the application.""" @@ -28,59 +29,79 @@ class Environment(StrEnum): TESTING = "testing" -@dataclass -class DatabaseConfig: - """Database configuration model.""" +class DatabaseConfig(BaseModel): + """Database configuration model with validation.""" url: str - pool_size: int = 5 - max_overflow: int = 10 + pool_size: int = Field(default=5, ge=0) + max_overflow: int = Field(default=10, ge=0) echo: bool = False ssl_required: bool = True + @field_validator("url") + @classmethod + def validate_url(cls, v: str) -> str: + """Validate database URL format.""" + if not v: + raise ValueError("Database URL cannot be empty") + # Basic URL validation - must contain :// + if "://" not in v: + raise ValueError( + "Invalid database URL format. Expected format: dialect://user:password@host:port/database" + ) + return v + -@dataclass -class RedisConfig: - """Redis configuration model.""" +class RedisConfig(BaseModel): + """Redis configuration model with validation.""" url: str - max_connections: int = 10 + max_connections: int = Field(default=10, ge=0) ssl: bool = True + @field_validator("url") + @classmethod + def validate_url(cls, v: str) -> str: + """Validate Redis URL format.""" + if not v: + raise ValueError("Redis URL cannot be empty") + # Basic URL validation - must contain :// + if "://" not in v: + raise ValueError( + "Invalid Redis URL format. Expected format: redis://host:port or rediss://host:port" + ) + return v + -@dataclass -class LLMConfig: - """LLM provider configuration model.""" +class LLMConfig(BaseModel): + """LLM provider configuration model with validation.""" provider: str - api_key: str | None = None + api_key: SecretStr | None = None model: str # Required; provider-specific (e.g., gpt-4, claude-3-opus, mistral-large) - temperature: float = 0.7 - max_tokens: int = 4096 + temperature: float = Field(default=0.7, ge=0.0, le=2.0) + max_tokens: int = Field(default=4096, gt=0) -@dataclass -class GitHubConfig: - """GitHub integration configuration model.""" +class GitHubConfig(BaseModel): + """GitHub integration configuration model with secrets protection.""" - token: str | None = None + token: SecretStr | None = None app_id: str | None = None - private_key: str | None = None - webhook_secret: str | None = None + private_key: SecretStr | None = None + webhook_secret: SecretStr | None = None -@dataclass -class WorkflowConfig: - """Workflow execution configuration model.""" +class WorkflowConfig(BaseModel): + """Workflow execution configuration model with validation.""" - max_concurrent: int = 10 - timeout_seconds: int = 300 - retry_attempts: int = 3 - retry_delay_seconds: int = 5 + max_concurrent: int = Field(default=10, gt=0) + timeout_seconds: int = Field(default=300, gt=0) + retry_attempts: int = Field(default=3, ge=0) + retry_delay_seconds: int = Field(default=5, gt=0) -@dataclass -class AppConfig: +class AppConfig(BaseModel): """Main application configuration model.""" environment: Environment = Environment.DEVELOPMENT @@ -90,8 +111,8 @@ class AppConfig: redis: RedisConfig | None = None llm: LLMConfig | None = None github: GitHubConfig | None = None - workflow: WorkflowConfig = field(default_factory=WorkflowConfig) - custom_settings: dict[str, Any] = field(default_factory=dict) + workflow: WorkflowConfig = Field(default_factory=WorkflowConfig) + custom_settings: dict[str, Any] = Field(default_factory=dict) __all__ = [ diff --git a/codeflow_engine/models/events.py b/codeflow_engine/models/events.py index c4144dd..ba31be1 100644 --- a/codeflow_engine/models/events.py +++ b/codeflow_engine/models/events.py @@ -5,7 +5,7 @@ """ from dataclasses import dataclass, field -from datetime import datetime +from datetime import datetime, timezone from enum import StrEnum from typing import Any @@ -133,7 +133,7 @@ class WebhookEvent: sender: User installation_id: int | None = None payload: dict[str, Any] = field(default_factory=dict) - timestamp: datetime = field(default_factory=datetime.utcnow) + timestamp: datetime = field(default_factory=lambda: datetime.now(timezone.utc)) # Optional specific event data pull_request: PullRequest | None = None