tripwire-py 0.2.0__tar.gz

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.
Files changed (60) hide show
  1. tripwire_py-0.2.0/.github/ISSUE_TEMPLATE/bug_report.md +50 -0
  2. tripwire_py-0.2.0/.github/ISSUE_TEMPLATE/feature_request.md +41 -0
  3. tripwire_py-0.2.0/.github/pull_request_template.md +75 -0
  4. tripwire_py-0.2.0/.github/scripts/validate_cli.sh +460 -0
  5. tripwire_py-0.2.0/.github/workflows/ci.yml +267 -0
  6. tripwire_py-0.2.0/.github/workflows/release.yml +613 -0
  7. tripwire_py-0.2.0/.github/workflows/security.yml +180 -0
  8. tripwire_py-0.2.0/.github/workflows/status.yml +24 -0
  9. tripwire_py-0.2.0/.gitignore +14 -0
  10. tripwire_py-0.2.0/.pre-commit-config.yaml +48 -0
  11. tripwire_py-0.2.0/LICENSE +21 -0
  12. tripwire_py-0.2.0/Makefile +141 -0
  13. tripwire_py-0.2.0/PKG-INFO +1113 -0
  14. tripwire_py-0.2.0/README.md +1053 -0
  15. tripwire_py-0.2.0/bandit.yaml +25 -0
  16. tripwire_py-0.2.0/benchmarks/README.md +124 -0
  17. tripwire_py-0.2.0/benchmarks/performance.py +370 -0
  18. tripwire_py-0.2.0/benchmarks/results.txt +27 -0
  19. tripwire_py-0.2.0/docs/EnvSync.md +1050 -0
  20. tripwire_py-0.2.0/docs/audit.md +829 -0
  21. tripwire_py-0.2.0/examples/README.md +232 -0
  22. tripwire_py-0.2.0/examples/advanced_usage.py +107 -0
  23. tripwire_py-0.2.0/examples/audit_example.txt +299 -0
  24. tripwire_py-0.2.0/examples/basic_usage.py +198 -0
  25. tripwire_py-0.2.0/examples/cli_workflow.md +261 -0
  26. tripwire_py-0.2.0/examples/custom_validators.py +121 -0
  27. tripwire_py-0.2.0/examples/quickstart.py +100 -0
  28. tripwire_py-0.2.0/pyproject.toml +175 -0
  29. tripwire_py-0.2.0/scripts/release.py +244 -0
  30. tripwire_py-0.2.0/scripts/setup-dev.py +267 -0
  31. tripwire_py-0.2.0/scripts/test_package.sh +90 -0
  32. tripwire_py-0.2.0/src/tripwire/__init__.py +45 -0
  33. tripwire_py-0.2.0/src/tripwire/branding.py +111 -0
  34. tripwire_py-0.2.0/src/tripwire/cli.py +1453 -0
  35. tripwire_py-0.2.0/src/tripwire/config.py +322 -0
  36. tripwire_py-0.2.0/src/tripwire/core.py +356 -0
  37. tripwire_py-0.2.0/src/tripwire/exceptions.py +220 -0
  38. tripwire_py-0.2.0/src/tripwire/git_audit.py +644 -0
  39. tripwire_py-0.2.0/src/tripwire/parser.py +447 -0
  40. tripwire_py-0.2.0/src/tripwire/py.typed +0 -0
  41. tripwire_py-0.2.0/src/tripwire/scanner.py +358 -0
  42. tripwire_py-0.2.0/src/tripwire/secrets.py +962 -0
  43. tripwire_py-0.2.0/src/tripwire/validation.py +562 -0
  44. tripwire_py-0.2.0/tests/__init__.py +1 -0
  45. tripwire_py-0.2.0/tests/conftest.py +95 -0
  46. tripwire_py-0.2.0/tests/test_audit_auto_detection.py +255 -0
  47. tripwire_py-0.2.0/tests/test_cli_validation.py +687 -0
  48. tripwire_py-0.2.0/tests/test_coercion.py +288 -0
  49. tripwire_py-0.2.0/tests/test_config.py +382 -0
  50. tripwire_py-0.2.0/tests/test_core.py +236 -0
  51. tripwire_py-0.2.0/tests/test_git_audit.py +779 -0
  52. tripwire_py-0.2.0/tests/test_integration.py +412 -0
  53. tripwire_py-0.2.0/tests/test_interpolation.py +300 -0
  54. tripwire_py-0.2.0/tests/test_parser.py +377 -0
  55. tripwire_py-0.2.0/tests/test_scanner.py +433 -0
  56. tripwire_py-0.2.0/tests/test_secrets.py +799 -0
  57. tripwire_py-0.2.0/tests/test_validation.py +369 -0
  58. tripwire_py-0.2.0/tests/test_validation_concurrency.py +414 -0
  59. tripwire_py-0.2.0/tests/test_validator_plugins.py +416 -0
  60. tripwire_py-0.2.0/uv.lock +1687 -0
