AI Update: تسک ها را آماده کن این ساختار عالیه
This commit is contained in:
+140
-19
@@ -159,8 +159,11 @@ class GitManager:
|
||||
|
||||
async def init_project_repo(self, workspace_path: str, repo_name: str, owner: str = "root") -> Dict[str, Any]:
|
||||
"""
|
||||
Initializes git in workspace_path, links with Gitea remote, creates .gitignore,
|
||||
and pushes the initial commit.
|
||||
Initializes git in workspace_path with dual branches:
|
||||
- `production`: base/production branch
|
||||
- `dev`: active development branch
|
||||
Links with Gitea remote, creates .gitignore, pushes initial commits on both branches,
|
||||
and leaves `dev` as the active branch.
|
||||
"""
|
||||
ws = Path(workspace_path).expanduser().resolve()
|
||||
os.makedirs(ws, exist_ok=True)
|
||||
@@ -186,7 +189,7 @@ class GitManager:
|
||||
|
||||
try:
|
||||
if is_new:
|
||||
await self._run_cmd(["git", "init", "-b", "main"], cwd=str(ws))
|
||||
await self._run_cmd(["git", "init", "-b", "production"], cwd=str(ws))
|
||||
|
||||
# Configure author
|
||||
await self._run_cmd(["git", "config", "user.name", "Antigravity Bot"], cwd=str(ws))
|
||||
@@ -200,19 +203,36 @@ class GitManager:
|
||||
else:
|
||||
await self._run_cmd(["git", "remote", "add", "origin", remote_url], cwd=str(ws))
|
||||
|
||||
# Initial add, commit and push
|
||||
# Check if production branch exists, if on main rename to production
|
||||
curr_branch = (await self._run_cmd(["git", "branch", "--show-current"], cwd=str(ws))).strip()
|
||||
if curr_branch in ("main", "master"):
|
||||
try:
|
||||
await self._run_cmd(["git", "branch", "-M", "production"], cwd=str(ws))
|
||||
except Exception:
|
||||
pass
|
||||
|
||||
# Initial add, commit and push to production
|
||||
status_out = await self._run_cmd(["git", "status", "--porcelain"], cwd=str(ws))
|
||||
if status_out.strip() or is_new:
|
||||
await self._run_cmd(["git", "add", "-A"], cwd=str(ws))
|
||||
await self._run_cmd(["git", "commit", "-m", f"Initial commit for {clean_name}"], cwd=str(ws))
|
||||
await self._run_cmd(["git", "push", "-u", "origin", "main", "--force"], cwd=str(ws))
|
||||
await self._run_cmd(["git", "push", "-u", "origin", "production", "--force"], cwd=str(ws))
|
||||
|
||||
logger.info(f"Initialized Git repo for {clean_name} at {ws}")
|
||||
# Ensure dev branch exists and is pushed
|
||||
branches_out = await self._run_cmd(["git", "branch"], cwd=str(ws))
|
||||
if "dev" not in branches_out:
|
||||
await self._run_cmd(["git", "checkout", "-b", "dev"], cwd=str(ws))
|
||||
await self._run_cmd(["git", "push", "-u", "origin", "dev", "--force"], cwd=str(ws))
|
||||
else:
|
||||
await self._run_cmd(["git", "checkout", "dev"], cwd=str(ws))
|
||||
|
||||
logger.info(f"Initialized Git repo for {clean_name} at {ws} with dev & production branches.")
|
||||
return {
|
||||
"success": True,
|
||||
"name": clean_name,
|
||||
"web_url": urls["web_url"],
|
||||
"clone_url": urls["clone_url"],
|
||||
"active_branch": "dev",
|
||||
}
|
||||
except Exception as e:
|
||||
logger.error(f"Error initializing git repo for {clean_name} in {ws}: {e}")
|
||||
@@ -233,7 +253,7 @@ class GitManager:
|
||||
porcelain = await self._run_cmd(["git", "status", "--porcelain"], cwd=str(ws))
|
||||
dirty_files = [line.strip() for line in porcelain.splitlines() if line.strip()]
|
||||
|
||||
branch = (await self._run_cmd(["git", "branch", "--show-current"], cwd=str(ws))).strip() or "main"
|
||||
branch = (await self._run_cmd(["git", "branch", "--show-current"], cwd=str(ws))).strip() or "dev"
|
||||
|
||||
# Last commit
|
||||
log_out = await self._run_cmd(["git", "log", "-1", "--format=%h|%an|%ar|%s"], cwd=str(ws))
|
||||
@@ -258,7 +278,7 @@ class GitManager:
|
||||
}
|
||||
except Exception as e:
|
||||
logger.debug(f"Git status error in {ws}: {e}")
|
||||
return {"is_git": True, "dirty": False, "files": [], "error": str(e), "branch": "main"}
|
||||
return {"is_git": True, "dirty": False, "files": [], "error": str(e), "branch": "dev"}
|
||||
|
||||
async def git_commit_and_push(
|
||||
self,
|
||||
@@ -266,8 +286,9 @@ class GitManager:
|
||||
message: str = "Auto-commit from Antigravity",
|
||||
repo_name: Optional[str] = None,
|
||||
owner: str = "root",
|
||||
branch: Optional[str] = None,
|
||||
) -> Tuple[bool, str]:
|
||||
"""Stages all changes, commits with message, and pushes to Gitea."""
|
||||
"""Stages all changes, commits with message, and pushes to Gitea on the active branch (default dev)."""
|
||||
ws = Path(workspace_path).expanduser().resolve()
|
||||
if not (ws / ".git").exists():
|
||||
name = repo_name or ws.name
|
||||
@@ -276,42 +297,142 @@ class GitManager:
|
||||
return False, f"Failed to initialize repo: {init_res.get('error')}"
|
||||
|
||||
try:
|
||||
curr_branch = branch or (await self._run_cmd(["git", "branch", "--show-current"], cwd=str(ws))).strip() or "dev"
|
||||
# Check for uncommitted changes
|
||||
porcelain = await self._run_cmd(["git", "status", "--porcelain"], cwd=str(ws))
|
||||
if not porcelain.strip():
|
||||
return True, "درخت کاری تمیز است (تغییری برای کامیت وجود نداشت)."
|
||||
return True, f"درخت کاری شاخه <code>{curr_branch}</code> تمیز است (تغییری برای کامیت وجود نداشت)."
|
||||
|
||||
await self._run_cmd(["git", "add", "-A"], cwd=str(ws))
|
||||
clean_msg = message.replace('"', '\\"').replace("\n", " ")
|
||||
if not clean_msg.strip():
|
||||
clean_msg = "Updates from Antigravity Bot"
|
||||
clean_msg = f"Updates on {curr_branch} from Antigravity Bot"
|
||||
|
||||
commit_out = await self._run_cmd(["git", "commit", "-m", clean_msg], cwd=str(ws))
|
||||
push_out = await self._run_cmd(["git", "push", "origin", "main"], cwd=str(ws))
|
||||
push_out = await self._run_cmd(["git", "push", "origin", curr_branch], cwd=str(ws))
|
||||
|
||||
# Extract hash
|
||||
log_out = await self._run_cmd(["git", "log", "-1", "--format=%h - %s"], cwd=str(ws))
|
||||
return True, f"✅ تغییرات با موفقیت کامیت و پوش شد:\n<code>{log_out.strip()}</code>"
|
||||
return True, f"✅ تغییرات با موفقیت در شاخه <code>{curr_branch}</code> کامیت و پوش شد:\n<code>{log_out.strip()}</code>"
|
||||
except Exception as e:
|
||||
logger.error(f"Git commit/push error in {ws}: {e}")
|
||||
return False, f"❌ خطا در کامیت یا پوش: {e}"
|
||||
|
||||
async def git_pull(self, workspace_path: str) -> Tuple[bool, str]:
|
||||
"""Pulls latest changes from remote Gitea repository."""
|
||||
async def git_pull(self, workspace_path: str, branch: Optional[str] = None) -> Tuple[bool, str]:
|
||||
"""Pulls latest changes from remote Gitea repository on current or specified branch."""
|
||||
ws = Path(workspace_path).expanduser().resolve()
|
||||
if not (ws / ".git").exists():
|
||||
return False, "مخزن گیت برای این مسیر مقداردهی نشده است."
|
||||
|
||||
try:
|
||||
# Fetch and check
|
||||
out = await self._run_cmd(["git", "pull", "origin", "main"], cwd=str(ws))
|
||||
return True, f"✅ وضعیت دریافت تغییرات:\n<code>{out.strip()}</code>"
|
||||
curr_branch = branch or (await self._run_cmd(["git", "branch", "--show-current"], cwd=str(ws))).strip() or "dev"
|
||||
out = await self._run_cmd(["git", "pull", "origin", curr_branch], cwd=str(ws))
|
||||
return True, f"✅ وضعیت دریافت تغییرات شاخه <code>{curr_branch}</code>:\n<code>{out.strip()}</code>"
|
||||
except Exception as e:
|
||||
logger.warning(f"Git pull warning/error in {ws}: {e}")
|
||||
return False, f"⚠️ خطا در دریافت تغییرات (Pull): {e}"
|
||||
|
||||
async def git_publish(
|
||||
self,
|
||||
workspace_path: str,
|
||||
message: str = "Publish dev to production",
|
||||
repo_name: Optional[str] = None,
|
||||
owner: str = "root",
|
||||
) -> Tuple[bool, str, Dict[str, Any]]:
|
||||
"""
|
||||
Publishes latest changes from `dev` to `production`:
|
||||
1. Ensures changes on `dev` are committed and pushed.
|
||||
2. Checks out `production` branch.
|
||||
3. Merges `dev` into `production`.
|
||||
4. Pushes `production` to Gitea origin.
|
||||
5. Switches back to `dev`.
|
||||
"""
|
||||
ws = Path(workspace_path).expanduser().resolve()
|
||||
if not (ws / ".git").exists():
|
||||
name = repo_name or ws.name
|
||||
init_res = await self.init_project_repo(str(ws), name, owner=owner)
|
||||
if not init_res.get("success"):
|
||||
return False, f"خطا در مقداردهی اولیه مخزن: {init_res.get('error')}", {}
|
||||
|
||||
try:
|
||||
# 1. Commit any pending changes on dev
|
||||
status = await self.git_status(str(ws))
|
||||
if status.get("dirty"):
|
||||
await self.git_commit_and_push(str(ws), message=f"Pre-publish commit: {message}", repo_name=repo_name, owner=owner)
|
||||
|
||||
# Ensure we are on dev branch
|
||||
curr_branch = (await self._run_cmd(["git", "branch", "--show-current"], cwd=str(ws))).strip()
|
||||
if curr_branch != "dev":
|
||||
# Ensure dev exists
|
||||
branches = await self._run_cmd(["git", "branch"], cwd=str(ws))
|
||||
if "dev" in branches:
|
||||
await self._run_cmd(["git", "checkout", "dev"], cwd=str(ws))
|
||||
else:
|
||||
await self._run_cmd(["git", "checkout", "-b", "dev"], cwd=str(ws))
|
||||
|
||||
# Push dev
|
||||
try:
|
||||
await self._run_cmd(["git", "push", "origin", "dev"], cwd=str(ws))
|
||||
except Exception as pe:
|
||||
logger.warning(f"Push dev warning before publish in {ws}: {pe}")
|
||||
|
||||
# Ensure production branch exists
|
||||
branches = await self._run_cmd(["git", "branch"], cwd=str(ws))
|
||||
if "production" not in branches:
|
||||
await self._run_cmd(["git", "branch", "production"], cwd=str(ws))
|
||||
|
||||
# 2. Checkout production
|
||||
await self._run_cmd(["git", "checkout", "production"], cwd=str(ws))
|
||||
|
||||
# Pull production if remote exists
|
||||
try:
|
||||
await self._run_cmd(["git", "pull", "origin", "production"], cwd=str(ws))
|
||||
except Exception:
|
||||
pass
|
||||
|
||||
# 3. Merge dev into production
|
||||
merge_msg = f"Merge branch 'dev' into production: {message}".replace('"', '\\"')
|
||||
merge_out = await self._run_cmd(["git", "merge", "dev", "--no-edit", "-m", merge_msg], cwd=str(ws))
|
||||
|
||||
# 4. Push production to Gitea
|
||||
push_out = await self._run_cmd(["git", "push", "origin", "production"], cwd=str(ws))
|
||||
|
||||
# Get production latest commit
|
||||
prod_commit = (await self._run_cmd(["git", "log", "-1", "--format=%h|%an|%ar|%s"], cwd=str(ws))).strip()
|
||||
|
||||
# 5. Switch back to dev branch
|
||||
await self._run_cmd(["git", "checkout", "dev"], cwd=str(ws))
|
||||
|
||||
commit_parts = prod_commit.split("|", 3) if "|" in prod_commit else [prod_commit, "", "", ""]
|
||||
commit_hash = commit_parts[0]
|
||||
commit_text = commit_parts[3] if len(commit_parts) > 3 else prod_commit
|
||||
|
||||
res_msg = (
|
||||
f"🚀 <b>پروژه با موفقیت روی شاخه <code>production</code> پابلیش شد!</b>\n\n"
|
||||
f"• 🌿 <b>شاخه مبدأ:</b> <code>dev</code>\n"
|
||||
f"• 🚀 <b>شاخه مقصد:</b> <code>production</code>\n"
|
||||
f"• 🔖 <b>آخرین کامیت:</b> <code>{commit_hash}</code> - {escape_html(commit_text)}\n"
|
||||
f"• 🔄 <b>وضعیت کاری:</b> مجدداً به شاخه <code>dev</code> بازگشت داده شد تا توسعه ادامه یابد."
|
||||
)
|
||||
|
||||
return True, res_msg, {
|
||||
"commit_hash": commit_hash,
|
||||
"commit_text": commit_text,
|
||||
"source_branch": "dev",
|
||||
"target_branch": "production",
|
||||
}
|
||||
|
||||
except Exception as e:
|
||||
logger.error(f"Error publishing dev to production in {ws}: {e}")
|
||||
# Ensure we return to dev
|
||||
try:
|
||||
await self._run_cmd(["git", "checkout", "dev"], cwd=str(ws))
|
||||
except Exception:
|
||||
pass
|
||||
return False, f"❌ خطا در عملیات پابلیش به شاخه پروداکشن: {e}", {}
|
||||
|
||||
async def git_sync(self, workspace_path: str, message: str = "Sync with Gitea", repo_name: Optional[str] = None) -> Tuple[bool, str]:
|
||||
"""Performs full sync: Pulls remote changes, then commits and pushes any local changes."""
|
||||
"""Performs full sync: Pulls remote changes, then commits and pushes any local changes on active branch."""
|
||||
ws = Path(workspace_path).expanduser().resolve()
|
||||
if not (ws / ".git").exists():
|
||||
name = repo_name or ws.name
|
||||
|
||||
Reference in New Issue
Block a user