domd 0.1.0__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- domd/__init__.py +8 -0
- domd/cli.py +231 -0
- domd/detector.py +902 -0
- domd/parsers/__init__.py +1 -0
- domd/parsers/base.py +4 -0
- domd/parsers/build_systems.py +4 -0
- domd/parsers/ci_cd.py +4 -0
- domd/parsers/docker.py +4 -0
- domd/parsers/javascript.py +4 -0
- domd/parsers/python.py +4 -0
- domd/parsers/test_build_systems.py +3 -0
- domd/parsers/test_ci_cd.py +3 -0
- domd/parsers/test_docker.py +3 -0
- domd/parsers/test_javascript.py +3 -0
- domd/parsers/test_python.py +3 -0
- domd/reporters/__init__.py +1 -0
- domd/reporters/console.py +4 -0
- domd/reporters/json_reporter.py +4 -0
- domd/reporters/test_console.py +3 -0
- domd/reporters/test_json_reporter.py +3 -0
- domd/reporters/test_todo_md.py +3 -0
- domd/reporters/todo_md.py +4 -0
- domd/utils/__init__.py +1 -0
- domd/utils/command_runner.py +4 -0
- domd/utils/file_utils.py +4 -0
- domd/utils/test_command_runner.py +3 -0
- domd/utils/test_file_utils.py +3 -0
- domd-0.1.0.dist-info/LICENSE +201 -0
- domd-0.1.0.dist-info/METADATA +418 -0
- domd-0.1.0.dist-info/RECORD +32 -0
- domd-0.1.0.dist-info/WHEEL +4 -0
- domd-0.1.0.dist-info/entry_points.txt +5 -0
domd/__init__.py
ADDED
domd/cli.py
ADDED
|
@@ -0,0 +1,231 @@
|
|
|
1
|
+
#!/usr/bin/env python3
|
|
2
|
+
"""
|
|
3
|
+
Command Line Interface for TodoMD
|
|
4
|
+
"""
|
|
5
|
+
|
|
6
|
+
import argparse
|
|
7
|
+
import sys
|
|
8
|
+
from pathlib import Path
|
|
9
|
+
from typing import Optional
|
|
10
|
+
|
|
11
|
+
from . import __version__
|
|
12
|
+
from .detector import ProjectCommandDetector
|
|
13
|
+
|
|
14
|
+
|
|
15
|
+
def create_parser() -> argparse.ArgumentParser:
|
|
16
|
+
"""Create and configure argument parser."""
|
|
17
|
+
parser = argparse.ArgumentParser(
|
|
18
|
+
prog='domd',
|
|
19
|
+
description='Project Command Detector - Automatically detects and tests project commands',
|
|
20
|
+
epilog='Examples:\n'
|
|
21
|
+
' domd # Scan current directory\n'
|
|
22
|
+
' domd --path /path/to/project # Scan specific project\n'
|
|
23
|
+
' domd --dry-run # Preview commands without executing\n'
|
|
24
|
+
' domd --output custom.md # Custom output file',
|
|
25
|
+
formatter_class=argparse.RawDescriptionHelpFormatter
|
|
26
|
+
)
|
|
27
|
+
|
|
28
|
+
parser.add_argument(
|
|
29
|
+
'--version',
|
|
30
|
+
action='version',
|
|
31
|
+
version=f'%(prog)s {__version__}'
|
|
32
|
+
)
|
|
33
|
+
|
|
34
|
+
parser.add_argument(
|
|
35
|
+
'--path', '-p',
|
|
36
|
+
type=str,
|
|
37
|
+
default='.',
|
|
38
|
+
help='Path to project directory (default: current directory)'
|
|
39
|
+
)
|
|
40
|
+
|
|
41
|
+
parser.add_argument(
|
|
42
|
+
'--dry-run', '-d',
|
|
43
|
+
action='store_true',
|
|
44
|
+
help='Only detect commands without executing them'
|
|
45
|
+
)
|
|
46
|
+
|
|
47
|
+
parser.add_argument(
|
|
48
|
+
'--verbose', '-v',
|
|
49
|
+
action='store_true',
|
|
50
|
+
help='Enable verbose output'
|
|
51
|
+
)
|
|
52
|
+
|
|
53
|
+
parser.add_argument(
|
|
54
|
+
'--quiet', '-q',
|
|
55
|
+
action='store_true',
|
|
56
|
+
help='Suppress all output except errors'
|
|
57
|
+
)
|
|
58
|
+
|
|
59
|
+
parser.add_argument(
|
|
60
|
+
'--output', '-o',
|
|
61
|
+
type=str,
|
|
62
|
+
default='TODO.md',
|
|
63
|
+
help='Output file for failed commands (default: TODO.md)'
|
|
64
|
+
)
|
|
65
|
+
|
|
66
|
+
parser.add_argument(
|
|
67
|
+
'--format',
|
|
68
|
+
choices=['markdown', 'json', 'text'],
|
|
69
|
+
default='markdown',
|
|
70
|
+
help='Output format (default: markdown)'
|
|
71
|
+
)
|
|
72
|
+
|
|
73
|
+
parser.add_argument(
|
|
74
|
+
'--timeout',
|
|
75
|
+
type=int,
|
|
76
|
+
default=60,
|
|
77
|
+
help='Command timeout in seconds (default: 60)'
|
|
78
|
+
)
|
|
79
|
+
|
|
80
|
+
parser.add_argument(
|
|
81
|
+
'--exclude',
|
|
82
|
+
action='append',
|
|
83
|
+
help='Exclude specific file patterns (can be used multiple times)'
|
|
84
|
+
)
|
|
85
|
+
|
|
86
|
+
parser.add_argument(
|
|
87
|
+
'--include-only',
|
|
88
|
+
action='append',
|
|
89
|
+
help='Include only specific file patterns (can be used multiple times)'
|
|
90
|
+
)
|
|
91
|
+
|
|
92
|
+
return parser
|
|
93
|
+
|
|
94
|
+
|
|
95
|
+
def validate_args(args: argparse.Namespace) -> Optional[str]:
|
|
96
|
+
"""Validate command line arguments."""
|
|
97
|
+
# Check if project path exists
|
|
98
|
+
project_path = Path(args.path)
|
|
99
|
+
if not project_path.exists():
|
|
100
|
+
return f"Project path does not exist: {project_path}"
|
|
101
|
+
|
|
102
|
+
if not project_path.is_dir():
|
|
103
|
+
return f"Project path is not a directory: {project_path}"
|
|
104
|
+
|
|
105
|
+
# Check timeout value
|
|
106
|
+
if args.timeout <= 0:
|
|
107
|
+
return "Timeout must be a positive integer"
|
|
108
|
+
|
|
109
|
+
# Check if verbose and quiet are both specified
|
|
110
|
+
if args.verbose and args.quiet:
|
|
111
|
+
return "Cannot specify both --verbose and --quiet"
|
|
112
|
+
|
|
113
|
+
return None
|
|
114
|
+
|
|
115
|
+
|
|
116
|
+
def setup_logging(verbose: bool, quiet: bool) -> None:
|
|
117
|
+
"""Setup logging based on verbosity level."""
|
|
118
|
+
import logging
|
|
119
|
+
|
|
120
|
+
if quiet:
|
|
121
|
+
level = logging.ERROR
|
|
122
|
+
elif verbose:
|
|
123
|
+
level = logging.DEBUG
|
|
124
|
+
else:
|
|
125
|
+
level = logging.INFO
|
|
126
|
+
|
|
127
|
+
logging.basicConfig(
|
|
128
|
+
level=level,
|
|
129
|
+
format='%(asctime)s - %(name)s - %(levelname)s - %(message)s'
|
|
130
|
+
)
|
|
131
|
+
|
|
132
|
+
|
|
133
|
+
def print_summary(detector: ProjectCommandDetector, total_commands: int) -> None:
|
|
134
|
+
"""Print execution summary."""
|
|
135
|
+
failed = len(detector.failed_commands)
|
|
136
|
+
success = total_commands - failed
|
|
137
|
+
|
|
138
|
+
print(f"\n{'=' * 50}")
|
|
139
|
+
print("EXECUTION SUMMARY")
|
|
140
|
+
print(f"{'=' * 50}")
|
|
141
|
+
print(f"ā
Successful: {success}/{total_commands}")
|
|
142
|
+
print(f"ā Failed: {failed}/{total_commands}")
|
|
143
|
+
|
|
144
|
+
if failed > 0:
|
|
145
|
+
success_rate = (success / total_commands) * 100
|
|
146
|
+
print(f"š Success rate: {success_rate:.1f}%")
|
|
147
|
+
print(f"š Check output file for failed command details")
|
|
148
|
+
else:
|
|
149
|
+
print(f"š All commands executed successfully!")
|
|
150
|
+
|
|
151
|
+
|
|
152
|
+
def main() -> int:
|
|
153
|
+
"""Main entry point for the CLI."""
|
|
154
|
+
parser = create_parser()
|
|
155
|
+
args = parser.parse_args()
|
|
156
|
+
|
|
157
|
+
# Validate arguments
|
|
158
|
+
error_msg = validate_args(args)
|
|
159
|
+
if error_msg:
|
|
160
|
+
print(f"Error: {error_msg}", file=sys.stderr)
|
|
161
|
+
return 1
|
|
162
|
+
|
|
163
|
+
# Setup logging
|
|
164
|
+
setup_logging(args.verbose, args.quiet)
|
|
165
|
+
|
|
166
|
+
try:
|
|
167
|
+
# Initialize detector
|
|
168
|
+
detector = ProjectCommandDetector(
|
|
169
|
+
project_path=args.path,
|
|
170
|
+
timeout=args.timeout,
|
|
171
|
+
exclude_patterns=args.exclude or [],
|
|
172
|
+
include_patterns=args.include_only or []
|
|
173
|
+
)
|
|
174
|
+
|
|
175
|
+
if not args.quiet:
|
|
176
|
+
print(f"TodoMD v{__version__}")
|
|
177
|
+
print(f"Scanning project: {Path(args.path).resolve()}")
|
|
178
|
+
|
|
179
|
+
# Scan project for commands
|
|
180
|
+
commands = detector.scan_project()
|
|
181
|
+
|
|
182
|
+
if not commands:
|
|
183
|
+
if not args.quiet:
|
|
184
|
+
print("No commands found to test.")
|
|
185
|
+
return 0
|
|
186
|
+
|
|
187
|
+
if not args.quiet:
|
|
188
|
+
print(f"Found {len(commands)} commands to test")
|
|
189
|
+
|
|
190
|
+
# Handle dry-run mode
|
|
191
|
+
if args.dry_run:
|
|
192
|
+
if not args.quiet:
|
|
193
|
+
print("\nš DRY RUN MODE - Commands found:")
|
|
194
|
+
for i, cmd in enumerate(commands, 1):
|
|
195
|
+
print(f"{i:3d}. {cmd['description']}")
|
|
196
|
+
print(f" Command: {cmd['command']}")
|
|
197
|
+
print(f" Source: {cmd['source']}")
|
|
198
|
+
if args.verbose:
|
|
199
|
+
print(f" Type: {cmd.get('type', 'unknown')}")
|
|
200
|
+
print()
|
|
201
|
+
return 0
|
|
202
|
+
|
|
203
|
+
# Execute commands
|
|
204
|
+
if not args.quiet:
|
|
205
|
+
print(f"\nTesting commands...")
|
|
206
|
+
|
|
207
|
+
detector.test_commands(commands)
|
|
208
|
+
|
|
209
|
+
# Generate output file
|
|
210
|
+
detector.generate_output_file(args.output, args.format)
|
|
211
|
+
|
|
212
|
+
# Print summary
|
|
213
|
+
if not args.quiet:
|
|
214
|
+
print_summary(detector, len(commands))
|
|
215
|
+
|
|
216
|
+
# Return exit code based on results
|
|
217
|
+
return 1 if detector.failed_commands else 0
|
|
218
|
+
|
|
219
|
+
except KeyboardInterrupt:
|
|
220
|
+
print("\nā ļø Operation cancelled by user", file=sys.stderr)
|
|
221
|
+
return 130
|
|
222
|
+
except Exception as e:
|
|
223
|
+
print(f"ā Unexpected error: {e}", file=sys.stderr)
|
|
224
|
+
if args.verbose:
|
|
225
|
+
import traceback
|
|
226
|
+
traceback.print_exc()
|
|
227
|
+
return 1
|
|
228
|
+
|
|
229
|
+
|
|
230
|
+
if __name__ == "__main__":
|
|
231
|
+
sys.exit(main())
|