Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 8 additions & 0 deletions cforge/commands/deploy/builder/__init__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
# -*- coding: utf-8 -*-
"""Location: ./cforge/commands/deploy/builder/__init__.py
Copyright 2025
SPDX-License-Identifier: Apache-2.0
Authors: Teryl Taylor

Builder Package.
"""
1,266 changes: 1,266 additions & 0 deletions cforge/commands/deploy/builder/common.py

Large diffs are not rendered by default.

554 changes: 554 additions & 0 deletions cforge/commands/deploy/builder/dagger_deploy.py

Large diffs are not rendered by default.

141 changes: 141 additions & 0 deletions cforge/commands/deploy/builder/factory.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,141 @@
# -*- coding: utf-8 -*-
"""Location: ./mcpgateway/tools/builder/factory.py
Copyright 2025
SPDX-License-Identifier: Apache-2.0
Authors: Teryl Taylor

Factory for creating MCP Stack deployment implementations.

This module provides a factory pattern for creating the appropriate deployment
implementation (Dagger or Plain Python) based on availability and user preference.

The factory handles graceful fallback from Dagger to Python if dependencies are
unavailable, ensuring the deployment system works in various environments.

Example:
>>> deployer, mode = DeployFactory.create_deployer("dagger", verbose=False)
⚠ Dagger not installed. Using plain python.
>>> # Validate configuration (output varies by config)
>>> # deployer.validate("mcp-stack.yaml")
"""

# Standard
from enum import Enum

# First-Party
from cforge.commands.deploy.builder.pipeline import CICDModule
from cforge.common import get_console


class CICDTypes(str, Enum):
"""Deployment implementation types.

Attributes:
DAGGER: Dagger-based implementation (optimal performance)
PYTHON: Plain Python implementation (fallback, no dependencies)

Examples:
>>> # Test enum values
>>> CICDTypes.DAGGER.value
'dagger'
>>> CICDTypes.PYTHON.value
'python'

>>> # Test enum comparison
>>> CICDTypes.DAGGER == "dagger"
True
>>> CICDTypes.PYTHON == "python"
True

>>> # Test enum membership
>>> "dagger" in [t.value for t in CICDTypes]
True
>>> "python" in [t.value for t in CICDTypes]
True

>>> # Test enum iteration
>>> types = list(CICDTypes)
>>> len(types)
2
>>> CICDTypes.DAGGER in types
True
"""

DAGGER = "dagger"
PYTHON = "python"


class DeployFactory:
"""Factory for creating MCP Stack deployment implementations.

This factory implements the Strategy pattern, allowing dynamic selection
between Dagger and Python implementations based on availability.
"""

@staticmethod
def create_deployer(deployer: str, verbose: bool = False) -> tuple[CICDModule, CICDTypes]:
"""Create a deployment implementation instance.

Attempts to load the requested deployer type with automatic fallback
to Python implementation if dependencies are missing.

Args:
deployer: Deployment type to create ("dagger" or "python")
verbose: Enable verbose logging during creation

Returns:
tuple: (deployer_instance, actual_type)
- deployer_instance: Instance of MCPStackDagger or MCPStackPython
- actual_type: CICDTypes enum indicating which implementation was loaded

Raises:
RuntimeError: If no implementation can be loaded (critical failure)

Example:
>>> # Try to load Dagger, fall back to Python if unavailable
>>> deployer, mode = DeployFactory.create_deployer("dagger", verbose=False)
⚠ Dagger not installed. Using plain python.
>>> if mode == CICDTypes.DAGGER:
... print("Using optimized Dagger implementation")
... else:
... print("Using fallback Python implementation")
Using fallback Python implementation
"""
# Attempt to load Dagger implementation first if requested
if deployer == "dagger":
try:
# First-Party
from cforge.commands.deploy.builder.dagger_deploy import DAGGER_AVAILABLE, MCPStackDagger

# Check if dagger is actually available (not just the module)
if not DAGGER_AVAILABLE:
raise ImportError("Dagger SDK not installed")

if verbose:
get_console().print("[green]✓ Dagger module loaded[/green]")

return (MCPStackDagger(verbose), CICDTypes.DAGGER)

except ImportError:
# Dagger dependencies not available, fall back to Python
get_console().print("[yellow]⚠ Dagger not installed. Using plain python.[/yellow]")

# Load plain Python implementation (fallback or explicitly requested)
try:
# First-Party
from cforge.commands.deploy.builder.python_deploy import MCPStackPython

if verbose and deployer != "dagger":
get_console().print("[blue]Using plain Python implementation[/blue]")

return (MCPStackPython(verbose), CICDTypes.PYTHON)

except ImportError as e:
# Critical failure - neither implementation can be loaded
get_console().print("[red]✗ ERROR: Cannot import deployment modules[/red]")
get_console().print(f"[red] Details: {e}[/red]")
get_console().print("[yellow] Make sure you're running from the project root[/yellow]")
get_console().print("[yellow] and PYTHONPATH is set correctly[/yellow]")

# This should never be reached if PYTHONPATH is set correctly
raise RuntimeError(f"Unable to load deployer of type '{deployer}'. ")
Loading