Skip to content

Commit 3eeb4ee

Browse files
authored
Merge pull request #3 from jomardyan/codex/enhance-repository-functionality-pwi6q8
Add dry-run execution plan support to runner CLI
2 parents 179cf3e + 329a394 commit 3eeb4ee

File tree

3 files changed

+90
-6
lines changed

3 files changed

+90
-6
lines changed

runner.py

Lines changed: 43 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6278,6 +6278,7 @@ def __init__(self, script_path: str, script_args: Optional[List[str]] = None,
62786278
self.max_output_lines = None
62796279
self.hooks = ExecutionHook()
62806280
self.monitor_interval = 0.1
6281+
self.config_file = config_file
62816282

62826283
# UPDATED: Phase 2 retry config (replaces old retry_count and retry_delay)
62836284
self.retry_config = RetryConfig() # Default configuration
@@ -6291,8 +6292,10 @@ def __init__(self, script_path: str, script_args: Optional[List[str]] = None,
62916292
# NEW: Phase 2 features
62926293
self.enable_history = enable_history
62936294
self.history_manager = None
6295+
self.history_db_path = None
62946296
if enable_history:
62956297
db_path = history_db or 'script_runner_history.db'
6298+
self.history_db_path = db_path
62966299
self.history_manager = HistoryManager(db_path=db_path)
62976300

62986301
# NEW: Trend Analysis (Phase 2)
@@ -6497,6 +6500,30 @@ def validate_script(self) -> bool:
64976500
self.logger.warning(f"Script does not have .py extension: {self.script_path}")
64986501
return True
64996502

6503+
def get_execution_plan(self) -> Dict[str, Any]:
6504+
"""Return a structured view of how the script will be executed.
6505+
6506+
This helper is used by the CLI ``--dry-run`` flag to show what the
6507+
runner would do without actually launching the subprocess. It surfaces
6508+
key configuration such as the script path, arguments, timeouts, logging
6509+
level, configuration file, and history database state.
6510+
6511+
Returns:
6512+
Dict[str, Any]: Execution summary including paths and toggles.
6513+
"""
6514+
return {
6515+
'script_path': os.path.abspath(self.script_path),
6516+
'script_args': list(self.script_args),
6517+
'timeout': self.timeout,
6518+
'log_level': logging.getLevelName(self.logger.level),
6519+
'config_file': os.path.abspath(self.config_file) if self.config_file else None,
6520+
'history_enabled': self.enable_history,
6521+
'history_db': os.path.abspath(self.history_db_path) if self.history_db_path else None,
6522+
'monitor_interval': self.monitor_interval,
6523+
'retry_strategy': self.retry_config.strategy,
6524+
'max_attempts': self.retry_config.max_attempts,
6525+
}
6526+
65006527
def run_script(self, retry_on_failure: bool = False) -> Dict:
65016528
"""Execute script with advanced retry and monitoring capabilities.
65026529
@@ -7148,8 +7175,10 @@ def main():
71487175
parser.add_argument('script', nargs='?', help='Python script to execute')
71497176
parser.add_argument('script_args', nargs='*', help='Arguments to pass to the script')
71507177
parser.add_argument('--timeout', type=int, default=None, help='Execution timeout in seconds')
7151-
parser.add_argument('--log-level', choices=['DEBUG', 'INFO', 'WARNING', 'ERROR'],
7178+
parser.add_argument('--log-level', choices=['DEBUG', 'INFO', 'WARNING', 'ERROR'],
71527179
default='INFO', help='Logging level')
7180+
parser.add_argument('--dry-run', action='store_true',
7181+
help='Validate the script and show execution plan without running it')
71537182
parser.add_argument('--config', help='Configuration file (YAML)')
71547183
parser.add_argument('--monitor-interval', type=float, default=0.1,
71557184
help='Process monitor sampling interval (seconds)')
@@ -8479,6 +8508,19 @@ def main():
84798508
enable_history=not args.disable_history
84808509
)
84818510

8511+
if args.dry_run:
8512+
try:
8513+
runner.validate_script()
8514+
except Exception as exc:
8515+
logging.error(f"Dry-run validation failed: {exc}")
8516+
return 1
8517+
8518+
plan = runner.get_execution_plan()
8519+
print("\nDRY-RUN: Execution plan (no script executed)")
8520+
for key, value in plan.items():
8521+
print(f" {key}: {value}")
8522+
return 0
8523+
84828524
runner.monitor_interval = args.monitor_interval
84838525
runner.suppress_warnings = args.suppress_warnings
84848526

tests/test_integration.py

Lines changed: 21 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@
1515
import tempfile
1616
import json
1717
import time
18+
import subprocess
1819
from pathlib import Path
1920
from unittest.mock import Mock, patch
2021

@@ -66,16 +67,33 @@ def test_history_database_creation(self, tmp_path):
6667
"""Test that history database is properly created"""
6768
script_file = tmp_path / "test_db.py"
6869
script_file.write_text("print('test'); exit(0)")
69-
70+
7071
db_file = tmp_path / "test.db"
7172
runner = ScriptRunner(str(script_file), enable_history=True)
72-
73+
7374
result = runner.run_script()
74-
75+
7576
# Check if metrics are collected
7677
assert 'metrics' in result
7778
assert len(result['metrics']) > 0
7879

80+
def test_cli_dry_run_shows_execution_plan(self, tmp_path):
81+
"""Ensure CLI dry-run validates script and prints plan without running it."""
82+
script_file = tmp_path / "dry_run_target.py"
83+
script_file.write_text("print('should not run during dry-run')")
84+
85+
result = subprocess.run(
86+
[sys.executable, "-m", "runner", str(script_file), "--dry-run", "--timeout", "3"],
87+
capture_output=True,
88+
text=True,
89+
check=False,
90+
)
91+
92+
assert result.returncode == 0
93+
assert "DRY-RUN: Execution plan" in result.stdout
94+
assert "dry_run_target.py" in result.stdout
95+
assert "timeout: 3" in result.stdout
96+
7997

8098
@pytest.mark.integration
8199
class TestAlertIntegration:

tests/test_runner_core.py

Lines changed: 26 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -60,11 +60,35 @@ def test_runner_with_history(self, tmp_path):
6060
script_file = tmp_path / "test.py"
6161
script_file.write_text("print('hello')")
6262
db_file = tmp_path / "history.db"
63-
63+
6464
runner = ScriptRunner(str(script_file), enable_history=True, history_db=str(db_file))
65-
65+
6666
assert runner.enable_history is True
6767

68+
def test_execution_plan_summary(self, tmp_path):
69+
"""Ensure execution plan surfaces key configuration without running script."""
70+
script_file = tmp_path / "plan.py"
71+
script_file.write_text("print('dry run')")
72+
db_file = tmp_path / "history.db"
73+
74+
runner = ScriptRunner(
75+
str(script_file),
76+
script_args=["--flag", "value"],
77+
timeout=5,
78+
history_db=str(db_file),
79+
enable_history=True,
80+
log_level="DEBUG",
81+
)
82+
83+
plan = runner.get_execution_plan()
84+
85+
assert plan["script_path"].endswith("plan.py")
86+
assert plan["script_args"] == ["--flag", "value"]
87+
assert plan["timeout"] == 5
88+
assert plan["history_enabled"] is True
89+
assert plan["history_db"].endswith("history.db")
90+
assert plan["log_level"] == "DEBUG"
91+
6892

6993
@pytest.mark.unit
7094
class TestScriptExecution:

0 commit comments

Comments
 (0)