@@ -0,0 +1,50 @@
1
+ ---
2
+ name: Bug report
3
+ about: Create a report to help us improve TripWire
4
+ title: '[BUG] '
5
+ labels: bug
6
+ assignees: ''
7
+ ---
8
+
9
+ ## Bug Description
10
+ A clear and concise description of what the bug is.
11
+
12
+ ## To Reproduce
13
+ Steps to reproduce the behavior:
14
+ 1. Go to '...'
15
+ 2. Run command '...'
16
+ 3. See error
17
+
18
+ ## Expected Behavior
19
+ A clear and concise description of what you expected to happen.
20
+
21
+ ## Actual Behavior
22
+ A clear and concise description of what actually happened.
23
+
24
+ ## Environment
25
+ - OS: [e.g. Ubuntu 20.04, macOS 12.0, Windows 10]
26
+ - Python version: [e.g. 3.9.7]
27
+ - TripWire version: [e.g. 0.1.0]
28
+ - Installation method: [e.g. pip, conda, from source]
29
+
30
+ ## Code Example
31
+ ```python
32
+ # Minimal code example that reproduces the issue
33
+ from tripwire import env
34
+
35
+ # Your code here
36
+ ```
37
+
38
+ ## Error Message
39
+ ```
40
+ # Paste the full error message here
41
+ ```
42
+
43
+ ## Additional Context
44
+ Add any other context about the problem here.
45
+
46
+ ## Checklist
47
+ - [ ] I have searched existing issues to avoid duplicates
48
+ - [ ] I have provided a minimal code example
49
+ - [ ] I have included the full error message
50
+ - [ ] I have specified my environment details
@@ -0,0 +1,41 @@
1
+ ---
2
+ name: Feature request
3
+ about: Suggest an idea for TripWire
4
+ title: '[FEATURE] '
5
+ labels: enhancement
6
+ assignees: ''
7
+ ---
8
+
9
+ ## Feature Description
10
+ A clear and concise description of the feature you'd like to see.
11
+
12
+ ## Problem Statement
13
+ What problem does this feature solve? What pain point does it address?
14
+
15
+ ## Proposed Solution
16
+ A clear and concise description of what you want to happen.
17
+
18
+ ## Alternative Solutions
19
+ A clear and concise description of any alternative solutions or features you've considered.
20
+
21
+ ## Use Case
22
+ Describe a specific use case where this feature would be helpful:
23
+
24
+ ```python
25
+ # Example of how you'd like to use this feature
26
+ from tripwire import env
27
+
28
+ # Your desired usage
29
+ ```
30
+
31
+ ## Additional Context
32
+ Add any other context, mockups, or examples about the feature request here.
33
+
34
+ ## Implementation Ideas
35
+ If you have ideas about how this could be implemented, please share them.
36
+
37
+ ## Checklist
38
+ - [ ] I have searched existing issues to avoid duplicates
39
+ - [ ] I have provided a clear problem statement
40
+ - [ ] I have described the proposed solution
41
+ - [ ] I have included a use case example
@@ -0,0 +1,75 @@
1
+ ## Description
2
+ Brief description of the changes in this PR.
3
+
4
+ ## Type of Change
5
+ - [ ] Bug fix (non-breaking change which fixes an issue)
6
+ - [ ] New feature (non-breaking change which adds functionality)
7
+ - [ ] Breaking change (fix or feature that would cause existing functionality to not work as expected)
8
+ - [ ] Documentation update
9
+ - [ ] Performance improvement
10
+ - [ ] Code refactoring
11
+ - [ ] Test improvements
12
+
13
+ ## Related Issues
14
+ Fixes #(issue number)
15
+ Closes #(issue number)
16
+ Related to #(issue number)
17
+
18
+ ## Changes Made
19
+ - [ ] List the main changes made
20
+ - [ ] Include any new dependencies
21
+ - [ ] Note any configuration changes
22
+
23
+ ## Testing
24
+ - [ ] Tests pass locally
25
+ - [ ] New tests added for new functionality
26
+ - [ ] Existing tests updated if needed
27
+ - [ ] Integration tests pass
28
+ - [ ] Manual testing completed
29
+
30
+ ## Code Quality
31
+ - [ ] Code follows project style guidelines
32
+ - [ ] Self-review completed
33
+ - [ ] Code is properly commented
34
+ - [ ] No hardcoded values or magic numbers
35
+ - [ ] Error handling is appropriate
36
+
37
+ ## Security
38
+ - [ ] No sensitive data exposed
39
+ - [ ] Input validation added where needed
40
+ - [ ] Security implications considered
41
+ - [ ] No new security vulnerabilities introduced
42
+
43
+ ## Documentation
44
+ - [ ] README updated if needed
45
+ - [ ] API documentation updated
46
+ - [ ] Code comments added for complex logic
47
+ - [ ] Changelog updated (if applicable)
48
+
49
+ ## Performance
50
+ - [ ] No performance regressions
51
+ - [ ] Performance improvements measured
52
+ - [ ] Memory usage considered
53
+ - [ ] Database queries optimized (if applicable)
54
+
55
+ ## Breaking Changes
56
+ If this is a breaking change, describe:
57
+ - What breaks
58
+ - How to migrate
59
+ - Timeline for deprecation
60
+
61
+ ## Screenshots/Examples
62
+ If applicable, add screenshots or code examples to help explain your changes.
63
+
64
+ ## Checklist
65
+ - [ ] My code follows the style guidelines of this project
66
+ - [ ] I have performed a self-review of my own code
67
+ - [ ] I have commented my code, particularly in hard-to-understand areas
68
+ - [ ] I have made corresponding changes to the documentation
69
+ - [ ] My changes generate no new warnings
70
+ - [ ] I have added tests that prove my fix is effective or that my feature works
71
+ - [ ] New and existing unit tests pass locally with my changes
72
+ - [ ] Any dependent changes have been merged and published
73
+
74
+ ## Additional Notes
75
+ Any additional information that reviewers should know.
@@ -0,0 +1,460 @@
1
+ #!/usr/bin/env bash
2
+ # Validates CLI installation in CI/CD workflows
3
+ # Uses behavior-based testing, not exact string matching
4
+ #
5
+ # Usage:
6
+ # ./validate_cli.sh [expected_version]
7
+ #
8
+ # Exit codes:
9
+ # 0 - All validations passed
10
+ # 1 - One or more validations failed
11
+
12
+ set -euo pipefail
13
+
14
+ # Color output for better readability
15
+ RED='\033[0;31m'
16
+ GREEN='\033[0;32m'
17
+ YELLOW='\033[1;33m'
18
+ CYAN='\033[0;36m'
19
+ NC='\033[0m' # No Color
20
+
21
+ # Test result tracking
22
+ TESTS_PASSED=0
23
+ TESTS_FAILED=0
24
+
25
+ # Collect failures for summary
26
+ declare -a FAILED_TESTS
27
+
28
+ #######################################
29
+ # Helper Functions
30
+ #######################################
31
+
32
+ log_success() {
33
+ echo -e "${GREEN}✓${NC} $1"
34
+ ((TESTS_PASSED++))
35
+ }
36
+
37
+ log_error() {
38
+ echo -e "${RED}✗${NC} $1"
39
+ ((TESTS_FAILED++))
40
+ FAILED_TESTS+=("$1")
41
+ }
42
+
43
+ log_info() {
44
+ echo -e "${CYAN}→${NC} $1"
45
+ }
46
+
47
+ log_section() {
48
+ echo ""
49
+ echo -e "${YELLOW}===${NC} $1"
50
+ }
51
+
52
+ #######################################
53
+ # Validation Tests
54
+ #######################################
55
+
56
+ test_command_exists() {
57
+ log_info "Testing: tripwire command is available in PATH"
58
+
59
+ if command -v tripwire &> /dev/null; then
60
+ log_success "tripwire command found in PATH"
61
+ return 0
62
+ else
63
+ log_error "tripwire command not found in PATH"
64
+ return 1
65
+ fi
66
+ }
67
+
68
+ test_basic_execution() {
69
+ log_info "Testing: Basic command execution (--help)"
70
+
71
+ if tripwire --help > /dev/null 2>&1; then
72
+ log_success "tripwire --help executes successfully"
73
+ return 0
74
+ else
75
+ log_error "tripwire --help failed to execute"
76
+ return 1
77
+ fi
78
+ }
79
+
80
+ test_version_flag() {
81
+ local expected_version="$1"
82
+ log_info "Testing: Version flag (expecting: $expected_version)"
83
+
84
+ # Capture version output
85
+ local version_output
86
+ if ! version_output=$(tripwire --version 2>&1); then
87
+ log_error "tripwire --version failed to execute"
88
+ return 1
89
+ fi
90
+
91
+ # Extract version with flexible regex (handles various formats)
92
+ if [[ $version_output =~ ([0-9]+\.[0-9]+\.[0-9]+(-[a-zA-Z0-9.]+)?) ]]; then
93
+ local actual_version="${BASH_REMATCH[1]}"
94
+
95
+ if [ "$actual_version" == "$expected_version" ]; then
96
+ log_success "Version matches: $actual_version"
97
+ return 0
98
+ else
99
+ log_error "Version mismatch: got '$actual_version', expected '$expected_version'"
100
+ return 1
101
+ fi
102
+ else
103
+ log_error "Could not extract version from output: $version_output"
104
+ return 1
105
+ fi
106
+ }
107
+
108
+ test_help_structure() {
109
+ log_info "Testing: Help output structure (not exact text)"
110
+
111
+ local help_output
112
+ if ! help_output=$(tripwire --help 2>&1); then
113
+ log_error "Failed to get help output"
114
+ return 1
115
+ fi
116
+
117
+ # Test structural elements exist (case-insensitive)
118
+ local output_lower
119
+ output_lower=$(echo "$help_output" | tr '[:upper:]' '[:lower:]')
120
+
121
+ local has_usage=false
122
+ local has_options=false
123
+ local has_program_name=false
124
+
125
+ [[ $output_lower =~ "usage:" ]] && has_usage=true
126
+ [[ $output_lower =~ "options:" ]] && has_options=true
127
+ [[ $output_lower =~ "tripwire" ]] && has_program_name=true
128
+
129
+ if $has_usage && $has_options && $has_program_name; then
130
+ log_success "Help output has expected structure (usage, options, program name)"
131
+ return 0
132
+ else
133
+ log_error "Help output missing expected structure"
134
+ [[ $has_usage == false ]] && echo " Missing: Usage section"
135
+ [[ $has_options == false ]] && echo " Missing: Options section"
136
+ [[ $has_program_name == false ]] && echo " Missing: Program name"
137
+ return 1
138
+ fi
139
+ }
140
+
141
+ test_help_not_empty() {
142
+ log_info "Testing: Help output is substantial (not empty)"
143
+
144
+ local help_output
145
+ help_output=$(tripwire --help 2>&1)
146
+
147
+ local length=${#help_output}
148
+
149
+ if [ "$length" -gt 100 ]; then
150
+ log_success "Help output is substantial ($length characters)"
151
+ return 0
152
+ else
153
+ log_error "Help output too short ($length characters)"
154
+ return 1
155
+ fi
156
+ }
157
+
158
+ test_commands_available() {
159
+ log_info "Testing: All expected commands execute without crashing"
160
+
161
+ local commands=("init" "generate" "check" "sync" "scan" "audit" "validate" "docs")
162
+ local all_passed=true
163
+
164
+ for cmd in "${commands[@]}"; do
165
+ if tripwire "$cmd" --help > /dev/null 2>&1; then
166
+ # Don't log individual successes to reduce noise
167
+ :
168
+ else
169
+ log_error "Command failed: $cmd --help"
170
+ all_passed=false
171
+ fi
172
+ done
173
+
174
+ if $all_passed; then
175
+ log_success "All ${#commands[@]} commands available and executable"
176
+ return 0
177
+ else
178
+ return 1
179
+ fi
180
+ }
181
+
182
+ test_python_imports() {
183
+ log_info "Testing: Python imports work correctly"
184
+
185
+ # Run Python import test
186
+ if python3 - <<'EOF'
187
+ import sys
188
+
189
+ try:
190
+ # Test basic import
191
+ import tripwire
192
+
193
+ # Test core components
194
+ from tripwire import env, TripWire
195
+
196
+ # Test exceptions
197
+ from tripwire import (
198
+ TripWireError,
199
+ MissingVariableError,
200
+ ValidationError,
201
+ )
202
+
203
+ # Test validator
204
+ from tripwire import validator
205
+
206
+ # All imports successful
207
+ sys.exit(0)
208
+
209
+ except ImportError as e:
210
+ print(f"Import failed: {e}", file=sys.stderr)
211
+ sys.exit(1)
212
+ EOF
213
+ then
214
+ log_success "All Python imports work correctly"
215
+ return 0
216
+ else
217
+ log_error "Python imports failed"
218
+ return 1
219
+ fi
220
+ }
221
+
222
+ test_version_consistency() {
223
+ log_info "Testing: CLI version matches package version"
224
+
225
+ # Get CLI version
226
+ local cli_output
227
+ cli_output=$(tripwire --version 2>&1)
228
+
229
+ if [[ $cli_output =~ ([0-9]+\.[0-9]+\.[0-9]+(-[a-zA-Z0-9.]+)?) ]]; then
230
+ local cli_version="${BASH_REMATCH[1]}"
231
+
232
+ # Get package version
233
+ local pkg_version
234
+ pkg_version=$(python3 -c "import tripwire; print(tripwire.__version__)" 2>&1)
235
+
236
+ if [ "$cli_version" == "$pkg_version" ]; then
237
+ log_success "CLI version matches package version: $cli_version"
238
+ return 0
239
+ else
240
+ log_error "Version mismatch: CLI=$cli_version, Package=$pkg_version"
241
+ return 1
242
+ fi
243
+ else
244
+ log_error "Could not extract CLI version"
245
+ return 1
246
+ fi
247
+ }
248
+
249
+ test_functional_workflow() {
250
+ log_info "Testing: Functional workflow (init creates files)"
251
+
252
+ # Create temporary directory
253
+ local test_dir
254
+ test_dir=$(mktemp -d)
255
+
256
+ # Run test in isolated environment
257
+ (
258
+ cd "$test_dir" || exit 1
259
+
260
+ # Test init command
261
+ if ! tripwire init --project-type=cli > /dev/null 2>&1; then
262
+ echo "init command failed" >&2
263
+ exit 1
264
+ fi
265
+
266
+ # Check files were created
267
+ if [ ! -f ".env" ]; then
268
+ echo ".env not created" >&2
269
+ exit 1
270
+ fi
271
+
272
+ if [ ! -f ".env.example" ]; then
273
+ echo ".env.example not created" >&2
274
+ exit 1
275
+ fi
276
+
277
+ # Check files have content
278
+ if [ ! -s ".env" ]; then
279
+ echo ".env is empty" >&2
280
+ exit 1
281
+ fi
282
+
283
+ if [ ! -s ".env.example" ]; then
284
+ echo ".env.example is empty" >&2
285
+ exit 1
286
+ fi
287
+
288
+ exit 0
289
+ )
290
+
291
+ local result=$?
292
+
293
+ # Cleanup
294
+ rm -rf "$test_dir"
295
+
296
+ if [ $result -eq 0 ]; then
297
+ log_success "init command creates expected files with content"
298
+ return 0
299
+ else
300
+ log_error "Functional workflow test failed"
301
+ return 1
302
+ fi
303
+ }
304
+
305
+ test_json_output_valid() {
306
+ log_info "Testing: Commands with --json produce valid JSON"
307
+
308
+ local test_dir
309
+ test_dir=$(mktemp -d)
310
+
311
+ (
312
+ cd "$test_dir" || exit 1
313
+
314
+ # Create test files
315
+ echo "VAR1=value1" > .env
316
+ echo "VAR1=" > .env.example
317
+ echo "VAR2=" >> .env.example
318
+
319
+ # Test check --json
320
+ local json_output
321
+ json_output=$(tripwire check --json 2>&1)
322
+
323
+ # Validate JSON with Python
324
+ python3 - <<EOF
325
+ import sys
326
+ import json
327
+
328
+ try:
329
+ data = json.loads('''$json_output''')
330
+ if not isinstance(data, dict):
331
+ print("JSON output is not a dictionary", file=sys.stderr)
332
+ sys.exit(1)
333
+ sys.exit(0)
334
+ except json.JSONDecodeError as e:
335
+ print(f"Invalid JSON: {e}", file=sys.stderr)
336
+ sys.exit(1)
337
+ EOF
338
+ )
339
+
340
+ local result=$?
341
+ rm -rf "$test_dir"
342
+
343
+ if [ $result -eq 0 ]; then
344
+ log_success "Commands produce valid JSON output"
345
+ return 0
346
+ else
347
+ log_error "JSON output validation failed"
348
+ return 1
349
+ fi
350
+ }
351
+
352
+ test_error_handling() {
353
+ log_info "Testing: CLI handles errors gracefully (no crashes)"
354
+
355
+ local all_passed=true
356
+
357
+ # Test 1: Invalid command
358
+ if tripwire invalid_command_xyz > /dev/null 2>&1; then
359
+ # Should fail, not succeed
360
+ log_error "Invalid command should return non-zero exit code"
361
+ all_passed=false
362
+ fi
363
+
364
+ # Test 2: Missing file (check should handle gracefully)
365
+ local test_dir
366
+ test_dir=$(mktemp -d)
367
+
368
+ (
369
+ cd "$test_dir" || exit 1
370
+ # No .env file exists
371
+ tripwire check > /dev/null 2>&1
372
+ # Should exit gracefully (not crash)
373
+ exit 0
374
+ )
375
+
376
+ if [ $? -ne 0 ]; then
377
+ all_passed=false
378
+ fi
379
+
380
+ rm -rf "$test_dir"
381
+
382
+ if $all_passed; then
383
+ log_success "CLI handles errors gracefully (no crashes)"
384
+ return 0
385
+ else
386
+ log_error "Error handling test failed"
387
+ return 1
388
+ fi
389
+ }
390
+
391
+ #######################################
392
+ # Main Execution
393
+ #######################################
394
+
395
+ main() {
396
+ local expected_version="${1:-}"
397
+
398
+ echo "========================================="
399
+ echo "TripWire CLI Validation"
400
+ echo "========================================="
401
+
402
+ if [ -n "$expected_version" ]; then
403
+ echo "Expected version: $expected_version"
404
+ fi
405
+
406
+ echo ""
407
+
408
+ # Run all validation tests
409
+ log_section "Basic Functionality"
410
+ test_command_exists || true
411
+ test_basic_execution || true
412
+ test_help_structure || true
413
+ test_help_not_empty || true
414
+
415
+ if [ -n "$expected_version" ]; then
416
+ log_section "Version Validation"
417
+ test_version_flag "$expected_version" || true
418
+ test_version_consistency || true
419
+ fi
420
+
421
+ log_section "Command Availability"
422
+ test_commands_available || true
423
+
424
+ log_section "Python Integration"
425
+ test_python_imports || true
426
+
427
+ log_section "Functional Tests"
428
+ test_functional_workflow || true
429
+ test_json_output_valid || true
430
+
431
+ log_section "Error Handling"
432
+ test_error_handling || true
433
+
434
+ # Summary
435
+ echo ""
436
+ echo "========================================="
437
+ echo "Test Summary"
438
+ echo "========================================="
439
+ echo -e "Passed: ${GREEN}${TESTS_PASSED}${NC}"
440
+ echo -e "Failed: ${RED}${TESTS_FAILED}${NC}"
441
+
442
+ if [ $TESTS_FAILED -gt 0 ]; then
443
+ echo ""
444
+ echo -e "${RED}Failed tests:${NC}"
445
+ for test in "${FAILED_TESTS[@]}"; do
446
+ echo " - $test"
447
+ done
448
+
449
+ echo ""
450
+ echo -e "${RED}Validation failed${NC}"
451
+ exit 1
452
+ else
453
+ echo ""
454
+ echo -e "${GREEN}All validations passed!${NC}"
455
+ exit 0
456
+ fi
457
+ }
458
+
459
+ # Run with optional version argument
460
+ main "$@"