Skip to content

Commit c36aa5c

Browse files
committed
feat: Initial port of cforge gateway -> cforge deploy command from upstream
Reference: IBM/mcp-context-forge#1207 Branch: PortDeployAndPlugins Signed-off-by: Gabe Goodhart <ghart@us.ibm.com>
1 parent 97ddcb1 commit c36aa5c

19 files changed

Lines changed: 4506 additions & 13 deletions
Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
# -*- coding: utf-8 -*-
2+
"""Location: ./cforge/commands/deploy/builder/__init__.py
3+
Copyright 2025
4+
SPDX-License-Identifier: Apache-2.0
5+
Authors: Teryl Taylor
6+
7+
Builder Package.
8+
"""

cforge/commands/deploy/builder/common.py

Lines changed: 1268 additions & 0 deletions
Large diffs are not rendered by default.

cforge/commands/deploy/builder/dagger_deploy.py

Lines changed: 557 additions & 0 deletions
Large diffs are not rendered by default.
Lines changed: 146 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,146 @@
1+
# -*- coding: utf-8 -*-
2+
"""Location: ./mcpgateway/tools/builder/factory.py
3+
Copyright 2025
4+
SPDX-License-Identifier: Apache-2.0
5+
Authors: Teryl Taylor
6+
7+
Factory for creating MCP Stack deployment implementations.
8+
9+
This module provides a factory pattern for creating the appropriate deployment
10+
implementation (Dagger or Plain Python) based on availability and user preference.
11+
12+
The factory handles graceful fallback from Dagger to Python if dependencies are
13+
unavailable, ensuring the deployment system works in various environments.
14+
15+
Example:
16+
>>> deployer, mode = DeployFactory.create_deployer("dagger", verbose=False)
17+
⚠ Dagger not installed. Using plain python.
18+
>>> # Validate configuration (output varies by config)
19+
>>> # deployer.validate("mcp-stack.yaml")
20+
"""
21+
22+
# Standard
23+
from enum import Enum
24+
25+
# Third-Party
26+
from rich.console import Console
27+
28+
# First-Party
29+
from cforge.commands.deploy.builder.pipeline import CICDModule
30+
31+
32+
class CICDTypes(str, Enum):
33+
"""Deployment implementation types.
34+
35+
Attributes:
36+
DAGGER: Dagger-based implementation (optimal performance)
37+
PYTHON: Plain Python implementation (fallback, no dependencies)
38+
39+
Examples:
40+
>>> # Test enum values
41+
>>> CICDTypes.DAGGER.value
42+
'dagger'
43+
>>> CICDTypes.PYTHON.value
44+
'python'
45+
46+
>>> # Test enum comparison
47+
>>> CICDTypes.DAGGER == "dagger"
48+
True
49+
>>> CICDTypes.PYTHON == "python"
50+
True
51+
52+
>>> # Test enum membership
53+
>>> "dagger" in [t.value for t in CICDTypes]
54+
True
55+
>>> "python" in [t.value for t in CICDTypes]
56+
True
57+
58+
>>> # Test enum iteration
59+
>>> types = list(CICDTypes)
60+
>>> len(types)
61+
2
62+
>>> CICDTypes.DAGGER in types
63+
True
64+
"""
65+
66+
DAGGER = "dagger"
67+
PYTHON = "python"
68+
69+
70+
console = Console()
71+
72+
73+
class DeployFactory:
74+
"""Factory for creating MCP Stack deployment implementations.
75+
76+
This factory implements the Strategy pattern, allowing dynamic selection
77+
between Dagger and Python implementations based on availability.
78+
"""
79+
80+
@staticmethod
81+
def create_deployer(deployer: str, verbose: bool = False) -> tuple[CICDModule, CICDTypes]:
82+
"""Create a deployment implementation instance.
83+
84+
Attempts to load the requested deployer type with automatic fallback
85+
to Python implementation if dependencies are missing.
86+
87+
Args:
88+
deployer: Deployment type to create ("dagger" or "python")
89+
verbose: Enable verbose logging during creation
90+
91+
Returns:
92+
tuple: (deployer_instance, actual_type)
93+
- deployer_instance: Instance of MCPStackDagger or MCPStackPython
94+
- actual_type: CICDTypes enum indicating which implementation was loaded
95+
96+
Raises:
97+
RuntimeError: If no implementation can be loaded (critical failure)
98+
99+
Example:
100+
>>> # Try to load Dagger, fall back to Python if unavailable
101+
>>> deployer, mode = DeployFactory.create_deployer("dagger", verbose=False)
102+
⚠ Dagger not installed. Using plain python.
103+
>>> if mode == CICDTypes.DAGGER:
104+
... print("Using optimized Dagger implementation")
105+
... else:
106+
... print("Using fallback Python implementation")
107+
Using fallback Python implementation
108+
"""
109+
# Attempt to load Dagger implementation first if requested
110+
if deployer == "dagger":
111+
try:
112+
# First-Party
113+
from cforge.commands.deploy.builder.dagger_deploy import DAGGER_AVAILABLE, MCPStackDagger
114+
115+
# Check if dagger is actually available (not just the module)
116+
if not DAGGER_AVAILABLE:
117+
raise ImportError("Dagger SDK not installed")
118+
119+
if verbose:
120+
console.print("[green]✓ Dagger module loaded[/green]")
121+
122+
return (MCPStackDagger(verbose), CICDTypes.DAGGER)
123+
124+
except ImportError:
125+
# Dagger dependencies not available, fall back to Python
126+
console.print("[yellow]⚠ Dagger not installed. Using plain python.[/yellow]")
127+
128+
# Load plain Python implementation (fallback or explicitly requested)
129+
try:
130+
# First-Party
131+
from cforge.commands.deploy.builder.python_deploy import MCPStackPython
132+
133+
if verbose and deployer != "dagger":
134+
console.print("[blue]Using plain Python implementation[/blue]")
135+
136+
return (MCPStackPython(verbose), CICDTypes.PYTHON)
137+
138+
except ImportError as e:
139+
# Critical failure - neither implementation can be loaded
140+
console.print("[red]✗ ERROR: Cannot import deployment modules[/red]")
141+
console.print(f"[red] Details: {e}[/red]")
142+
console.print("[yellow] Make sure you're running from the project root[/yellow]")
143+
console.print("[yellow] and PYTHONPATH is set correctly[/yellow]")
144+
145+
# This should never be reached if PYTHONPATH is set correctly
146+
raise RuntimeError(f"Unable to load deployer of type '{deployer}'. ")

0 commit comments

Comments
 (0)