Skip to content

Commit 84ca250

Browse files
authored
Merge pull request #6 from dmartinol/ci
feat: CI workflow
2 parents e22ce6e + 92c5664 commit 84ca250

15 files changed

Lines changed: 635 additions & 361 deletions

File tree

.github/workflows/ci.yml

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
name: CI
2+
3+
on:
4+
pull_request:
5+
6+
jobs:
7+
validate:
8+
runs-on: ubuntu-latest
9+
10+
steps:
11+
- uses: actions/checkout@v4
12+
13+
- name: Install uv
14+
uses: astral-sh/setup-uv@v3
15+
with:
16+
version: "latest"
17+
18+
- name: Set up Python
19+
run: uv python install
20+
21+
- name: Install dependencies
22+
run: make install
23+
24+
- name: Run CI checks
25+
run: make ci

Makefile

Lines changed: 6 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -80,23 +80,17 @@ debug-test:
8080
# Pre-commit workflow
8181
pre-commit:
8282
@echo "Running pre-commit workflow..."
83-
@echo "1. Formatting code..."
84-
uv run --extra dev black mcp_validation/
85-
@echo "2. Fixing lint issues..."
86-
uv run --extra dev ruff check --fix mcp_validation/
87-
@echo "3. Running tests..."
88-
uv run --extra dev pytest tests/ -v
83+
@$(MAKE) format
84+
@$(MAKE) lint-fix
85+
@$(MAKE) test
8986
@echo "Pre-commit workflow completed successfully!"
9087

9188
# CI-like checks (no automatic fixes)
9289
ci:
9390
@echo "Running CI checks..."
94-
@echo "1. Checking code formatting..."
95-
uv run --extra dev black --check mcp_validation/
96-
@echo "2. Checking lint issues..."
97-
uv run --extra dev ruff check mcp_validation/
98-
@echo "3. Running tests..."
99-
uv run --extra dev pytest tests/ -v
91+
@$(MAKE) check
92+
@$(MAKE) lint
93+
@$(MAKE) test
10094
@echo "All CI checks passed!"
10195

10296
# Cleanup

mcp_validation/cli/main.py

Lines changed: 90 additions & 46 deletions
Original file line numberDiff line numberDiff line change
@@ -26,48 +26,61 @@ def detect_runtime_command(command_args: List[str]) -> Optional[str]:
2626
"""Auto-detect runtime command from MCP server command arguments."""
2727
if not command_args:
2828
return None
29-
29+
3030
first_command = command_args[0]
31-
31+
3232
# Direct runtime commands
3333
runtime_commands = {
34-
'uv', 'docker', 'podman', 'npx', 'node', 'python', 'python3',
35-
'pip', 'java', 'mvn', 'gradle', 'go', 'cargo', 'rust'
34+
"uv",
35+
"docker",
36+
"podman",
37+
"npx",
38+
"node",
39+
"python",
40+
"python3",
41+
"pip",
42+
"java",
43+
"mvn",
44+
"gradle",
45+
"go",
46+
"cargo",
47+
"rust",
3648
}
37-
49+
3850
if first_command in runtime_commands:
3951
return first_command
40-
52+
4153
# Check for common patterns
42-
if first_command.endswith('python') or first_command.endswith('python3'):
43-
return 'python3' if 'python3' in first_command else 'python'
44-
45-
if first_command.endswith('node'):
46-
return 'node'
47-
54+
if first_command.endswith("python") or first_command.endswith("python3"):
55+
return "python3" if "python3" in first_command else "python"
56+
57+
if first_command.endswith("node"):
58+
return "node"
59+
4860
# Check for shebang or script patterns
49-
if first_command.startswith('./') or first_command.endswith('.py'):
50-
return 'python3' # Assume Python for .py files
51-
52-
if first_command.endswith('.js') or first_command.endswith('.mjs'):
53-
return 'node'
54-
61+
if first_command.startswith("./") or first_command.endswith(".py"):
62+
return "python3" # Assume Python for .py files
63+
64+
if first_command.endswith(".js") or first_command.endswith(".mjs"):
65+
return "node"
66+
5567
# Check for container run patterns
56-
if len(command_args) >= 2 and first_command in ['docker', 'podman'] and command_args[1] == 'run':
68+
if (
69+
len(command_args) >= 2
70+
and first_command in ["docker", "podman"]
71+
and command_args[1] == "run"
72+
):
5773
return first_command
58-
74+
5975
return None
6076

6177

6278
def is_container_runtime_command(command_args: List[str]) -> bool:
6379
"""Check if command is a container runtime command (docker/podman run)."""
6480
if not command_args or len(command_args) < 3:
6581
return False
66-
67-
return (
68-
command_args[0] in ['docker', 'podman']
69-
and command_args[1] == 'run'
70-
)
82+
83+
return command_args[0] in ["docker", "podman"] and command_args[1] == "run"
7184

7285

7386
def create_argument_parser() -> argparse.ArgumentParser:
@@ -167,7 +180,9 @@ def create_argument_parser() -> argparse.ArgumentParser:
167180

168181
# Runtime validation options
169182
parser.add_argument(
170-
"--runtime-command", metavar="COMMAND", help="Runtime command to validate (e.g., uv, docker, npx). If not specified, will be auto-detected from the MCP server command."
183+
"--runtime-command",
184+
metavar="COMMAND",
185+
help="Runtime command to validate (e.g., uv, docker, npx). If not specified, will be auto-detected from the MCP server command.",
171186
)
172187

