-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathaction_runner.py
More file actions
131 lines (112 loc) Β· 5.23 KB
/
Copy pathaction_runner.py
File metadata and controls
131 lines (112 loc) Β· 5.23 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
"""
RepoForge Action Runner
Executes parsed Forge actions safely (create/modify files, etc.).
Accepts either list[str] or list[dict] (from ActionParser).
"""
from __future__ import annotations
from pathlib import Path
from typing import Any, Dict, List, Union
class ActionRunner:
def __init__(self, repo_path: str):
self.repo_path = Path(repo_path).resolve()
self.executed: List[Dict[str, Any]] = []
self.errors: List[Dict[str, Any]] = []
def run(
self,
actions: Union[List[str], List[Dict[str, Any]], str, None],
dry_run: bool = False,
) -> Dict[str, Any]:
if actions is None:
actions = []
if isinstance(actions, str):
# Safety: never iterate a raw string character-by-character
actions = [actions] if actions.strip() else []
if not actions:
return {
"status": "no_actions",
"executed": [],
"errors": [],
"completed": 0,
}
print(f" π Executing {len(actions)} actions in {self.repo_path}")
for action in actions:
try:
if isinstance(action, dict):
self._execute_dict_action(action, dry_run)
else:
self._execute_action(str(action), dry_run)
except Exception as e:
self.errors.append({"action": str(action)[:200], "error": str(e)})
completed = sum(
1 for e in self.executed if e.get("status") in ("created", "modified", "ok")
)
return {
"status": "completed" if not self.errors else "partial",
"executed": self.executed,
"errors": self.errors,
"completed": completed,
}
def _execute_dict_action(self, action: Dict[str, Any], dry_run: bool):
atype = (action.get("type") or action.get("action") or "").lower()
file = action.get("file") or action.get("path") or ""
content = action.get("content") or action.get("body") or ""
if atype in ("modify", "create", "write", "update") and file:
self._write_file(file, content, dry_run)
return
if atype in ("mkdir", "create_dir", "directory") and file:
self._create_directory(file, dry_run)
return
# Fall through to string handling
desc = action.get("description") or action.get("summary") or str(action)
self._execute_action(desc, dry_run)
def _execute_action(self, action: str, dry_run: bool):
action = action.strip()
if not action or len(action) < 3:
return
lower = action.lower()
if lower.startswith("create ") or lower.startswith("add "):
parts = action.split(" ", 1)
if len(parts) == 2:
filename = parts[1].strip()
if "directory" in filename.lower():
self._create_directory(
filename.replace(" directory", "").strip(), dry_run
)
return
self._write_file(filename, "", dry_run)
return
# Heuristic keyword β useful files
mapping = [
("readme", "README.md", "# Project\n\n## Description\n\nGenerated by RepoForge.\n"),
("license", "LICENSE", "MIT License\n\nCopyright (c) 2026\n"),
("contributing", "CONTRIBUTING.md", "# Contributing\n\n1. Fork\n2. PR\n"),
("changelog", "CHANGELOG.md", "# Changelog\n\n## [Unreleased]\n- Initial\n"),
("dockerfile", "Dockerfile", "FROM python:3.11\nWORKDIR /app\nCOPY . .\nCMD [\"python\", \"main.py\"]\n"),
("requirements", "requirements.txt", "# Dependencies\n"),
("github actions", ".github/workflows/ci.yml", "name: CI\non: [push]\njobs:\n build:\n runs-on: ubuntu-latest\n steps:\n - uses: actions/checkout@v4\n"),
]
for keyword, path, content in mapping:
if keyword in lower:
self._write_file(path, content, dry_run)
return
print(f" β οΈ Unknown action: {action[:80]}")
self.executed.append({"action": action[:120], "status": "unknown"})
def _write_file(self, filepath: str, content: str, dry_run: bool):
full_path = self.repo_path / filepath
if dry_run:
print(f" π Would write: {full_path}")
self.executed.append({"action": f"Write {filepath}", "status": "dry_run"})
return
full_path.parent.mkdir(parents=True, exist_ok=True)
full_path.write_text(content, encoding="utf-8")
print(f" β
Wrote: {full_path}")
self.executed.append({"action": f"Write {filepath}", "status": "created"})
def _create_directory(self, dirname: str, dry_run: bool):
full_path = self.repo_path / dirname
if dry_run:
print(f" π Would create directory: {full_path}")
self.executed.append({"action": f"Create directory {dirname}", "status": "dry_run"})
return
full_path.mkdir(parents=True, exist_ok=True)
print(f" β
Created directory: {full_path}")
self.executed.append({"action": f"Create directory {dirname}", "status": "created"})