jleechanorg-pr-automation 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.
Potentially problematic release.
This version of jleechanorg-pr-automation might be problematic. Click here for more details.
- jleechanorg_pr_automation/__init__.py +32 -0
- jleechanorg_pr_automation/automation_safety_manager.py +700 -0
- jleechanorg_pr_automation/automation_safety_wrapper.py +116 -0
- jleechanorg_pr_automation/automation_utils.py +314 -0
- jleechanorg_pr_automation/check_codex_comment.py +76 -0
- jleechanorg_pr_automation/codex_branch_updater.py +272 -0
- jleechanorg_pr_automation/codex_config.py +57 -0
- jleechanorg_pr_automation/jleechanorg_pr_monitor.py +1202 -0
- jleechanorg_pr_automation/tests/conftest.py +12 -0
- jleechanorg_pr_automation/tests/test_actionable_counting_matrix.py +221 -0
- jleechanorg_pr_automation/tests/test_automation_over_running_reproduction.py +147 -0
- jleechanorg_pr_automation/tests/test_automation_safety_limits.py +340 -0
- jleechanorg_pr_automation/tests/test_automation_safety_manager_comprehensive.py +615 -0
- jleechanorg_pr_automation/tests/test_codex_actor_matching.py +137 -0
- jleechanorg_pr_automation/tests/test_graphql_error_handling.py +155 -0
- jleechanorg_pr_automation/tests/test_pr_filtering_matrix.py +473 -0
- jleechanorg_pr_automation/tests/test_pr_targeting.py +95 -0
- jleechanorg_pr_automation/utils.py +232 -0
- jleechanorg_pr_automation-0.1.0.dist-info/METADATA +217 -0
- jleechanorg_pr_automation-0.1.0.dist-info/RECORD +23 -0
- jleechanorg_pr_automation-0.1.0.dist-info/WHEEL +5 -0
- jleechanorg_pr_automation-0.1.0.dist-info/entry_points.txt +3 -0
- jleechanorg_pr_automation-0.1.0.dist-info/top_level.txt +1 -0
|
@@ -0,0 +1,232 @@
|
|
|
1
|
+
#!/usr/bin/env python3
|
|
2
|
+
"""
|
|
3
|
+
Shared utilities for automation system
|
|
4
|
+
|
|
5
|
+
Consolidates common patterns:
|
|
6
|
+
- JSON file operations with thread safety
|
|
7
|
+
- Logging configuration setup
|
|
8
|
+
- Environment variable handling
|
|
9
|
+
- Email configuration management
|
|
10
|
+
"""
|
|
11
|
+
|
|
12
|
+
import fcntl
|
|
13
|
+
import json
|
|
14
|
+
import logging
|
|
15
|
+
import os
|
|
16
|
+
import tempfile
|
|
17
|
+
import threading
|
|
18
|
+
from datetime import datetime
|
|
19
|
+
from pathlib import Path
|
|
20
|
+
from typing import Dict, Any, Optional
|
|
21
|
+
|
|
22
|
+
|
|
23
|
+
class SafeJSONManager:
|
|
24
|
+
"""Thread-safe and cross-process safe JSON file operations with file locking"""
|
|
25
|
+
|
|
26
|
+
def __init__(self):
|
|
27
|
+
self._locks = {}
|
|
28
|
+
self._locks_lock = threading.Lock()
|
|
29
|
+
|
|
30
|
+
def _get_lock(self, file_path: str) -> threading.RLock:
|
|
31
|
+
"""Get or create a lock for a specific file"""
|
|
32
|
+
# Normalize path to prevent duplicate locks for same file
|
|
33
|
+
norm_path = os.path.abspath(file_path)
|
|
34
|
+
with self._locks_lock:
|
|
35
|
+
if norm_path not in self._locks:
|
|
36
|
+
self._locks[norm_path] = threading.RLock()
|
|
37
|
+
return self._locks[norm_path]
|
|
38
|
+
|
|
39
|
+
def _get_file_lock_path(self, file_path: str) -> str:
|
|
40
|
+
"""Get file lock path for cross-process safety"""
|
|
41
|
+
return f"{file_path}.lock"
|
|
42
|
+
|
|
43
|
+
def read_json(self, file_path: str, default: Any = None) -> Any:
|
|
44
|
+
"""Cross-process safe JSON file reading with default fallback"""
|
|
45
|
+
lock = self._get_lock(file_path)
|
|
46
|
+
with lock:
|
|
47
|
+
try:
|
|
48
|
+
if os.path.exists(file_path):
|
|
49
|
+
with open(file_path, 'r') as f:
|
|
50
|
+
# Add file lock for cross-process safety
|
|
51
|
+
fcntl.flock(f.fileno(), fcntl.LOCK_SH)
|
|
52
|
+
try:
|
|
53
|
+
return json.load(f)
|
|
54
|
+
finally:
|
|
55
|
+
fcntl.flock(f.fileno(), fcntl.LOCK_UN)
|
|
56
|
+
else:
|
|
57
|
+
return default if default is not None else {}
|
|
58
|
+
except (json.JSONDecodeError, IOError) as e:
|
|
59
|
+
logging.warning(f"Failed to read JSON from {file_path}: {e}")
|
|
60
|
+
return default if default is not None else {}
|
|
61
|
+
|
|
62
|
+
def write_json(self, file_path: str, data: Any) -> bool:
|
|
63
|
+
"""Cross-process safe JSON file writing with atomic operations"""
|
|
64
|
+
lock = self._get_lock(file_path)
|
|
65
|
+
with lock:
|
|
66
|
+
try:
|
|
67
|
+
# Ensure directory exists (guard against empty dirname for root files)
|
|
68
|
+
directory = os.path.dirname(file_path)
|
|
69
|
+
if directory:
|
|
70
|
+
os.makedirs(directory, exist_ok=True)
|
|
71
|
+
|
|
72
|
+
# Create temp file in same directory for atomic replace
|
|
73
|
+
dir_path = directory or "."
|
|
74
|
+
fd, temp_path = tempfile.mkstemp(
|
|
75
|
+
dir=dir_path,
|
|
76
|
+
prefix=os.path.basename(file_path),
|
|
77
|
+
suffix=".tmp"
|
|
78
|
+
)
|
|
79
|
+
|
|
80
|
+
try:
|
|
81
|
+
with os.fdopen(fd, 'w') as f:
|
|
82
|
+
# Add exclusive file lock for cross-process safety
|
|
83
|
+
fcntl.flock(f.fileno(), fcntl.LOCK_EX)
|
|
84
|
+
try:
|
|
85
|
+
json.dump(data, f, indent=2, default=str)
|
|
86
|
+
f.flush()
|
|
87
|
+
os.fsync(f.fileno())
|
|
88
|
+
finally:
|
|
89
|
+
fcntl.flock(f.fileno(), fcntl.LOCK_UN)
|
|
90
|
+
|
|
91
|
+
# Atomic replace
|
|
92
|
+
os.replace(temp_path, file_path)
|
|
93
|
+
return True
|
|
94
|
+
finally:
|
|
95
|
+
# Clean up temp file if replace failed
|
|
96
|
+
if os.path.exists(temp_path):
|
|
97
|
+
try:
|
|
98
|
+
os.remove(temp_path)
|
|
99
|
+
except OSError:
|
|
100
|
+
pass
|
|
101
|
+
|
|
102
|
+
except (IOError, OSError) as e:
|
|
103
|
+
logging.error(f"Failed to write JSON to {file_path}: {e}")
|
|
104
|
+
return False
|
|
105
|
+
|
|
106
|
+
def update_json(self, file_path: str, update_func, lock_timeout: int = 10) -> bool:
|
|
107
|
+
"""Cross-process safe JSON file update with callback function"""
|
|
108
|
+
lock = self._get_lock(file_path)
|
|
109
|
+
with lock:
|
|
110
|
+
try:
|
|
111
|
+
data = self.read_json(file_path, {})
|
|
112
|
+
updated_data = update_func(data)
|
|
113
|
+
return self.write_json(file_path, updated_data)
|
|
114
|
+
except Exception as e:
|
|
115
|
+
logging.error(f"Failed to update JSON {file_path}: {e}")
|
|
116
|
+
return False
|
|
117
|
+
|
|
118
|
+
|
|
119
|
+
# Global instance for shared use
|
|
120
|
+
json_manager = SafeJSONManager()
|
|
121
|
+
|
|
122
|
+
|
|
123
|
+
def setup_logging(name: str, level: int = logging.INFO,
|
|
124
|
+
log_file: Optional[str] = None) -> logging.Logger:
|
|
125
|
+
"""Standardized logging setup for automation components"""
|
|
126
|
+
logger = logging.getLogger(name)
|
|
127
|
+
|
|
128
|
+
# Avoid duplicate handlers
|
|
129
|
+
if logger.handlers:
|
|
130
|
+
return logger
|
|
131
|
+
|
|
132
|
+
logger.setLevel(level)
|
|
133
|
+
|
|
134
|
+
# Create formatter
|
|
135
|
+
formatter = logging.Formatter(
|
|
136
|
+
'%(asctime)s - %(name)s - %(levelname)s - %(message)s'
|
|
137
|
+
)
|
|
138
|
+
|
|
139
|
+
# Console handler
|
|
140
|
+
console_handler = logging.StreamHandler()
|
|
141
|
+
console_handler.setFormatter(formatter)
|
|
142
|
+
logger.addHandler(console_handler)
|
|
143
|
+
|
|
144
|
+
# File handler if specified
|
|
145
|
+
if log_file:
|
|
146
|
+
os.makedirs(os.path.dirname(log_file), exist_ok=True)
|
|
147
|
+
file_handler = logging.FileHandler(log_file)
|
|
148
|
+
file_handler.setFormatter(formatter)
|
|
149
|
+
logger.addHandler(file_handler)
|
|
150
|
+
|
|
151
|
+
return logger
|
|
152
|
+
|
|
153
|
+
|
|
154
|
+
def get_env_config(prefix: str = "AUTOMATION_") -> Dict[str, str]:
|
|
155
|
+
"""Get all environment variables with specified prefix"""
|
|
156
|
+
config = {}
|
|
157
|
+
for key, value in os.environ.items():
|
|
158
|
+
if key.startswith(prefix):
|
|
159
|
+
# Remove prefix and convert to lowercase
|
|
160
|
+
config_key = key[len(prefix):].lower()
|
|
161
|
+
config[config_key] = value
|
|
162
|
+
return config
|
|
163
|
+
|
|
164
|
+
|
|
165
|
+
def get_email_config() -> Dict[str, str]:
|
|
166
|
+
"""Get email configuration from environment variables"""
|
|
167
|
+
email_config = {
|
|
168
|
+
'smtp_server': os.getenv('SMTP_SERVER', 'localhost'),
|
|
169
|
+
'smtp_port': int(os.getenv('SMTP_PORT', '587')),
|
|
170
|
+
'email_user': os.getenv('EMAIL_USER', ''),
|
|
171
|
+
'email_pass': os.getenv('EMAIL_PASS', ''),
|
|
172
|
+
'email_to': os.getenv('EMAIL_TO', ''),
|
|
173
|
+
'email_from': os.getenv('EMAIL_FROM', os.getenv('EMAIL_USER', ''))
|
|
174
|
+
}
|
|
175
|
+
return email_config
|
|
176
|
+
|
|
177
|
+
|
|
178
|
+
def validate_email_config(config: Dict[str, str]) -> bool:
|
|
179
|
+
"""Validate that required email configuration is present"""
|
|
180
|
+
required_fields = ['smtp_server', 'email_user', 'email_pass', 'email_to']
|
|
181
|
+
return all(config.get(field) for field in required_fields)
|
|
182
|
+
|
|
183
|
+
|
|
184
|
+
def get_automation_limits() -> Dict[str, int]:
|
|
185
|
+
"""Get automation safety limits from environment or defaults"""
|
|
186
|
+
return {
|
|
187
|
+
'pr_limit': int(os.getenv('AUTOMATION_PR_LIMIT', '5')),
|
|
188
|
+
'global_limit': int(os.getenv('AUTOMATION_GLOBAL_LIMIT', '50')),
|
|
189
|
+
'approval_hours': int(os.getenv('AUTOMATION_APPROVAL_HOURS', '24')),
|
|
190
|
+
'subprocess_timeout': int(os.getenv('AUTOMATION_SUBPROCESS_TIMEOUT', '300'))
|
|
191
|
+
}
|
|
192
|
+
|
|
193
|
+
|
|
194
|
+
def ensure_directory(file_path: str) -> None:
|
|
195
|
+
"""Ensure directory exists for given file path"""
|
|
196
|
+
directory = os.path.dirname(file_path)
|
|
197
|
+
if directory:
|
|
198
|
+
os.makedirs(directory, exist_ok=True)
|
|
199
|
+
|
|
200
|
+
|
|
201
|
+
def get_project_root() -> str:
|
|
202
|
+
"""Get project root directory"""
|
|
203
|
+
current_dir = Path(__file__).resolve()
|
|
204
|
+
# Look for CLAUDE.md to identify project root
|
|
205
|
+
for parent in current_dir.parents:
|
|
206
|
+
if (parent / "CLAUDE.md").exists():
|
|
207
|
+
return str(parent)
|
|
208
|
+
# Fallback to parent of automation directory
|
|
209
|
+
return str(current_dir.parent.parent)
|
|
210
|
+
|
|
211
|
+
|
|
212
|
+
def format_timestamp(dt: datetime = None) -> str:
|
|
213
|
+
"""Format datetime as ISO string"""
|
|
214
|
+
if dt is None:
|
|
215
|
+
dt = datetime.now()
|
|
216
|
+
return dt.isoformat()
|
|
217
|
+
|
|
218
|
+
|
|
219
|
+
def parse_timestamp(timestamp_str: str) -> datetime:
|
|
220
|
+
"""Parse ISO timestamp string to datetime"""
|
|
221
|
+
return datetime.fromisoformat(timestamp_str)
|
|
222
|
+
|
|
223
|
+
|
|
224
|
+
def get_test_email_config() -> Dict[str, str]:
|
|
225
|
+
"""Get standardized test email configuration"""
|
|
226
|
+
return {
|
|
227
|
+
'SMTP_SERVER': 'smtp.example.com',
|
|
228
|
+
'SMTP_PORT': '587',
|
|
229
|
+
'EMAIL_USER': 'test@example.com',
|
|
230
|
+
'EMAIL_PASS': 'testpass',
|
|
231
|
+
'EMAIL_TO': 'admin@example.com'
|
|
232
|
+
}
|
|
@@ -0,0 +1,217 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: jleechanorg-pr-automation
|
|
3
|
+
Version: 0.1.0
|
|
4
|
+
Summary: GitHub PR automation system with safety limits and actionable counting
|
|
5
|
+
Author-email: jleechan <jlee@jleechan.org>
|
|
6
|
+
License: MIT
|
|
7
|
+
Project-URL: Homepage, https://github.com/jleechanorg/worldarchitect.ai
|
|
8
|
+
Project-URL: Repository, https://github.com/jleechanorg/worldarchitect.ai
|
|
9
|
+
Project-URL: Issues, https://github.com/jleechanorg/worldarchitect.ai/issues
|
|
10
|
+
Keywords: github,automation,pr,pull-request,monitoring
|
|
11
|
+
Classifier: Development Status :: 4 - Beta
|
|
12
|
+
Classifier: Intended Audience :: Developers
|
|
13
|
+
Classifier: License :: OSI Approved :: MIT License
|
|
14
|
+
Classifier: Programming Language :: Python :: 3
|
|
15
|
+
Classifier: Programming Language :: Python :: 3.9
|
|
16
|
+
Classifier: Programming Language :: Python :: 3.10
|
|
17
|
+
Classifier: Programming Language :: Python :: 3.11
|
|
18
|
+
Classifier: Programming Language :: Python :: 3.12
|
|
19
|
+
Classifier: Topic :: Software Development :: Libraries :: Python Modules
|
|
20
|
+
Classifier: Topic :: Software Development :: Version Control :: Git
|
|
21
|
+
Requires-Python: >=3.9
|
|
22
|
+
Description-Content-Type: text/markdown
|
|
23
|
+
Requires-Dist: requests>=2.25.0
|
|
24
|
+
Provides-Extra: email
|
|
25
|
+
Requires-Dist: keyring>=23.0.0; extra == "email"
|
|
26
|
+
Provides-Extra: dev
|
|
27
|
+
Requires-Dist: pytest>=7.0.0; extra == "dev"
|
|
28
|
+
Requires-Dist: pytest-cov>=4.0.0; extra == "dev"
|
|
29
|
+
Requires-Dist: black>=22.0.0; extra == "dev"
|
|
30
|
+
Requires-Dist: ruff>=0.1.0; extra == "dev"
|
|
31
|
+
|
|
32
|
+
# jleechanorg-pr-automation
|
|
33
|
+
|
|
34
|
+
A comprehensive GitHub PR automation system with safety limits, actionable counting, and intelligent filtering.
|
|
35
|
+
|
|
36
|
+
## Features
|
|
37
|
+
|
|
38
|
+
- **Actionable PR Counting**: Only processes PRs that need attention, excluding already-processed ones
|
|
39
|
+
- **Safety Limits**: Built-in rate limiting and attempt tracking to prevent automation abuse
|
|
40
|
+
- **Cross-Process Safety**: Thread-safe operations with file-based persistence
|
|
41
|
+
- **Email Notifications**: Optional SMTP integration for automation alerts
|
|
42
|
+
- **Commit-Based Tracking**: Avoids duplicate processing using commit SHAs
|
|
43
|
+
- **Comprehensive Testing**: 200+ test cases with matrix-driven coverage
|
|
44
|
+
|
|
45
|
+
## Installation
|
|
46
|
+
|
|
47
|
+
```bash
|
|
48
|
+
pip install jleechanorg-pr-automation
|
|
49
|
+
```
|
|
50
|
+
|
|
51
|
+
### Optional Dependencies
|
|
52
|
+
|
|
53
|
+
For email notifications:
|
|
54
|
+
```bash
|
|
55
|
+
pip install jleechanorg-pr-automation[email]
|
|
56
|
+
```
|
|
57
|
+
|
|
58
|
+
For development:
|
|
59
|
+
```bash
|
|
60
|
+
pip install jleechanorg-pr-automation[dev]
|
|
61
|
+
```
|
|
62
|
+
|
|
63
|
+
## Quick Start
|
|
64
|
+
|
|
65
|
+
### Basic PR Monitoring
|
|
66
|
+
|
|
67
|
+
```python
|
|
68
|
+
from jleechanorg_pr_automation import JleechanorgPRMonitor
|
|
69
|
+
|
|
70
|
+
# Initialize monitor
|
|
71
|
+
monitor = JleechanorgPRMonitor()
|
|
72
|
+
|
|
73
|
+
# Process up to 20 actionable PRs
|
|
74
|
+
result = monitor.run_monitoring_cycle_with_actionable_count(target_actionable_count=20)
|
|
75
|
+
|
|
76
|
+
print(f"Processed {result['actionable_processed']} actionable PRs")
|
|
77
|
+
print(f"Skipped {result['skipped_count']} non-actionable PRs")
|
|
78
|
+
```
|
|
79
|
+
|
|
80
|
+
### Safety Management
|
|
81
|
+
|
|
82
|
+
```python
|
|
83
|
+
from jleechanorg_pr_automation import AutomationSafetyManager
|
|
84
|
+
|
|
85
|
+
# Initialize safety manager with data directory
|
|
86
|
+
safety = AutomationSafetyManager(data_dir="/tmp/automation_safety")
|
|
87
|
+
|
|
88
|
+
# Limits are configured via automation_safety_config.json inside the data directory
|
|
89
|
+
# or the AUTOMATION_PR_LIMIT / AUTOMATION_GLOBAL_LIMIT environment variables.
|
|
90
|
+
|
|
91
|
+
# Check if PR can be processed
|
|
92
|
+
if safety.can_process_pr(pr_number=123, repo="my-repo"):
|
|
93
|
+
# Process PR...
|
|
94
|
+
safety.record_pr_attempt(pr_number=123, result="success", repo="my-repo")
|
|
95
|
+
```
|
|
96
|
+
|
|
97
|
+
## Configuration
|
|
98
|
+
|
|
99
|
+
### Environment Variables
|
|
100
|
+
|
|
101
|
+
- `GITHUB_TOKEN`: GitHub personal access token (required)
|
|
102
|
+
- `PR_AUTOMATION_WORKSPACE`: Custom workspace directory (optional)
|
|
103
|
+
- `AUTOMATION_PR_LIMIT`: Maximum attempts per PR (default: 5)
|
|
104
|
+
- `AUTOMATION_GLOBAL_LIMIT`: Maximum global automation runs (default: 50)
|
|
105
|
+
- `AUTOMATION_APPROVAL_HOURS`: Hours before approval expires (default: 24)
|
|
106
|
+
|
|
107
|
+
### Email Configuration (Optional)
|
|
108
|
+
|
|
109
|
+
- `SMTP_SERVER`: SMTP server hostname
|
|
110
|
+
- `SMTP_PORT`: SMTP server port (default: 587)
|
|
111
|
+
- `EMAIL_USER`: SMTP username
|
|
112
|
+
- `EMAIL_PASS`: SMTP password
|
|
113
|
+
- `EMAIL_TO`: Notification recipient
|
|
114
|
+
- `EMAIL_FROM`: Sender address (defaults to EMAIL_USER)
|
|
115
|
+
|
|
116
|
+
## Command Line Interface
|
|
117
|
+
|
|
118
|
+
### PR Monitor
|
|
119
|
+
|
|
120
|
+
```bash
|
|
121
|
+
# Monitor all repositories
|
|
122
|
+
jleechanorg-pr-monitor
|
|
123
|
+
|
|
124
|
+
# Process specific repository
|
|
125
|
+
jleechanorg-pr-monitor --single-repo worldarchitect.ai
|
|
126
|
+
|
|
127
|
+
# Process specific PR
|
|
128
|
+
jleechanorg-pr-monitor --target-pr 123 --target-repo jleechanorg/worldarchitect.ai
|
|
129
|
+
|
|
130
|
+
# Dry run (discovery only)
|
|
131
|
+
jleechanorg-pr-monitor --dry-run
|
|
132
|
+
```
|
|
133
|
+
|
|
134
|
+
### Safety Manager CLI
|
|
135
|
+
|
|
136
|
+
```bash
|
|
137
|
+
# Check current status
|
|
138
|
+
automation-safety-cli status
|
|
139
|
+
|
|
140
|
+
# Clear all safety data
|
|
141
|
+
automation-safety-cli clear
|
|
142
|
+
|
|
143
|
+
# Check specific PR
|
|
144
|
+
automation-safety-cli check-pr 123 --repo my-repo
|
|
145
|
+
```
|
|
146
|
+
|
|
147
|
+
## Architecture
|
|
148
|
+
|
|
149
|
+
### Actionable PR Logic
|
|
150
|
+
|
|
151
|
+
The system implements intelligent PR filtering:
|
|
152
|
+
|
|
153
|
+
1. **State Check**: Only processes open PRs
|
|
154
|
+
2. **Commit Tracking**: Skips PRs already processed with current commit SHA
|
|
155
|
+
3. **Safety Limits**: Respects per-PR and global automation limits
|
|
156
|
+
4. **Ordering**: Processes most recently updated PRs first
|
|
157
|
+
|
|
158
|
+
### Safety Features
|
|
159
|
+
|
|
160
|
+
- **Dual Limiting**: Per-PR consecutive failure limits + global run limits
|
|
161
|
+
- **Cross-Process Safety**: File-based locking for concurrent automation instances
|
|
162
|
+
- **Attempt Tracking**: Full history of success/failure with timestamps
|
|
163
|
+
- **Graceful Degradation**: Continues processing other PRs if one fails
|
|
164
|
+
|
|
165
|
+
### Testing
|
|
166
|
+
|
|
167
|
+
The library includes comprehensive test coverage:
|
|
168
|
+
|
|
169
|
+
- **Matrix Testing**: All PR state combinations (Open/Closed × New/Old Commits × Processed/Fresh)
|
|
170
|
+
- **Actionable Counting**: Batch processing with skip exclusion
|
|
171
|
+
- **Safety Limits**: Concurrent access and edge cases
|
|
172
|
+
- **Integration Tests**: Real GitHub API interactions (optional)
|
|
173
|
+
|
|
174
|
+
## Development
|
|
175
|
+
|
|
176
|
+
```bash
|
|
177
|
+
# Clone repository
|
|
178
|
+
git clone https://github.com/jleechanorg/worldarchitect.ai.git
|
|
179
|
+
cd worldarchitect.ai/automation
|
|
180
|
+
|
|
181
|
+
# Install in development mode
|
|
182
|
+
pip install -e .[dev]
|
|
183
|
+
|
|
184
|
+
# Run tests
|
|
185
|
+
pytest
|
|
186
|
+
|
|
187
|
+
# Run tests with coverage
|
|
188
|
+
pytest --cov=jleechanorg_pr_automation
|
|
189
|
+
|
|
190
|
+
# Format code
|
|
191
|
+
black .
|
|
192
|
+
ruff check .
|
|
193
|
+
```
|
|
194
|
+
|
|
195
|
+
## Contributing
|
|
196
|
+
|
|
197
|
+
1. Fork the repository
|
|
198
|
+
2. Create a feature branch
|
|
199
|
+
3. Add tests for new functionality
|
|
200
|
+
4. Ensure all tests pass
|
|
201
|
+
5. Submit a pull request
|
|
202
|
+
|
|
203
|
+
## License
|
|
204
|
+
|
|
205
|
+
MIT License - see LICENSE file for details.
|
|
206
|
+
|
|
207
|
+
## Changelog
|
|
208
|
+
|
|
209
|
+
### 0.1.0 (2025-09-28)
|
|
210
|
+
|
|
211
|
+
- Initial release
|
|
212
|
+
- Actionable PR counting system
|
|
213
|
+
- Safety management with dual limits
|
|
214
|
+
- Cross-process file-based persistence
|
|
215
|
+
- Email notification support
|
|
216
|
+
- Comprehensive test suite (200+ tests)
|
|
217
|
+
- CLI interfaces for monitoring and safety management
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
jleechanorg_pr_automation/__init__.py,sha256=qYUTmbETCH775SM_6ln8jX05WzAGPErD-JN7OTIzXrY,852
|
|
2
|
+
jleechanorg_pr_automation/automation_safety_manager.py,sha256=La3GStljdkxsSJBklWVj8CKAx6ndCsyI_aS6tSBZGJ8,27531
|
|
3
|
+
jleechanorg_pr_automation/automation_safety_wrapper.py,sha256=Oa88wGH7XKfOywVnDMxwL0EP_v5YHvkKaSHxTvyiR48,4059
|
|
4
|
+
jleechanorg_pr_automation/automation_utils.py,sha256=MInKTLuQSPwIXga8em70JtgGED3KU_OsV3cvX-4tN-Y,11608
|
|
5
|
+
jleechanorg_pr_automation/check_codex_comment.py,sha256=ccs4XjPLPnsuMiSS_pXo_u-EzNldR34PqyWTlu0U0H0,2221
|
|
6
|
+
jleechanorg_pr_automation/codex_branch_updater.py,sha256=490tjJDOCm58LoRS3F7vslOINVBU4F2C__2dWYRKcFs,9135
|
|
7
|
+
jleechanorg_pr_automation/codex_config.py,sha256=JOrAzVwdDac9gHdLFSYDco0fJ13pdOQT22kAiMYRHhg,2101
|
|
8
|
+
jleechanorg_pr_automation/jleechanorg_pr_monitor.py,sha256=6WDl0zwbJJKrRrHLZQ87J-680FC2DbLCZJ4Q2Cn62dU,48700
|
|
9
|
+
jleechanorg_pr_automation/utils.py,sha256=nhPyR7_wm2Bh-D2aE6ueqWVWLelzzXz_BCwkdC920ig,8168
|
|
10
|
+
jleechanorg_pr_automation/tests/conftest.py,sha256=GVE-RLmzVM9yBNVgfZOHu39onmZPMPjN_vrIsmcWlvU,388
|
|
11
|
+
jleechanorg_pr_automation/tests/test_actionable_counting_matrix.py,sha256=rQxFH6ontV3GpZigFRiHCXRkX0mEnespJv73L1DfPw0,12174
|
|
12
|
+
jleechanorg_pr_automation/tests/test_automation_over_running_reproduction.py,sha256=X1sIKr-ZpAp-lks0wVGnNDM4Yas1uCrgSokW6rYGm38,5759
|
|
13
|
+
jleechanorg_pr_automation/tests/test_automation_safety_limits.py,sha256=xFQIxLsXlP0VLnZRueFEUYOFhX1d9fxXXJf_1EzBY2Q,13340
|
|
14
|
+
jleechanorg_pr_automation/tests/test_automation_safety_manager_comprehensive.py,sha256=CLyWuhl3TQrMKDoLxUikpuMtSotm6FZZZXT_WPBCI_w,22740
|
|
15
|
+
jleechanorg_pr_automation/tests/test_codex_actor_matching.py,sha256=rY6AGM1DatKrYwGIryM36zYLBBK--ydOwJwkH2T7uRk,5252
|
|
16
|
+
jleechanorg_pr_automation/tests/test_graphql_error_handling.py,sha256=Fv0BWLSF-eQ4n5gHwf_JSc42Y0RizrjPPlCB4THLzaI,6401
|
|
17
|
+
jleechanorg_pr_automation/tests/test_pr_filtering_matrix.py,sha256=IBRn9APpNBSpedsvmKVb26ZRrHHj0EzE4F5AvCH9mP8,21415
|
|
18
|
+
jleechanorg_pr_automation/tests/test_pr_targeting.py,sha256=YXyWUGQj2LzUDyZ1NU57SoRXtNcUCuRNMtgCcqrcoGU,3798
|
|
19
|
+
jleechanorg_pr_automation-0.1.0.dist-info/METADATA,sha256=IUHkLXylUcpFalTqBYu82Rk35s1XmHYTSB-0_lHWUOQ,6333
|
|
20
|
+
jleechanorg_pr_automation-0.1.0.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
|
|
21
|
+
jleechanorg_pr_automation-0.1.0.dist-info/entry_points.txt,sha256=QD8UUHJ4H09_beMvHzZ5SOy5cRbthvD11mXLzaWjwrg,178
|
|
22
|
+
jleechanorg_pr_automation-0.1.0.dist-info/top_level.txt,sha256=1DJKrq0Be2B5_NL5jTICV1rvnqaMXFmyJpuOTUatcig,26
|
|
23
|
+
jleechanorg_pr_automation-0.1.0.dist-info/RECORD,,
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
jleechanorg_pr_automation
|