173188
return parser
@@ -227,19 +242,27 @@ async def main():
227242
# Add repo validators to profile if not already present
228243
if "repo_availability" not in active_profile.validators:
229244
from ..config.settings import ValidatorConfig
245+
230246
active_profile.validators["repo_availability"] = ValidatorConfig(
231-
enabled=True, required=True, timeout=30.0,
232-
parameters={"repo_url": args.repo_url, "clone_timeout": 30.0}
247+
enabled=True,
248+
required=True,
249+
timeout=30.0,
250+
parameters={"repo_url": args.repo_url, "clone_timeout": 30.0},
233251
)
234252
else:
235253
active_profile.validators["repo_availability"].enabled = True
236-
active_profile.validators["repo_availability"].parameters["repo_url"] = args.repo_url
254+
active_profile.validators["repo_availability"].parameters[
255+
"repo_url"
256+
] = args.repo_url
237257

238258
if "license" not in active_profile.validators:
239259
from ..config.settings import ValidatorConfig
260+
240261
active_profile.validators["license"] = ValidatorConfig(
241-
enabled=True, required=True, timeout=30.0,
242-
parameters={"repo_url": args.repo_url, "clone_timeout": 30.0}
262+
enabled=True,
263+
required=True,
264+
timeout=30.0,
265+
parameters={"repo_url": args.repo_url, "clone_timeout": 30.0},
243266
)
244267
else:
245268
active_profile.validators["license"].enabled = True
@@ -251,45 +274,63 @@ async def main():
251274
# Add runtime validators to profile if not already present
252275
if "runtime_exists" not in active_profile.validators:
253276
from ..config.settings import ValidatorConfig
277+
254278
active_profile.validators["runtime_exists"] = ValidatorConfig(
255-
enabled=True, required=True, timeout=10.0,
256-
parameters={"runtime_command": runtime_command}
279+
enabled=True,
280+
required=True,
281+
timeout=10.0,
282+
parameters={"runtime_command": runtime_command},
257283
)
258284
else:
259285
active_profile.validators["runtime_exists"].enabled = True
260-
active_profile.validators["runtime_exists"].parameters["runtime_command"] = runtime_command
286+
active_profile.validators["runtime_exists"].parameters[
287+
"runtime_command"
288+
] = runtime_command
261289

262290
if "runtime_executable" not in active_profile.validators:
263291
from ..config.settings import ValidatorConfig
292+
264293
active_profile.validators["runtime_executable"] = ValidatorConfig(
265-
enabled=True, required=True, timeout=10.0,
266-
parameters={"runtime_command": runtime_command, "execution_timeout": 10.0}
294+
enabled=True,
295+
required=True,
296+
timeout=10.0,
297+
parameters={"runtime_command": runtime_command, "execution_timeout": 10.0},
267298
)
268299
else:
269300
active_profile.validators["runtime_executable"].enabled = True
270-
active_profile.validators["runtime_executable"].parameters["runtime_command"] = runtime_command
301+
active_profile.validators["runtime_executable"].parameters[
302+
"runtime_command"
303+
] = runtime_command
271304

272305
# Enable container validators for container runtime commands
273306
if is_container_runtime_command(args.command):
274307
# Add container UBI validator
275308
if "container_ubi" not in active_profile.validators:
276309
from ..config.settings import ValidatorConfig
310+
277311
active_profile.validators["container_ubi"] = ValidatorConfig(
278-
enabled=True, required=False, timeout=60.0,
279-
parameters={"warn_only_for_non_ubi": True}
312+
enabled=True,
313+
required=False,
314+
timeout=60.0,
315+
parameters={"warn_only_for_non_ubi": True},
280316
)
281317
else:
282318
active_profile.validators["container_ubi"].enabled = True
283319
# Ensure warn_only is set to True by default
284-
if "warn_only_for_non_ubi" not in active_profile.validators["container_ubi"].parameters:
285-
active_profile.validators["container_ubi"].parameters["warn_only_for_non_ubi"] = True
286-
287-
# Add container version validator
320+
if (
321+
"warn_only_for_non_ubi"
322+
not in active_profile.validators["container_ubi"].parameters
323+
):
324+
active_profile.validators["container_ubi"].parameters[
325+
"warn_only_for_non_ubi"
326+
] = True
327+
328+
# Add container version validator
288329
if "container_version" not in active_profile.validators:
289330
from ..config.settings import ValidatorConfig
331+
290332
active_profile.validators["container_version"] = ValidatorConfig(
291-
enabled=True, required=False, timeout=30.0,
292-
parameters={}
333+
enabled=True, required=False, timeout=30.0, parameters={}
293334
)
294335
else:
295336
active_profile.validators["container_version"].enabled = True
@@ -320,7 +361,10 @@ async def main():
320361

321362
# Run validation
322363
session = await orchestrator.validate_server(
323-
command_args=args.command, env_vars=env_vars, profile_name=args.profile, debug=args.debug
364+
command_args=args.command,
365+
env_vars=env_vars,
366+
profile_name=args.profile,
367+
debug=args.debug,
324368
)
325369

326370
# Display results

mcp_validation/core/result.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -35,6 +35,7 @@ class ValidatorResult:
3535
data: Dict[str, Any]
3636
execution_time: float
3737

38+
3839
@dataclass
3940
class ValidationSession:
4041
"""Complete validation session result."""

0 commit comments

Comments
 (0)